mcpField
A
axiom-alarmkit-ref

axiom-alarmkit-ref

github.com/charleswiltgen开发1.2k88 次安装今天更新
代码仓库

关于 axiom-alarmkit-ref

面向 iOS 开发者的 AlarmKit Swift 参考技能,涵盖授权、闹钟与计时器调度、界面呈现及 Live Activity 集成。

主要功能

  1. 01AlarmKit API 组件参考
  2. 02授权请求与状态观察
  3. 03一次性及重复闹钟
  4. 04倒计时、暂停、恢复与稍后提醒
  5. 05闹钟界面与 Live Activity 指引

适用场景

  1. 01在 iOS 应用中实现定时闹钟
  2. 02添加支持暂停与恢复的倒计时
  3. 03构建锁定屏幕和灵动岛闹钟界面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-alarmkit-ref

安装后在 Claude Code 中输入/axiom-alarmkit-ref确认已加载。

详细介绍

AlarmKit 参考

AlarmKit 的完整 API 参考。AlarmKit 是 Apple 的框架,用于安排闹钟和倒计时计时器,并提供系统级提醒、Dynamic Island 集成,以及对专注模式和静音模式的覆盖。

概览

AlarmKit 允许应用创建行为类似于内置“时钟”应用的闹钟和计时器——它们会覆盖“勿扰模式”、显示在 Dynamic Island 中,并显示在锁定屏幕上。该框架通过一组以 AlarmManager 为核心的类型处理安排、稍后提醒、暂停/继续和 UI 呈现。

系统要求

  • iOS 26+(AlarmKit 在 iOS 26 中引入)
  • 需要 Widget Extension 来呈现实时活动 / Dynamic Island
  • 建议使用实体设备进行闹钟声音和通知测试

第 1 部分:关键组件

AlarmManager

所有闹钟操作的单例入口。

import AlarmKit

let manager = AlarmManager.shared

所有安排、取消和观察流程都通过此共享实例进行。

Alarm

描述可触发一次或按重复计划触发的闹钟。

struct Alarm {
    var id: UUID
    var schedule: Schedule?
    var countdownDuration: CountdownDuration?
    var state: AlarmState
}
AlarmPresentation

闹钟 UI 在三种状态下的内容——提醒中、倒计时和已暂停。

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

用于将呈现内容与应用专属元数据和色调配对的通用容器。用于配置实时活动小组件。

struct AlarmAttributes<Metadata: AlarmMetadata> {
    var presentation: AlarmPresentation
    var metadata: Metadata
    var tintColor: Color
}
AlarmMetadata

用于附加到闹钟的应用专属数据协议。可以遵循一个空结构体以满足最低使用需求,也可以添加属性以实现更丰富的 UI。

struct RecipeMetadata: AlarmMetadata {
    let recipeName: String
    let cookingStep: String
}

第 2 部分:授权

应用必须先请求权限,才能安排闹钟。将 NSAlarmKitUsageDescription 添加到 Info.plist。

请求授权
func requestAlarmAuthorization() async -> Bool {
    do {
        let state = try await AlarmManager.shared.requestAuthorization()
        return state == .authorized
    } catch {
        print("Authorization error: \(error)")
        return false
    }
}
检查当前状态

使用 authorizationState(而不是 authorizationStatus)读取当前值:

let state = await AlarmManager.shared.authorizationState
// .authorized | .denied | .notDetermined
观察授权变更
for await authState in AlarmManager.shared.authorizationUpdates {
    switch authState {
    case .authorized: enableAlarmUI()
    case .denied:     showPermissionPrompt()
    case .notDetermined: break
    @unknown default: break
    }
}

第 3 部分:安排闹钟

每个闹钟都需要一个 UUID、一个 AlarmManager.AlarmConfiguration,以及对 schedule(id:configuration:) 的调用。

单次闹钟
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)
重复闹钟

使用 .weekly(Array(weekdays)) 指定具体日期:

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])
))
倒计时计时器

将 schedule: nil,并提供 countdownDuration 和一个 preAlert 时间间隔:

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
)

计时器支持暂停/继续;提供 AlarmPresentation.countdown 时,会显示倒计时呈现内容。

稍后提醒配置

稍后提醒使用 CountdownDuration.postAlert,并配合 .snoozeButton 次要操作:

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
)

第 4 部分:自定义闹钟 UI

提醒呈现

闹钟触发时显示提醒状态。停止按钮为必需项;次要按钮为可选项。

// 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
)
倒计时呈现

计时器倒计时期间显示。仅适用于设置了 countdownDuration.preAlert 的闹钟。

let countdown = AlarmPresentation.Countdown(
    title: "Timer Running",
    pauseButton: .pauseButton
)
暂停呈现

倒计时计时器暂停时显示。

let paused = AlarmPresentation.Paused(
    title: "Timer Paused",
    resumeButton: .resumeButton
)
完整的三态呈现

组合全部三种状态,打造完整的计时器体验:

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
    )
)

第 5 部分:管理闹钟

获取所有闹钟
let alarms = try AlarmManager.shared.alarms
暂停 / 继续
try await AlarmManager.shared.pause(id: alarmID)
try await AlarmManager.shared.resume(id: alarmID)
取消
try await AlarmManager.shared.cancel(id: alarmID)
观察闹钟更新

使用 alarmUpdates 使 UI 保持同步。若某个闹钟未出现在发出的数组中,则表示它已不再安排。

for await alarms in AlarmManager.shared.alarmUpdates {
    self.alarms = alarms
}

第 6 部分:实时活动集成

AlarmKit 闹钟通过 ActivityConfiguration 显示在 Dynamic Island 和锁定屏幕上。添加一个 Widget Extension 目标,并使用 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")
            }
        }
    }
}

第 7 部分:SwiftUI 集成

使用 @Observable 的 ViewModel 模式
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)
            }
        }
    }
}
闹钟列表视图
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()
            }
        }
    }
}

第 8 部分:最佳实践

实践详情
尽早请求授权在首次启动或首次尝试创建闹钟时请求
妥善处理拒绝授权的情况如果用户拒绝了权限,引导他们前往“设置”
持久化闹钟 UUID存储 ID,以便在应用启动之间管理闹钟
实现小组件扩展倒计时/灵动岛展示所必需
使用 alarmUpdates让 UI 保持同步;不要轮询或缓存过期状态
在实体设备上测试闹钟声音、通知和实时活动需要真实硬件
遵守系统限制系统对每个应用可创建的闹钟数量设有上限
使用 authorizationState不是 authorizationStatus——正确的属性名称是 authorizationState

资源

WWDC:2025-230

文档:/alarmkit, /alarmkit/alarmmanager, /alarmkit/alarm, /alarmkit/alarmpresentation, /alarmkit/alarmattributes

技能:axiom-extensions-widgets-ref, axiom-swiftui-26-ref

评论

支持 Markdown