$npx -y skills add dpearson2699/swift-ios-skills --skill alarmkitImplement AlarmKit alarms and countdown timers for iOS and iPadOS with Lock Screen, Dynamic Island, StandBy, and paired Apple Watch system UI. Covers AlarmManager scheduling, AlarmAttributes and AlarmPresentation, system Stop and AlarmButton secondary actions, authorization, stat
| 1 | # AlarmKit |
| 2 | |
| 3 | Schedule prominent alarms and countdown timers that surface on the Lock Screen, |
| 4 | Dynamic Island, StandBy, and a paired Apple Watch when the alarm fires. AlarmKit |
| 5 | requires iOS 26+ / iPadOS 26+. Alarms can break through Focus and Silent mode. |
| 6 | |
| 7 | AlarmKit uses ActivityKit data models for its Live Activity, but the firing alert |
| 8 | is system-managed alarm UI, not a general custom notification UI surface. Custom |
| 9 | UI belongs only to countdown and paused Live Activity states rendered by a Widget |
| 10 | Extension with the same `AlarmAttributes<Metadata>` and |
| 11 | `AlarmPresentationState` used when scheduling. |
| 12 | |
| 13 | See [references/alarmkit-patterns.md](references/alarmkit-patterns.md) for complete code patterns including |
| 14 | authorization, scheduling, countdown timers, snooze handling, and widget setup. |
| 15 | |
| 16 | ```swift |
| 17 | import AlarmKit |
| 18 | ``` |
| 19 | |
| 20 | ## Contents |
| 21 | |
| 22 | - [Workflow](#workflow) |
| 23 | - [Authorization](#authorization) |
| 24 | - [Alarm vs Timer Decision](#alarm-vs-timer-decision) |
| 25 | - [Scheduling Alarms](#scheduling-alarms) |
| 26 | - [Countdown Timers](#countdown-timers) |
| 27 | - [Alarm States](#alarm-states) |
| 28 | - [AlarmAttributes and AlarmPresentation](#alarmattributes-and-alarmpresentation) |
| 29 | - [AlarmButton](#alarmbutton) |
| 30 | - [Live Activity Integration](#live-activity-integration) |
| 31 | - [Common Mistakes](#common-mistakes) |
| 32 | - [Review Checklist](#review-checklist) |
| 33 | - [References](#references) |
| 34 | |
| 35 | ## Workflow |
| 36 | |
| 37 | ### 1. Create a new alarm or timer |
| 38 | |
| 39 | 1. Add `NSAlarmKitUsageDescription` to Info.plist with a user-facing string. |
| 40 | 2. Request authorization with `AlarmManager.shared.requestAuthorization()` when the app can explain the value, or handle the first-schedule system prompt. |
| 41 | 3. If authorization is `.denied` or not `.authorized`, show recovery UI instead of scheduling. |
| 42 | 4. Configure `AlarmPresentation` (alert, countdown, paused states). |
| 43 | 5. Create `AlarmAttributes` with the presentation, optional metadata, and tint color. |
| 44 | 6. Build an `AlarmManager.AlarmConfiguration` (.alarm or .timer). |
| 45 | 7. Schedule with `AlarmManager.shared.schedule(id:configuration:)`. |
| 46 | 8. Observe `alarmManager.alarmUpdates` and confirm the scheduled ID reaches the expected state. |
| 47 | 9. If using countdown, add a Widget Extension target with an `ActivityConfiguration` for the same `AlarmAttributes<Metadata>` type. |
| 48 | |
| 49 | ### 2. Review existing alarm code |
| 50 | |
| 51 | Run through the Review Checklist at the end of this document. |
| 52 | |
| 53 | ## Authorization |
| 54 | |
| 55 | AlarmKit requires user authorization. Request early when the app can explain the |
| 56 | value, or let AlarmKit prompt automatically on first schedule. If authorization |
| 57 | is not granted after the explicit or automatic prompt, alarms are not scheduled |
| 58 | and will not alert. |
| 59 | |
| 60 | ```swift |
| 61 | let manager = AlarmManager.shared |
| 62 | |
| 63 | // Request authorization explicitly |
| 64 | let state = try await manager.requestAuthorization() |
| 65 | guard state == .authorized else { return } |
| 66 | |
| 67 | // Check current state synchronously |
| 68 | let current = manager.authorizationState // .authorized, .denied, .notDetermined |
| 69 | |
| 70 | // Observe authorization changes |
| 71 | for await state in manager.authorizationUpdates { |
| 72 | switch state { |
| 73 | case .authorized: print("Alarms enabled") |
| 74 | case .denied: print("Alarms disabled") |
| 75 | case .notDetermined: break |
| 76 | @unknown default: break |
| 77 | } |
| 78 | } |
| 79 | ``` |
| 80 | |
| 81 | ## Alarm vs Timer Decision |
| 82 | |
| 83 | | Feature | Alarm (`.alarm`) | Timer (`.timer`) | |
| 84 | |---|---|---| |
| 85 | | Fires at | Specific time (schedule) | After duration elapses | |
| 86 | | Countdown UI | Optional | Always shown | |
| 87 | | Recurring | Yes (weekly days) | No | |
| 88 | | Use case | Wake-up, scheduled reminders | Cooking, workout intervals | |
| 89 | |
| 90 | Use `.alarm(schedule:...)` when firing at a clock time. Use `.timer(duration:...)` |
| 91 | when firing after a duration from now. |
| 92 | |
| 93 | ## Scheduling Alarms |
| 94 | |
| 95 | ### Alarm.Schedule |
| 96 | |
| 97 | Use `.fixed(date)` for a one-time absolute date or `.relative` for a local clock |
| 98 | time with `.never` or `.weekly` repetition. Load |
| 99 | [Recurring Alarm Patterns](references/alarmkit-patterns.md#recurring-alarm-patterns) |
| 100 | for daily, weekday, weekend, and fixed-date variants. |
| 101 | |
| 102 | ### Schedule and Configure |
| 103 | |
| 104 | ```swift |
| 105 | let id = UUID() |
| 106 | |
| 107 | let alert = AlarmPresentation.Alert( |
| 108 | title: "Wake Up", |
| 109 | secondaryButton: AlarmButton( |
| 110 | text: "Snooze", textColor: .white, systemImageName: "bell.slash" |
| 111 | ), |
| 112 | secondaryButtonBehavior: .countdown |
| 113 | ) |
| 114 | let presentation = AlarmPresentation(alert: alert) |
| 115 | struct EmptyAlarmMetadata: AlarmMetadata {} |
| 116 | let attributes = AlarmAttributes<EmptyAlarmMetadata>( |
| 117 | presentation: presentation, |
| 118 | metadata: nil, |
| 119 | tintColor: .indigo |
| 120 | ) |
| 121 | |
| 122 | let snooze = Alarm.CountdownDuration(preAlert: nil, postAlert: 300) |