axiom-alarmkit-ref

安装量: 50
排名: #14737

安装

npx skills add https://github.com/charleswiltgen/axiom --skill axiom-alarmkit-ref
AlarmKit Reference
Complete API reference for AlarmKit, Apple's framework for scheduling alarms and countdown timers with system-level alerting, Dynamic Island integration, and focus/silent mode override.
Overview
AlarmKit lets apps create alarms and timers that behave like the built-in Clock app -- they override Do Not Disturb, appear in the Dynamic Island, and show on the Lock Screen. The framework handles scheduling, snooze, pause/resume, and UI presentation through a small set of types centered on
AlarmManager
.
System Requirements
iOS 26+
(AlarmKit introduced in iOS 26)
Widget Extension
required for Live Activity / Dynamic Island presentation
Physical device
recommended for alarm sound and notification testing
Part 1: Key Components
AlarmManager
Singleton entry point for all alarm operations.
import
AlarmKit
let
manager
=
AlarmManager
.
shared
All scheduling, cancellation, and observation flows through this shared instance.
Alarm
Describes an alarm that can alert once or on a repeating schedule.
struct
Alarm
{
var
id
:
UUID
var
schedule
:
Schedule
?
var
countdownDuration
:
CountdownDuration
?
var
state
:
AlarmState
}
AlarmPresentation
Content for the alarm UI across three states -- alerting, counting down, and paused.
struct
AlarmPresentation
{
var
alert
:
Alert
// Required: shown when alarm fires
var
countdown
:
Countdown
?
// Optional: shown during countdown
var
paused
:
Paused
?
// Optional: shown when paused
}
AlarmAttributes
Generic container pairing presentation with app-specific metadata and tint color. Used to configure the Live Activity widget.
struct
AlarmAttributes
<
Metadata
:
AlarmMetadata
>
{
var
presentation
:
AlarmPresentation
var
metadata
:
Metadata
var
tintColor
:
Color
}
AlarmMetadata
Protocol for app-specific data attached to an alarm. Conform an empty struct for minimal usage, or add properties for richer UI.
struct
RecipeMetadata
:
AlarmMetadata
{
let
recipeName
:
String
let
cookingStep
:
String
}
Part 2: Authorization
Apps must request permission before scheduling alarms. Add
NSAlarmKitUsageDescription
to Info.plist.
Requesting Authorization
func
requestAlarmAuthorization
(
)
async
->
Bool
{
do
{
let
state
=
try
await
AlarmManager
.
shared
.
requestAuthorization
(
)
return
state
==
.
authorized
}
catch
{
print
(
"Authorization error:
(
error
)
"
)
return
false
}
}
Checking Current State
Use
authorizationState
(not
authorizationStatus
) to read the current value:
let
state
=
await
AlarmManager
.
shared
.
authorizationState
// .authorized | .denied | .notDetermined
Observing Authorization Changes
for
await
authState
in
AlarmManager
.
shared
.
authorizationUpdates
{
switch
authState
{
case
.
authorized
:
enableAlarmUI
(
)
case
.
denied
:
showPermissionPrompt
(
)
case
.
notDetermined
:
break
@unknown
default
:
break
}
}
Part 3: Scheduling Alarms
Every alarm requires a
UUID
, an
AlarmManager.AlarmConfiguration
, and a call to
schedule(id:configuration:)
.
One-Time Alarm
let
id
=
UUID
(
)
let
time
=
Alarm
.
Schedule
.
Relative
.
Time
(
hour
:
7
,
minute
:
30
)
let
schedule
=
Alarm
.
Schedule
.
relative
(
.
init
(
time
:
time
,
repeats
:
.
never
)
)
let
alert
=
AlarmPresentation
.
Alert
(
title
:
"Wake Up"
,
stopButton
:
.
stopButton
,
secondaryButton
:
.
snoozeButton
,
secondaryButtonBehavior
:
.
countdown
)
struct
EmptyMetadata
:
AlarmMetadata
{
}
let
config
=
AlarmManager
.
AlarmConfiguration
(
countdownDuration
:
nil
,
schedule
:
schedule
,
attributes
:
AlarmAttributes
(
presentation
:
AlarmPresentation
(
alert
:
alert
)
,
metadata
:
EmptyMetadata
(
)
,
tintColor
:
.
blue
)
,
sound
:
.
default
)
let
alarm
=
try
await
AlarmManager
.
shared
.
schedule
(
id
:
id
,
configuration
:
config
)
Repeating Alarm
Use
.weekly(Array(weekdays))
for specific days:
let
time
=
Alarm
.
Schedule
.
Relative
.
Time
(
hour
:
6
,
minute
:
0
)
let
schedule
=
Alarm
.
Schedule
.
relative
(
.
init
(
time
:
time
,
repeats
:
.
weekly
(
[
.
monday
,
.
tuesday
,
.
wednesday
,
.
thursday
,
.
friday
]
)
)
)
Countdown Timer
Set
schedule: nil
and provide
countdownDuration
with a
preAlert
interval:
let
countdown
=
Alarm
.
CountdownDuration
(
preAlert
:
300
,
// 5 minutes
postAlert
:
10
// Optional post-alert snooze window
)
let
config
=
AlarmManager
.
AlarmConfiguration
(
countdownDuration
:
countdown
,
schedule
:
nil
,
attributes
:
attributes
,
sound
:
.
default
)
Timers support pause/resume and show a countdown presentation when
AlarmPresentation.countdown
is provided.
Snooze Configuration
Snooze uses
CountdownDuration.postAlert
combined with a
.snoozeButton
secondary action:
let
alert
=
AlarmPresentation
.
Alert
(
title
:
"Alarm"
,
stopButton
:
.
stopButton
,
secondaryButton
:
.
snoozeButton
,
secondaryButtonBehavior
:
.
countdown
// Starts post-alert countdown
)
let
countdownDuration
=
Alarm
.
CountdownDuration
(
preAlert
:
nil
,
postAlert
:
9
*
60
// 9-minute snooze
)
Part 4: Customizing Alarm UI
Alert Presentation
The alert state is shown when the alarm fires. The stop button is required; secondary button is optional.
// Minimal
let
basic
=
AlarmPresentation
.
Alert
(
title
:
"Alarm"
,
stopButton
:
.
stopButton
)
// With custom button labels
let
custom
=
AlarmPresentation
.
Alert
(
title
:
"Medication Reminder"
,
stopButton
:
AlarmButton
(
label
:
"Taken"
)
,
secondaryButton
:
AlarmButton
(
label
:
"Remind Later"
)
,
secondaryButtonBehavior
:
.
countdown
)
// With open-app action
let
openApp
=
AlarmPresentation
.
Alert
(
title
:
"Workout Time"
,
stopButton
:
.
stopButton
,
secondaryButton
:
.
openAppButton
,
secondaryButtonBehavior
:
.
custom
)
Countdown Presentation
Shown while a timer counts down. Only relevant for alarms with
countdownDuration.preAlert
.
let
countdown
=
AlarmPresentation
.
Countdown
(
title
:
"Timer Running"
,
pauseButton
:
.
pauseButton
)
Paused Presentation
Shown when a countdown timer is paused.
let
paused
=
AlarmPresentation
.
Paused
(
title
:
"Timer Paused"
,
resumeButton
:
.
resumeButton
)
Full Three-State Presentation
Combine all three for a complete timer experience:
let
presentation
=
AlarmPresentation
(
alert
:
AlarmPresentation
.
Alert
(
title
:
"Timer Complete"
,
stopButton
:
.
stopButton
,
secondaryButton
:
.
repeatButton
,
secondaryButtonBehavior
:
.
countdown
)
,
countdown
:
AlarmPresentation
.
Countdown
(
title
:
"Cooking Timer"
,
pauseButton
:
.
pauseButton
)
,
paused
:
AlarmPresentation
.
Paused
(
title
:
"Timer Paused"
,
resumeButton
:
.
resumeButton
)
)
Part 5: Managing Alarms
Retrieve All Alarms
let
alarms
=
try
AlarmManager
.
shared
.
alarms
Pause / Resume
try
await
AlarmManager
.
shared
.
pause
(
id
:
alarmID
)
try
await
AlarmManager
.
shared
.
resume
(
id
:
alarmID
)
Cancel
try
await
AlarmManager
.
shared
.
cancel
(
id
:
alarmID
)
Observe Alarm Updates
Use
alarmUpdates
to keep UI in sync. An alarm absent from the emitted array is no longer scheduled.
for
await
alarms
in
AlarmManager
.
shared
.
alarmUpdates
{
self
.
alarms
=
alarms
}
Part 6: Live Activity Integration
AlarmKit alarms appear in the Dynamic Island and Lock Screen through
ActivityConfiguration
. Add a Widget Extension target and implement the widget using
AlarmAttributes
.
struct
AlarmWidgetView
:
Widget
{
var
body
:
some
WidgetConfiguration
{
ActivityConfiguration
(
for
:
AlarmAttributes
<
YourMetadata
>
.
self
)
{
context
in
// Lock Screen presentation
VStack
{
Text
(
context
.
attributes
.
presentation
.
alert
.
title
)
if
context
.
state
.
mode
==
.
countdown
{
Text
(
timerInterval
:
context
.
state
.
countdownEndDate
.
timeIntervalSinceNow
,
countsDown
:
true
)
.
bold
(
)
}
}
.
padding
(
)
}
dynamicIsland
:
{
context
in
DynamicIsland
{
DynamicIslandExpandedRegion
(
.
leading
)
{
Text
(
context
.
attributes
.
presentation
.
alert
.
title
)
}
DynamicIslandExpandedRegion
(
.
trailing
)
{
if
context
.
state
.
mode
==
.
countdown
{
Text
(
timerInterval
:
context
.
state
.
countdownEndDate
.
timeIntervalSinceNow
,
countsDown
:
true
)
}
}
}
compactLeading
:
{
Image
(
systemName
:
"alarm"
)
}
compactTrailing
:
{
if
context
.
state
.
mode
==
.
countdown
{
Text
(
timerInterval
:
context
.
state
.
countdownEndDate
.
timeIntervalSinceNow
,
countsDown
:
true
)
}
}
minimal
:
{
Image
(
systemName
:
"alarm"
)
}
}
}
}
Part 7: SwiftUI Integration
ViewModel Pattern with @Observable
import
AlarmKit
@Observable
class
AlarmViewModel
{
var
alarms
:
[
Alarm
]
=
[
]
private
let
manager
=
AlarmManager
.
shared
func
requestAuthorization
(
)
{
Task
{
_
=
try
?
await
manager
.
requestAuthorization
(
)
}
}
func
loadAndObserve
(
)
{
Task
{
alarms
=
(
try
?
manager
.
alarms
)
??
[
]
for
await
updated
in
manager
.
alarmUpdates
{
alarms
=
updated
}
}
}
func
addAlarm
(
hour
:
Int
,
minute
:
Int
,
weekdays
:
Set
<
Locale
.
Weekday
>
)
{
Task
{
let
time
=
Alarm
.
Schedule
.
Relative
.
Time
(
hour
:
hour
,
minute
:
minute
)
let
schedule
=
Alarm
.
Schedule
.
relative
(
.
init
(
time
:
time
,
repeats
:
weekdays
.
isEmpty
?
.
never
:
.
weekly
(
Array
(
weekdays
)
)
)
)
let
alert
=
AlarmPresentation
.
Alert
(
title
:
"Alarm"
,
stopButton
:
.
stopButton
,
secondaryButton
:
.
snoozeButton
,
secondaryButtonBehavior
:
.
countdown
)
struct
EmptyMetadata
:
AlarmMetadata
{
}
let
config
=
AlarmManager
.
AlarmConfiguration
(
countdownDuration
:
Alarm
.
CountdownDuration
(
preAlert
:
nil
,
postAlert
:
9
*
60
)
,
schedule
:
schedule
,
attributes
:
AlarmAttributes
(
presentation
:
AlarmPresentation
(
alert
:
alert
)
,
metadata
:
EmptyMetadata
(
)
,
tintColor
:
.
blue
)
,
sound
:
.
default
)
_
=
try
?
await
manager
.
schedule
(
id
:
UUID
(
)
,
configuration
:
config
)
}
}
func
cancel
(
id
:
UUID
)
{
Task
{
try
?
await
manager
.
cancel
(
id
:
id
)
}
}
func
togglePause
(
id
:
UUID
,
isPaused
:
Bool
)
{
Task
{
if
isPaused
{
try
?
await
manager
.
resume
(
id
:
id
)
}
else
{
try
?
await
manager
.
pause
(
id
:
id
)
}
}
}
}
Alarm List View
struct
AlarmListView
:
View
{
@State
private
var
viewModel
=
AlarmViewModel
(
)
var
body
:
some
View
{
NavigationStack
{
List
(
viewModel
.
alarms
,
id
:
\
.
id
)
{
alarm
in
AlarmRow
(
alarm
:
alarm
,
viewModel
:
viewModel
)
}
.
navigationTitle
(
"Alarms"
)
.
onAppear
{
viewModel
.
requestAuthorization
(
)
viewModel
.
loadAndObserve
(
)
}
}
}
}
Part 8: Best Practices
Practice
Detail
Request authorization early
On first launch or first alarm creation attempt
Handle denial gracefully
Guide users to Settings if permission was denied
Persist alarm UUIDs
Store IDs to manage alarms across app launches
Implement widget extension
Required for countdown/Dynamic Island presentation
Use
alarmUpdates
Keep UI in sync; don't poll or cache stale state
Test on physical device
Alarm sounds, notifications, and Live Activities require real hardware
Respect system limits
There is a system-imposed cap on alarms per app
Use
authorizationState
Not
authorizationStatus
-- the correct property name is
authorizationState
Resources
WWDC
2025-230
Docs
/alarmkit, /alarmkit/alarmmanager, /alarmkit/alarm, /alarmkit/alarmpresentation, /alarmkit/alarmattributes
Skills
axiom-extensions-widgets-ref, axiom-swiftui-26-ref
返回排行榜