移动/游戏
在 iOS/iPadOS 26+ 需要系统级闹钟…
为 iOS/iPadOS 26+ 应用实现会在锁屏、灵动岛、StandBy 和配对的 Apple Watch 上出现的系统闹钟、倒计时器或闹钟式提醒时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 iOS/iPadOS 26+ 需要系特定
解决思路
- 将 `NSAlarmKitUsageDescription` 添加到 Info.plist,并提供面向用户的说明字符串。
- 在应用能解释价值时调用 `AlarmManager.shared.requestAuthorization()` 显式请求授权,或处理首次调度时的系统自动提示。
- 如果授权状态是 `.denied` 或不是 `.authorized`,显示恢复 UI,不要继续调度。
- 通过 `manager.authorizationState` 同步检查当前授权状态 `.authorized`、`.denied`、`.notDetermined`。
- 通过 `manager.authorizationUpdates` 异步序列观察授权状态变化,并分别处理 `.authorized`、`.denied`、`.notDetermined` 和 `@unknown default`。
- 配置 `AlarmPresentation`,包括 alert、countdown、paused 状态所需的展示内容。
- 创建 `AlarmAttributes`,传入 presentation、可选 metadata 和 tint color。
- 构建 `AlarmManager.AlarmConfiguration`,根据场景选择 `.alarm` 或 `.timer`。
- 调用 `AlarmManager.shared.schedule(id:configuration:)` 调度闹钟或计时器。
- 观察 `alarmManager.alarmUpdates`,确认已调度的 ID 到达预期状态。
- 如果使用倒计时,添加 Widget Extension target,并为同一个 `AlarmAttributes<Metadata>` 类型添加 `ActivityConfiguration`。
- 已有闹钟代码时,按 Review Checklist 逐项检查。
- 选择类型:需要在某个时钟时间触发时使用 `.alarm(schedule:...)`。
- 选择类型:需要从现在起经过一段时长后触发时使用 `.timer(duration:...)`。
- 调度重复闹钟时,使用 `.fixed(date)` 表示一次性绝对日期,或使用 `.relative` 表示本地时钟时间并配合 `.never` 或 `.weekly` 重复;每日、工作日、周末和固定日期变体参考 recurring patterns。
- 调度时创建 UUID 作为 ID,定义 `AlarmPresentation.Alert`,可包含 title、`secondaryButton` 和 `secondaryButtonBehavior`。
- 在 `AlarmAttributes` 中传入 presentation、metadata(可为 nil)和 tintColor,并定义符合 `AlarmMetadata` 的 metadata 类型。
- 使用 `Alarm.CountdownDuration(preAlert:postAlert:)` 设置倒计时阶段,再放入 `AlarmManager.AlarmConfiguration`,并设置 schedule、attributes、sound。
- 对标准系统 Stop 行为省略 `stopIntent`;仅当 Stop 需要执行应用清理、自定义停止行为或其他副作用时提供 `stopIntent`。
- 对普通 Snooze/Repeat 使用 `secondaryButtonBehavior: .countdown` 和 `Alarm.CountdownDuration.postAlert`,省略 `secondaryIntent`;仅当需要 `.custom` 次要行为或应用清理/自定义行为时提供 `secondaryIntent`。
- 理解并处理状态转移:`scheduled -> countdown -> alerting`,以及 `paused` 通过 `resume(id:)` 回到 `countdown`。
- 用 `cancel(id:)` 从系统完全移除闹钟,包括重复闹钟。
- 用 `pause(id:)` 暂停正在倒计时的闹钟;其他状态调用会抛出错误。
- 用 `resume(id:)` 恢复已暂停闹钟;其他状态调用会抛出错误。
- 用 `stop(id:)` 停止闹钟;一次性闹钟会被移除,重复闹钟会重新调度。
- 用 `countdown(id:)` 从 alerting 状态重新开始倒计时(snooze);其他状态调用会抛出错误。
- 计时器在经过时长后触发并总是显示倒计时 UI。
- 用 `Alarm.CountdownDuration` 控制可见倒计时阶段:`preAlert` 是触发前倒计时秒数(主倒计时),`postAlert` 是触发后用于 repeat/snooze 的倒计时秒数。
- 读取每个 `Alarm` 的 `state` 属性,区分 `.scheduled`、`.countdown`、`.paused`、`.alerting`。
- 使用 `try AlarmManager.shared.alarms` 获取当前 daemon 快照;传播错误,或在启动刷新时用 `do/catch` 包裹后再依赖快照。
- 观察 `alarmUpdates`,不要维护独立生命周期;按 ID 与系统序列对账。
- 当闹钟从 `alarmUpdates` 消失时,视为不再被 AlarmKit 调度;需要区分已触发、已取消和已重调度时,与 app 持久化的 ID 比较。
- 使用 `AlarmAttributes` 作为 Live Activity 静态数据;它遵循 `ActivityAttributes`,并泛型约束 `Metadata` 符合 `AlarmMetadata`(继承 `Decodable`、`Encodable`、`Hashable`、`Sendable`),metadata 本身可选且默认 nil。
- 用 `AlarmPresentation` 提供必需的 alerting 内容和可选的 countdown/paused 内容;系统渲染 alerting UI,Widget Extension 仅用相同 attributes 和 presentation state 自定义 countdown/paused Live Activity 视图。
- 保持 metadata 轻量;不需要时用 nil;并让 app 与 Widget Extension 共享该 metadata 类型。
- 在 Widget Extension 中读取系统管理的 `AlarmPresentationState.mode`:`.alert(Alert)` 表示正在触发并含计划时间,`.countdown(Countdown)` 表示正在倒计时并含触发日期与时长,`.paused(Paused)` 表示已暂停并含已过时长与总时长;据此决定 Dynamic Island 和锁屏上的非 alerting 状态 UI。
- 用 `AlarmButton` 定义 alarm action 的文本、颜色和 symbol。
- 配置次要按钮行为:`.countdown` 使用 `postAlert` 时长重新开始倒计时(snooze);`.custom` 触发 `secondaryIntent`(例如打开 app)。
- 依赖 AlarmKit 将闹钟作为 Live Activity 显示在锁屏、灵动岛、StandBy 和配对的 Apple Watch 上;alerting UI 由系统管理。
- 如果闹钟使用 countdown presentation,应添加 Widget Extension;否则闹钟可能被意外关闭或无法提醒,尽管系统在有限情况下(如设备重启后首次解锁前)仍可显示回退倒计时 UI。
- 按职责路由:Authorization、scheduling/state、presentation、sound、system alarm actions 归 AlarmKit。
- 按职责路由:Home Screen/Smart Stack widgets、families、timelines、reloads 归 `widgetkit`。
- 按职责路由:非闹钟 Live Activity 生命周期、tokens、remote content state 归 `activitykit`。
- 按职责路由:APNs、notification categories/actions、自定义通知 UI 归 `push-notifications`。
- 记住 AlarmKit 的 alert 在锁屏、灵动岛、StandBy 和配对 Apple Watch 上由系统渲染;只有 countdown/paused 状态使用 Widget Extension。
- 设置时只使用 Apple 文档命名的 `NSAlarmKitUsageDescription` 和 `AlarmManager` 授权;除非当前 Apple 来源明确说明,不要要求不支持的 AlarmKit 设置键或 `com.apple.developer.alarmkit`。
- 需要完整共享 attributes 的 Widget 实现时,查阅 references/alarmkit-patterns.md 的 Live Activity Widget Extension for Alarms。
- 避免常见错误:缺少 usage string 或授权门禁时,添加 `NSAlarmKitUsageDescription` 并在调度前处理拒绝。
- 避免常见错误:不要用 Timer 实现重复;重复场景使用带 `.weekly([...])` 的 alarm。
- 避免常见错误:不要用 app 自有状态替代 `alarmUpdates`;观察系统序列并按 ID 对账。
- 避免常见错误:不要为标准 Stop/Snooze 添加 intents;除非需要清理或自定义行为。
- 避免常见错误:不要传大 `AlarmMetadata` payload;保持轻量 metadata 或用 ID 引用 app 数据。
- 避免常见错误:不要使用已弃用的 `stopButton` 初始化器;使用 `init(title:secondaryButton:secondaryButtonBehavior:)`。
- 复核:Setup 和授权门禁通过,且不使用不支持的 entitlement keys。
- 复核:Alarm/timer 选择、presentation、metadata、intents、snooze duration 和 tint 有效。
- 复核:保留已调度 ID 并通过 `alarmUpdates` 对账;处理操作错误。
- 复核:Countdown 使用共享的 Widget Extension attributes 和 presentation state。
- 复核:系统管理的 alert UI 和相邻 skill 所有权遵循路由表。
- 复核:Sound、vibration、Stop/Snooze 和状态变化在真机测试通过。
- 需要完整代码模式时查阅 references/alarmkit-patterns.md,包括授权、调度、倒计时、snooze 处理和 widget setup。
适用边界 · 注意事项
- 目标平台低于 iOS 26/iPadOS 26,或不需要 Apple 系统闹钟体验时不要使用 AlarmKit。
- 需要自定义 alert 通知 UI、APNs、通知分类/操作,或非闹钟 Live Activity 生命周期/远程内容状态时,应改用 push-notifications、activitykit 等相邻能力。
- 没有 Widget Extension 却要自定义 countdown/paused Live Activity 展示,或希望系统 alert UI 可自定义时,不适合 AlarmKit。