Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.
90
90%
Does it follow best practices?
Impact
—
Average score across 248 eval scenarios
Advisory
Suggest reviewing before use
Complete implementation patterns for AlarmKit alarms, countdown timers, authorization, state observation, and Live Activity integration. All patterns target iOS 26+ / iPadOS 26+ with Swift 6.3. The alerting UI is system-managed; countdown and paused states use a widget extension for custom Live Activity UI.
End-to-end pattern for scheduling a wake-up alarm with snooze support.
import AlarmKit
import AppIntents
struct StopAlarmIntent: LiveActivityIntent {
static var title: LocalizedStringResource = "Stop Alarm"
@Parameter(title: "Alarm ID") var alarmID: String
init() {}
init(alarmID: String) { self.alarmID = alarmID }
func perform() async throws -> some IntentResult {
try AlarmManager.shared.stop(id: UUID(uuidString: alarmID)!)
return .result()
}
}
struct SnoozeAlarmIntent: LiveActivityIntent {
static var title: LocalizedStringResource = "Snooze Alarm"
@Parameter(title: "Alarm ID") var alarmID: String
init() {}
init(alarmID: String) { self.alarmID = alarmID }
func perform() async throws -> some IntentResult {
try AlarmManager.shared.countdown(id: UUID(uuidString: alarmID)!)
return .result()
}
}
struct WakeUpMetadata: AlarmMetadata {
var label: String
}
@MainActor
func scheduleWakeUpAlarm(
hour: Int, minute: Int, label: String
) async throws -> Alarm {
let manager = AlarmManager.shared
let authState = try await manager.requestAuthorization()
guard authState == .authorized else { throw AlarmSchedulingError.notAuthorized }
let alert = AlarmPresentation.Alert(
title: LocalizedStringResource(stringLiteral: label),
secondaryButton: AlarmButton(
text: "Snooze", textColor: .white, systemImageName: "bell.slash"
),
secondaryButtonBehavior: .countdown
)
let presentation = AlarmPresentation(alert: alert)
let attributes = AlarmAttributes(
presentation: presentation,
metadata: WakeUpMetadata(label: label),
tintColor: .indigo
)
let id = UUID()
let snooze = Alarm.CountdownDuration(preAlert: nil, postAlert: 300)
let config = AlarmManager.AlarmConfiguration(
countdownDuration: snooze,
schedule: .relative(.init(
time: .init(hour: hour, minute: minute), repeats: .never
)),
attributes: attributes,
stopIntent: StopAlarmIntent(alarmID: id.uuidString),
secondaryIntent: SnoozeAlarmIntent(alarmID: id.uuidString),
sound: .default
)
return try await manager.schedule(id: id, configuration: config)
}
enum AlarmSchedulingError: Error {
case notAuthorized
}End-to-end pattern for a countdown timer with pause/resume support.
import AlarmKit
import AppIntents
struct StopTimerIntent: LiveActivityIntent {
static var title: LocalizedStringResource = "Stop Timer"
@Parameter(title: "Timer ID") var timerID: String
init() {}
init(timerID: String) { self.timerID = timerID }
func perform() async throws -> some IntentResult {
try AlarmManager.shared.stop(id: UUID(uuidString: timerID)!)
return .result()
}
}
struct CookingTimerMetadata: AlarmMetadata {
var recipeName: String
var stepDescription: String
}
@MainActor
func startCookingTimer(
durationSeconds: TimeInterval, recipeName: String, step: String
) async throws -> Alarm {
let manager = AlarmManager.shared
let authState = try await manager.requestAuthorization()
guard authState == .authorized else { throw AlarmSchedulingError.notAuthorized }
let alert = AlarmPresentation.Alert(
title: LocalizedStringResource(stringLiteral: "\(recipeName): \(step)"),
secondaryButton: nil, secondaryButtonBehavior: nil
)
let countdown = AlarmPresentation.Countdown(
title: LocalizedStringResource(stringLiteral: recipeName),
pauseButton: AlarmButton(
text: "Pause", textColor: .orange, systemImageName: "pause.fill"
)
)
let paused = AlarmPresentation.Paused(
title: "Paused",
resumeButton: AlarmButton(
text: "Resume", textColor: .green, systemImageName: "play.fill"
)
)
let presentation = AlarmPresentation(
alert: alert, countdown: countdown, paused: paused
)
let attributes = AlarmAttributes(
presentation: presentation,
metadata: CookingTimerMetadata(recipeName: recipeName, stepDescription: step),
tintColor: .orange
)
let id = UUID()
let config = AlarmManager.AlarmConfiguration.timer(
duration: durationSeconds,
attributes: attributes,
stopIntent: StopTimerIntent(timerID: id.uuidString),
sound: .default
)
return try await manager.schedule(id: id, configuration: config)
}Observable pattern for centralized authorization management.
import AlarmKit
import Observation
@Observable
@MainActor
final class AlarmAuthorizationManager {
private let manager = AlarmManager.shared
private(set) var isAuthorized = false
private(set) var authState: AlarmManager.AuthorizationState = .notDetermined
init() {
authState = manager.authorizationState
isAuthorized = authState == .authorized
}
func requestIfNeeded() async throws -> Bool {
guard authState == .notDetermined else { return isAuthorized }
let state = try await manager.requestAuthorization()
authState = state
isAuthorized = state == .authorized
return isAuthorized
}
func observeAuthorizationChanges() async {
for await state in manager.authorizationUpdates {
authState = state
isAuthorized = state == .authorized
}
}
}
// Usage in SwiftUI
struct AlarmSettingsView: View {
@State private var authManager = AlarmAuthorizationManager()
var body: some View {
Group {
if authManager.isAuthorized {
Text("Alarms are enabled")
} else if authManager.authState == .denied {
ContentUnavailableView(
"Alarms Disabled", systemImage: "alarm.waves.left.and.right",
description: Text("Enable in Settings > Your App > Alarms & Timers.")
)
} else {
Button("Enable Alarms") {
Task { try? await authManager.requestIfNeeded() }
}
}
}
.task { await authManager.observeAuthorizationChanges() }
}
}Pattern for tracking all alarms and reacting to state changes.
import AlarmKit
import Observation
@Observable
@MainActor
final class AlarmStore {
private let manager = AlarmManager.shared
private(set) var alarms: [Alarm] = []
func refreshAlarms() throws {
alarms = try manager.alarms
}
func startObserving() async {
for await updatedAlarms in manager.alarmUpdates {
alarms = updatedAlarms
}
}
func alarm(for id: UUID) -> Alarm? { alarms.first { $0.id == id } }
func alarms(in state: Alarm.State) -> [Alarm] { alarms.filter { $0.state == state } }
func cancel(_ id: Alarm.ID) throws { try manager.cancel(id: id) }
func pause(_ id: Alarm.ID) throws { try manager.pause(id: id) }
func resume(_ id: Alarm.ID) throws { try manager.resume(id: id) }
func stop(_ id: Alarm.ID) throws { try manager.stop(id: id) }
func snooze(_ id: Alarm.ID) throws { try manager.countdown(id: id) }
}
// Usage in SwiftUI
struct AlarmListView: View {
@State private var store = AlarmStore()
var body: some View {
List(store.alarms, id: \.id) { alarm in
HStack {
Text(alarm.id.uuidString.prefix(8)).font(.headline)
Spacer()
switch alarm.state {
case .scheduled:
Button("Cancel", role: .destructive) { try? store.cancel(alarm.id) }
case .countdown:
Button("Pause") { try? store.pause(alarm.id) }
case .paused:
Button("Resume") { try? store.resume(alarm.id) }
case .alerting:
Button("Stop") { try? store.stop(alarm.id) }
@unknown default:
EmptyView()
}
}
}
.task {
try? store.refreshAlarms()
await store.startObserving()
}
}
}Widget extension that renders countdown and paused states. AlarmKit expects this when your alarm uses countdown presentation; the system fallback countdown UI is limited to cases such as after restart before first unlock.
import WidgetKit
import SwiftUI
import AlarmKit
// MARK: - Widget bundle
struct AlarmWidgetBundle: WidgetBundle {
var body: some Widget {
AlarmLiveActivityWidget()
}
}
// MARK: - Live Activity configuration
struct AlarmLiveActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: AlarmAttributes<CookingTimerMetadata>.self) { context in
AlarmLockScreenView(context: context)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
Image(systemName: "alarm.fill")
.font(.title2)
.foregroundStyle(context.attributes.tintColor)
}
DynamicIslandExpandedRegion(.trailing) {
AlarmCountdownText(state: context.state)
.font(.title3.monospacedDigit())
}
DynamicIslandExpandedRegion(.center) {
Text(context.attributes.metadata?.recipeName ?? "Timer")
.font(.headline)
}
DynamicIslandExpandedRegion(.bottom) {
if let step = context.attributes.metadata?.stepDescription {
Text(step).font(.subheadline).foregroundStyle(.secondary)
}
}
} compactLeading: {
Image(systemName: "alarm.fill").foregroundStyle(context.attributes.tintColor)
} compactTrailing: {
AlarmCountdownText(state: context.state)
.frame(width: 44).monospacedDigit()
} minimal: {
Image(systemName: "alarm.fill").foregroundStyle(context.attributes.tintColor)
}
.keylineTint(context.attributes.tintColor)
}
}
}
// MARK: - Lock Screen view
struct AlarmLockScreenView: View {
let context: ActivityViewContext<AlarmAttributes<CookingTimerMetadata>>
var body: some View {
VStack(alignment: .leading) {
HStack {
Image(systemName: "alarm.fill")
.foregroundStyle(context.attributes.tintColor)
Text(context.attributes.metadata?.recipeName ?? "Timer")
.font(.headline)
Spacer()
AlarmCountdownText(state: context.state)
.font(.title3.monospacedDigit().bold())
}
switch context.state.mode {
case .countdown(let info):
ProgressView(
value: info.previouslyElapsedDuration,
total: info.totalCountdownDuration
)
.tint(context.attributes.tintColor)
case .paused:
Label("Paused", systemImage: "pause.fill")
.font(.subheadline).foregroundStyle(.secondary)
case .alert:
EmptyView() // System handles alerting UI
@unknown default:
EmptyView()
}
}
.padding()
}
}
// MARK: - Helper views
struct AlarmCountdownText: View {
let state: AlarmPresentationState
var body: some View {
switch state.mode {
case .countdown(let info):
Text(info.fireDate, style: .timer)
case .paused(let info):
let remaining = info.totalCountdownDuration - info.previouslyElapsedDuration
Text(Duration.seconds(remaining), format: .time(pattern: .minuteSecond))
case .alert(let info):
Text("\(info.time.hour):\(String(format: "%02d", info.time.minute))")
@unknown default:
Text("--:--")
}
}
}// Daily alarm (every day)
let dailySchedule = Alarm.Schedule.relative(.init(
time: .init(hour: 7, minute: 0),
repeats: .weekly([.sunday, .monday, .tuesday, .wednesday,
.thursday, .friday, .saturday])
))
// Weekday-only alarm
let weekdaySchedule = Alarm.Schedule.relative(.init(
time: .init(hour: 6, minute: 30),
repeats: .weekly([.monday, .tuesday, .wednesday, .thursday, .friday])
))
// Weekend alarm
let weekendSchedule = Alarm.Schedule.relative(.init(
time: .init(hour: 9, minute: 0),
repeats: .weekly([.saturday, .sunday])
))
// One-time alarm at a specific Date
let targetDate = Calendar.current.date(
from: DateComponents(year: 2026, month: 6, day: 15, hour: 14, minute: 30)
)!
let fixedSchedule = Alarm.Schedule.fixed(targetDate)Pattern for alarm with snooze (countdown behavior) and custom secondary action.
func scheduleAlarmWithSnooze(
hour: Int, minute: Int, snoozeDurationSeconds: TimeInterval
) async throws -> Alarm {
let id = UUID()
let alert = AlarmPresentation.Alert(
title: "Good Morning",
secondaryButton: AlarmButton(
text: "Snooze 5 min", textColor: .white, systemImageName: "zzz"
),
secondaryButtonBehavior: .countdown // tapping Snooze restarts countdown
)
// postAlert defines the snooze duration
let countdown = Alarm.CountdownDuration(
preAlert: nil, postAlert: snoozeDurationSeconds
)
let countdownPresentation = AlarmPresentation.Countdown(
title: "Snoozing...", pauseButton: nil
)
let presentation = AlarmPresentation(
alert: alert, countdown: countdownPresentation
)
let attributes = AlarmAttributes(
presentation: presentation,
metadata: nil as WakeUpMetadata?,
tintColor: .purple
)
let config = AlarmManager.AlarmConfiguration(
countdownDuration: countdown,
schedule: .relative(.init(
time: .init(hour: hour, minute: minute), repeats: .never
)),
attributes: attributes,
stopIntent: StopAlarmIntent(alarmID: id.uuidString),
secondaryIntent: SnoozeAlarmIntent(alarmID: id.uuidString),
sound: .default
)
return try await AlarmManager.shared.schedule(id: id, configuration: config)
}Use .custom behavior to trigger the secondaryIntent instead of restarting
a countdown. The intent opens the app or performs custom logic.
struct OpenAppIntent: LiveActivityIntent {
static var title: LocalizedStringResource = "Open App"
func perform() async throws -> some IntentResult { .result() }
}
func scheduleAlarmWithOpenAction(hour: Int, minute: Int) async throws -> Alarm {
let id = UUID()
let alert = AlarmPresentation.Alert(
title: "Medication Reminder",
secondaryButton: AlarmButton(
text: "Open", textColor: .blue, systemImageName: "pill.fill"
),
secondaryButtonBehavior: .custom // triggers secondaryIntent
)
let presentation = AlarmPresentation(alert: alert)
let attributes = AlarmAttributes<WakeUpMetadata>(
presentation: presentation, metadata: nil, tintColor: .blue
)
let config = AlarmManager.AlarmConfiguration.alarm(
schedule: .relative(.init(
time: .init(hour: hour, minute: minute), repeats: .never
)),
attributes: attributes,
stopIntent: StopAlarmIntent(alarmID: id.uuidString),
secondaryIntent: OpenAppIntent(),
sound: .default
)
return try await AlarmManager.shared.schedule(id: id, configuration: config)
}<key>NSAlarmKitUsageDescription</key>
<string>We schedule alerts for alarms and timers you create.</string>This key is mandatory. If missing or empty, schedule(id:configuration:)
will fail and no alarms can be created by the app.
If you support countdown presentation, add a widget extension target for the
custom Live Activity UI. Keep NSAlarmKitUsageDescription in the host app's
Info.plist; Apple does not document NSSupportsLiveActivities as an AlarmKit
setup key.
import AlarmKit
func scheduleAlarmSafely<Metadata: AlarmMetadata>(
id: UUID,
configuration: AlarmManager.AlarmConfiguration<Metadata>
) async {
guard AlarmManager.shared.authorizationState == .authorized else {
print("Not authorized -- request authorization first")
return
}
do {
let alarm = try await AlarmManager.shared.schedule(
id: id, configuration: configuration
)
print("Scheduled alarm: \(alarm.id), state: \(alarm.state)")
} catch let error as AlarmManager.AlarmError {
switch error {
case .maximumLimitReached:
print("Too many alarms -- cancel an existing one first")
@unknown default:
print("AlarmKit error: \(error)")
}
} catch {
print("Unexpected error: \(error)")
}
}
// State transition helpers -- each throws if alarm is in wrong state
func cancelAlarmSafely(id: Alarm.ID) {
do { try AlarmManager.shared.cancel(id: id) }
catch { print("Failed to cancel: \(error)") }
}
func pauseAlarmSafely(id: Alarm.ID) {
// Only valid when alarm is in .countdown state
do { try AlarmManager.shared.pause(id: id) }
catch { print("Cannot pause: \(error)") }
}.tessl-plugin
skills
accessorysetupkit
references
activitykit
references
adattributionkit
references
alarmkit
references
app-clips
app-intents
references
app-store-optimization
app-store-review
apple-on-device-ai
appmigrationkit
references
audioaccessorykit
references
authentication
references
avkit
references
background-processing
references
browserenginekit
references
callkit
references
carplay
references
cloudkit
references
contacts-framework
references
core-bluetooth
references
core-data
core-motion
references
core-nfc
references
coreml
references
cryptokit
references
cryptotokenkit
references
debugging-instruments
device-integrity
references
dockkit
references
energykit
references
eventkit
references
financekit
references
focus-engine
gamekit
references
healthkit
references
homekit
references
ios-accessibility
ios-localization
ios-networking
ios-simulator
references
mapkit
metrickit
references
musickit
references
natural-language
references
paperkit
references
passkit
references
pdfkit
references
pencilkit
references
permissionkit
references
photokit
push-notifications
realitykit
references
relevancekit
references
scenekit
references
sensorkit
references
speech-recognition
references
spritekit
references
storekit
swift-api-design-guidelines
swift-architecture
swift-charts
references
swift-codable
swift-concurrency
swift-formatstyle
swift-language
swift-security
references
swift-testing
swiftdata
swiftlint
swiftui-animation
swiftui-gestures
references
swiftui-layout-components
swiftui-liquid-glass
references
swiftui-patterns
swiftui-performance
swiftui-uikit-interop
swiftui-webkit
tabletopkit
references
tipkit
references
vision-framework
weatherkit
references
widgetkit
references