- Published on
> Scheduling Alarms with AlarmKit
- Authors

- Name
- Mick MacCallum
- @0x7fs
For years, developers have worked around iOS's lack of alarm APIs by using local notifications or background audio tricks. AlarmKit changes that by letting your app schedule real alarms that show up in the Clock app and use the system's alarm infrastructure.
The framework is straightforward: you request authorization, create an alarm with a time and optional recurrence, and schedule it through AlarmManager. The alarm then appears alongside the user's other alarms in Clock.
Requesting Authorization
Before scheduling alarms, you need the user's permission. AlarmKit uses a familiar authorization pattern:
import AlarmKit
func requestAlarmAccess() async -> Bool {
let manager = AlarmManager.shared
do {
let status = try await manager.requestAuthorization()
return status == .authorized
} catch {
print("Authorization failed: \(error)")
return false
}
}
The system shows a permission prompt explaining that your app wants to create alarms. Once granted, this permission persists until the user revokes it in Settings.
Scheduling an Alarm
With authorization in place, you can create and schedule alarms. Each alarm needs at minimum a time, specified as DateComponents:
import AlarmKit
func scheduleWakeUpAlarm() async throws {
let manager = AlarmManager.shared
var time = DateComponents()
time.hour = 7
time.minute = 30
let alarm = AlarmAttributes(
time: time,
title: "Wake Up"
)
try await manager.schedule(alarm)
}
The alarm now appears in the Clock app with your specified title. When it fires, the user gets the standard alarm experience with sound, snooze options, and the familiar dismiss interface.
Recurring Alarms
For alarms that repeat on specific days, add a recurrence rule:
import AlarmKit
func scheduleDailyAlarm() async throws {
let manager = AlarmManager.shared
var time = DateComponents()
time.hour = 6
time.minute = 0
// Repeat on weekdays
let recurrence = AlarmRecurrenceRule(
daysOfWeek: [.monday, .tuesday, .wednesday, .thursday, .friday]
)
let alarm = AlarmAttributes(
time: time,
title: "Weekday Alarm",
recurrenceRule: recurrence
)
try await manager.schedule(alarm)
}
You can also specify a single day for weekly recurrence, or omit the recurrence rule entirely for one-time alarms.
Managing Scheduled Alarms
Your app can fetch and manage the alarms it has scheduled:
import AlarmKit
func listMyAlarms() async throws -> [Alarm] {
let manager = AlarmManager.shared
return try await manager.scheduledAlarms()
}
func deleteAlarm(_ alarm: Alarm) async throws {
let manager = AlarmManager.shared
try await manager.cancel(alarm.id)
}
func deleteAllAlarms() async throws {
let manager = AlarmManager.shared
let alarms = try await manager.scheduledAlarms()
for alarm in alarms {
try await manager.cancel(alarm.id)
}
}
Note that you can only access alarms your app created. Alarms from other apps or manually created in Clock aren't visible to your app.
Handling Alarm Events
To respond when your alarm fires or gets dismissed, implement an AlarmHandler:
import AlarmKit
class MyAlarmHandler: AlarmHandler {
func alarmDidFire(_ alarm: Alarm) {
// Called when the alarm goes off
print("Alarm fired: \(alarm.attributes.title ?? "Untitled")")
}
func alarmWasDismissed(_ alarm: Alarm) {
// Called when user dismisses or snoozes
print("Alarm dismissed: \(alarm.id)")
}
}
Register your handler when the app launches:
import SwiftUI
import AlarmKit
@main
struct MyApp: App {
init() {
AlarmManager.shared.handler = MyAlarmHandler()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Practical Example
Here's a simple alarm scheduling view that ties everything together:
import SwiftUI
import AlarmKit
struct AlarmSchedulerView: View {
@State private var alarmTime = Date()
@State private var alarmTitle = ""
@State private var isAuthorized = false
@State private var scheduledAlarms: [Alarm] = []
var body: some View {
Form {
Section("New Alarm") {
DatePicker("Time", selection: $alarmTime, displayedComponents: .hourAndMinute)
TextField("Title", text: $alarmTitle)
Button("Schedule Alarm") {
Task { await scheduleAlarm() }
}
.disabled(!isAuthorized || alarmTitle.isEmpty)
}
Section("Scheduled Alarms") {
ForEach(scheduledAlarms, id: \.id) { alarm in
HStack {
Text(alarm.attributes.title ?? "Untitled")
Spacer()
Text(formatTime(alarm.attributes.time))
.foregroundStyle(.secondary)
}
}
.onDelete(perform: deleteAlarms)
}
}
.task {
isAuthorized = await requestAuthorization()
await refreshAlarms()
}
}
func requestAuthorization() async -> Bool {
do {
let status = try await AlarmManager.shared.requestAuthorization()
return status == .authorized
} catch {
return false
}
}
func scheduleAlarm() async {
let calendar = Calendar.current
let components = calendar.dateComponents([.hour, .minute], from: alarmTime)
let attributes = AlarmAttributes(
time: components,
title: alarmTitle
)
do {
try await AlarmManager.shared.schedule(attributes)
alarmTitle = ""
await refreshAlarms()
} catch {
print("Failed to schedule: \(error)")
}
}
func refreshAlarms() async {
do {
scheduledAlarms = try await AlarmManager.shared.scheduledAlarms()
} catch {
scheduledAlarms = []
}
}
func deleteAlarms(at offsets: IndexSet) {
Task {
for index in offsets {
let alarm = scheduledAlarms[index]
try? await AlarmManager.shared.cancel(alarm.id)
}
await refreshAlarms()
}
}
func formatTime(_ components: DateComponents) -> String {
let formatter = DateFormatter()
formatter.timeStyle = .short
var date = Calendar.current.date(from: components) ?? Date()
return formatter.string(from: date)
}
}
Entitlement Requirements
AlarmKit requires a special entitlement that you need to request from Apple. Add the AlarmKit capability in your app's Signing & Capabilities, then apply for access through the Apple Developer portal. Apple reviews these requests to ensure apps have a legitimate use case for alarm functionality.
Without the entitlement, AlarmKit APIs will throw authorization errors even if the user grants permission.
Things to Know
Alarms scheduled through AlarmKit respect the user's Do Not Disturb and Focus settings, just like regular Clock alarms. The system handles all the sound playback and UI, so you don't need to manage audio sessions or implement a custom alarm interface.
Users can edit or delete your app's alarms directly in the Clock app. If they do, your next call to scheduledAlarms() will reflect those changes. There's no callback for external modifications, so check the current state when your app becomes active if you need to stay in sync.
For details on the full API surface, see Apple's AlarmKit documentation.
// Continue_Learning
Reading Your App's Age Rating with StoreKit's ageRatingCode
Learn how to use AppStore.ageRatingCode to read your app's current age rating and react to rating changes for parental consent compliance.
Finding the Top View Controller in Swift
How to traverse the view controller hierarchy to find the currently visible view controller.
Getting Current Disk Usage in Swift
How to check available and total disk space on iOS devices using URL resource values.
// Stay Updated
Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.