AlarmKit
Schedule prominent alarms and countdown timers that surface on the Lock Screen, Dynamic Island, StandBy, and paired Apple Watch when firing. Targets iOS 26+ / iPadOS 26+.
UI Boundary: AlarmKit alerts use system-managed alert UI that breaks through Focus and Silent modes. Custom UI is limited to countdown and paused Live Activity states rendered by a Widget Extension.
Contents
- Authorization & Setup
- Scheduling Alarms
- Countdown Timers
- Alarm State Observation
- Widget Extension Live Activity
- Common Mistakes
- Review Checklist
- References
Authorization & Setup
Add NSAlarmKitUsageDescription to Info.plist. Request authorization before scheduling alarms:
import AlarmKit
func setupAlarmKit() async throws {
let status = await AlarmManager.shared.requestAuthorization()
guard status == .authorized else { throw AlarmError.unauthorized }
}
Scheduling Alarms
Construct and schedule an alarm with firing date and stop actions:
let alarm = Alarm(
id: UUID(),
schedule: .time(Date().addingTimeInterval(3600)),
title: "Morning Medication",
stopAction: .dismiss,
secondaryAction: .snooze(duration: 300)
)
try await AlarmManager.shared.schedule(alarm)
Countdown Timers
Create active countdown experiences that present live timers across Dynamic Island and Lock Screen:
let timer = Alarm(
id: UUID(),
schedule: .countdown(duration: 600),
title: "Tea Steep",
stopAction: .dismiss
)
try await AlarmManager.shared.schedule(timer)
Alarm State Observation
Observe active alarms and firing transitions asynchronously:
Task {
for await activeAlarms in AlarmManager.shared.alarms {
for alarm in activeAlarms {
print("Alarm \(alarm.title): \(alarm.state)")
}
}
}
Widget Extension Live Activity
Render custom UI for countdown and paused states in a Widget Extension conforming to ActivityConfiguration(for: AlarmAttributes<MyMetadata>.self):
import WidgetKit
import SwiftUI
import AlarmKit
struct AlarmLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: AlarmAttributes<MyMetadata>.self) { context in
// Lock Screen presentation
Text(context.state.presentationState.title)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
Image(systemName: "alarm")
}
DynamicIslandExpandedRegion(.trailing) {
Text(context.state.schedule, style: .timer)
}
} compactLeading: {
Image(systemName: "alarm")
} compactTrailing: {
Text(context.state.schedule, style: .timer)
} minimal: {
Image(systemName: "alarm")
}
}
}
}
Common Mistakes
- Attempting custom firing alert UI: Firing alerts are strictly system-managed. Custom views only apply to countdown Live Activities.
- Missing NSAlarmKitUsageDescription: Throws unhandled exceptions when calling
requestAuthorization(). - Scheduling without authorization check: Alarms scheduled while unauthorized fail silently or throw errors.
- Ignoring state observation stream: Fails to update in-app UI when the user dismisses an alarm via the Lock Screen or Apple Watch.
- Confusing AlarmKit with UserNotifications: Use AlarmKit for critical alarms that must break through Silent/Focus; use UserNotifications for standard app alerts.
Review Checklist
-
NSAlarmKitUsageDescriptiondeclared in target Info.plist -
AlarmManager.shared.requestAuthorization()confirmed authorized - Firing schedule and stop/snooze actions configured
- Widget Extension matches
AlarmAttributesgeneric metadata - State changes observed via
AlarmManager.shared.alarms
References
- Patterns and code: references/alarmkit-patterns.md
- AlarmKit
- AlarmManager
- AlarmAttributes
- Scheduling an alarm