
-
使用 AlarmKit API 实现唤醒功能
铃-铃-铃!从你食谱 App 中的倒计时器到你旅行规划 App 中的闹钟,iOS 和 iPadOS 26 中的 AlarmKit 框架可将计时器和闹钟引入到锁定屏幕、灵动岛等其他地方。了解如何使用 App Intents 框架来创建和管理你 App 的闹钟,自定闹钟的实时活动,并提供自定提醒操作。为了充分从这个视频中获益,建议你先观看 WWDC23 讲座“了解 ActivityKit”。
章节
资源
- ActivityKit
- AlarmKit
- App Intents
- Creating your first app intent
- Human Interface Guidelines: Live Activities
- Scheduling an alarm with AlarmKit
相关视频
WWDC25
WWDC23
-
搜索此视频…
大家好 我是系统体验团队的 工程师 Anton 在这个讲座中 你将了解 AlarmKit 它是一个框架 你可以利用它 在自己的 App 中创建闹钟 在这个视频中 首先 我将为大家演示 可以使用 AlarmKit 构建的体验 然后 我将讨论如何 获得 App 授权以使用这个框架 如何创建闹钟 以及如何管理它的生命周期 先说体验 闹钟是醒目的提醒 目的是让用户注意在预先确定的 固定时间发生的事情 它基于日程表或倒计时 一旦触发 提醒就会突破静音模式 和当前专注模式
在提醒中 用户会看到 自定提醒标题 以及 App 的名称 用户可以选择让提醒停下 或者使用可选的“Snooze”按钮 稍后提醒
另外 闹钟可以拥有一个自定按钮 它的操作由 App Intent 定义
还有其他多项系统体验 也支持闹钟 例如在待机显示中 以及闹钟触发时 直接在与 iPhone 配对的 Apple Watch 上操作
闹钟支持使用 App 提供的实时活动 来自定倒计时界面 这个界面可以显示在锁定屏幕 以及灵动岛和待机显示中
用户可以选择为他们设备上的 每个 App 启用闹钟功能 你现在已知道闹钟是什么了 接下来我们谈谈为了让用户能够 授权你的 App 设定闹钟 你需要做什么
你的 App 需要取得用户同意 然后才能设定闹钟 而用户的同意 是通过提供授权来完成的 你可以手动请求授权 或者在创建第一个闹钟时 由系统自动请求授权 用户可以随时在“设置”App 中 更改授权状态 设置授权很简单 只需将 NSAlarmKitUsageDescription 添加到 App 的 info plist 文件 说明显示闹钟的用例 提供简短的描述性说明 解释你的 App 将如何使用闹钟 从而帮助用户做出明智决策 要手动请求授权 可以使用 AlarmManager requestAuthorization API
如果用户之前没有做出选择 系统会显示提示 其中包含使用描述
设定闹钟之前 你可以通过 在 AlarmManager 类中 查询 authorizationState 来查看授权状态
在这个示例中 如果没有确定 我可以手动请求授权 如果获得授权 就可以继续设定闹钟了 如果被拒绝 请务必 在 App 界面中明确说明 无法设定闹钟 你现在已经熟悉了 闹钟的授权设置 接下来我们谈谈如何创建闹钟 创建闹钟所需的主要组件 包括倒计时持续时间、 包含特定日期 或重复模式的日程表、 外观配置、 自定操作的处理 以及关联的声音
先说说倒计时持续时间 闹钟可以具有提醒前 和提醒后倒计时间隔 首次设定闹钟后 它会显示倒计时 UI 持续显示的时长为 提醒前倒计时持续时间 提醒前持续时间过后 闹钟就会触发 并显示根据你的配置自定的提醒
如果用户将闹钟延时 系统会再次显示倒计时用户界面 持续显示的时长为提醒后间隔
这个间隔过后 闹钟会再次触发 用户可以将它延时提醒或关闭
在这个示例中 我要设置一个计时器 提醒前持续时间为10分钟 如果闹钟触发后 用户选择重复提醒 闹钟就会遵循提醒后持续时间 进行 5 分钟倒计时 然后再次触发 创建闹钟时 你还可以提供一个日程表 它可以是固定的 也可以是相对的
固定时间表会指定一个未来日期 到那时 闹钟会发出提醒 这个时间表具有绝对性 不会随设备时区更改而变化 在这个示例中 我为 WWDC 主题演讲 设置了一个闹钟 我为 6 月 9 日上午 9:41 创建了相应的日期组件 然后使用这些组件来创建日期
接下来使用固定构造器 将这个日期传递给闹钟日程表 还可以为闹钟指定相对时间表 其中包括一天中的时间 和可选的每周重复模式 相对时间表会考虑到时区更改
在这个示例中 我要将闹钟设置为 每逢周一、周三 和周五上午 7 点触发 我设置时间部分的小时和分钟 并指定每周重复模式
你现在已经熟悉了 如何设定闹钟 接下来我们看看 如何自定义闹钟外观 你可以自定义许多元素 我想在我的 App 中 创建一个烹饪计时器 我想自定闹钟触发时 显示提醒的方式 首先创建提醒按钮 为此使用了 AlarmButton 结构 它让我能够指定按钮的 text、 textColor 和 systemImage
我使用它把停止按钮的 标题定义为“Dismiss” 文本颜色为白色
它还将包含一个 SF Symbol 在灵动岛中显示提醒时 会使用这个符号
现在可以创建 alertPresentation 并设置闹钟标题 说明食物做好了 我还添加了刚刚创建的停止按钮 接下来创建 AlarmAttributes 这些信息是渲染闹钟画面效果 所必需的 在 presentation 参数中 传递刚刚创建的 alertPresentation 最后 创建 alarmConfiguration 设定闹钟所必需的 所有组件就是这些 包括倒计时持续时间、 日程表和属性 将这些属性 传递给 alarmConfiguration
现在 闹钟提醒画面效果设置好了 其中有一个“Dismiss”按钮
如果闹钟只需要显示提醒 设置闹钟外观所需的 所有操作就是这些 但如果你希望闹钟显示倒计时界面 那就还需要完成几个步骤 接下来 我将为我的烹饪闹钟 完成这些步骤 首先在提醒中添加一个 重复按钮 用于触发倒计时
我使用 AlarmButton 结构 创建了重复按钮 并将标题设置为“Repeat” 还将字体颜色指定为白色 并指定了重复图标 闹钟在灵动岛中显示提醒时 会显示这个图标
现在 创建 alertPresentation 时 我还添加了重复按钮
secondaryButton 行为参数 指定了二级按钮是否会让闹钟 转换为倒计时状态 比如计时器重复或闹钟延时 或者是否会执行某个自定操作 在这个示例中 我希望在轻点“Repeat”按钮后 提醒能转换为倒计时 我通过将 secondaryButtonBehavior 设置为 countdown 来指定这一点 和之前一样 创建 AlarmAttributes 其中包含 alertPresentation 然后创建包含这些属性的 AlarmConfiguration 现在 我已经在提醒中 添加了一个“Repeat”按钮 轻点它 就会开始倒计时 接下来,我需要实现一个实时活动 来显示倒计时的 UI 如果闹钟支持倒计时功能 那么 App 需要 使用实时活动来实现它 接下来我会这么做 我要创建一个倒计时 在食物烹饪完毕时通知我 它会显示在锁定屏幕上、 灵动岛中和待机显示中
如果你知道如何构建实时活动 那么你已经知道了 如何为倒计时创建自定义界面 你需要将倒计时实时活动 添加到 App 的小组件扩展中
如果你还没有实时活动 别慌! 你可以先观看 WWDC23 中的 视频“了解 ActivityKit” 然后设置 ActivityConfiguration 并使用元数据类型 指定 AlarmAttributes
在这个示例中 我可以设置 ActivityConfiguration 并指定它将使用 AlarmAttributes 和 CookingData 元数据 首先重点关注锁定屏幕 我的闹钟倒计时可以 处于倒计时状态或暂停状态 我将为每种状态提供相应的视图
为此 我会检查 context 对象中的 当前闹钟模式 并渲染相应的视图
首先处理 countdown 状态 并提供 countdownView 接下来处理 paused 状态 并提供 pausedView 现在 我要设置 灵动岛外观的扩展区域 其中包含了闹钟标题、 倒计时和按钮 然后 设置灵动岛的 紧凑视图和最小视图 其中包含了闹钟倒计时和一个图标 现在 我已经设置了 基本的实时活动倒计时 我想添加一个图标来指示 食物的烹饪方式 我可以在 AlarmMetadata 中 提供这些额外的信息 CookingData 是一个符合 AlarmMetadata 协议的结构 我定义了一个字符串枚举 来指定烹饪方法 并添加了 frying 和 grilling 选项 然后创建一个属性 来存储我的烹饪方法 现在 我可以使用 CookingData 元数据了 在我的 App 中设定闹钟时 我创建了 CookingData 对象 并将 frying 指定为烹饪方法 然后在创建 AlarmAttributes 时 添加这个 customMetadata
在我的实时活动中 我将使用 context 属性 访问 customMetadata 中的烹饪方法
然后 我可以使用烹饪方法 创建一个图标 现在 我的倒计时在渲染后 带有我使用烹饪元数据 中的烹饪方法生成的 油炸图标 我在锁定屏幕上 和灵动岛中都使用这个图标
现在 我已成功使用实时活动 设置好闹钟倒计时 我还使用闹钟元数据为闹钟倒计时 创建了一个自定图标
如果闹钟支持倒计时功能 系统会保证显示倒计时界面 在某些情况下 实时活动无法显示 例如 设备重新启动之后 和首次解锁之前 在这种情况下 你仍然可以 自定倒计时在系统中的 画面效果 现在 我要为烹饪闹钟倒计时 设置系统画面效果 首先 使用 AlarmButton 结构 定义暂停按钮 并将它设置为具有暂停系统图标
接下来 创建倒计时画面效果 将标题设置为说明 食物正在烹饪中 并添加暂停按钮
现在 我已经定义了 倒计时画面效果 接下来我要将它添加为 AlarmAttributes 的组成部分 由于我的闹钟支持暂停功能 因此我还要为烹饪倒计时 设置暂停系统画面效果 倒计时暂停后 我希望 用户能够让倒计时继续进行 首先 使用 AlarmButton 结构 定义继续按钮 并将它设置为具有播放图标
然后 创建 pausedPresentation 并将标题设置为说明烹饪已暂停 我还传入了继续按钮
现在 我可以将 pausedPresentation 添加到 AlarmAttributes 中 现在 我已经搞定了提醒、 倒计时和暂停的系统画面效果 接下来 我们花点时间 讨论一下色调 色调应用于所有这些画面效果 有助于让用户看到你的闹钟 就联想到你的 App 我把它作为 AlarmAttributes 的组成部分进行传递 在提醒画面效果中 色调用于设置二级按钮的 填充颜色
在锁定屏幕上 色调用于 对二级按钮中的符号 以及闹钟标题和倒计时进行着色 同样 它也用于灵动岛 你现在已经熟悉了 如何设置闹钟外观 接下来 我们谈谈 如何自定闹钟的操作 AlarmKit 让你能够 在用户轻点闹钟图标时 运行你自己的代码 为此 可以使用 App Intent 你可以为停止按钮 或二级按钮提供自定 App Intent 我们创建一个自定二级按钮 用户轻点时 它将执行 一个 Intent 以打开我的 App 为此 我需要修改提示的外观 首先 使用 AlarmButton 结构 创建一个打开按钮 标题为“Open” 文本颜色为白色 在灵动岛中有一个箭头图标
创建 alertPresentation 时 我将 openButton 添加为 secondaryButton
由于我希望这个按钮执行自定操作 因此将 secondaryButtonBehavior 更改为 custom
现在 我的自定二级按钮 已经准备好了 接下来 使用 App Intent 设置操作 在用户轻点按钮时打开我的 App 这就是我想使用的 OpenInApp Intent 它包含了用户轻点按钮时 对应的闹钟标识符 它还将 openAppWhenRun 标志设置为 true 表明我希望 在执行这个 Intent 时 打开我的 App 打开 App 后 我就可以执行其他任务 比如显示带这个标识符的 闹钟的更详细视图 我们使用刚刚创建的 Intent 设定闹钟时 我创建了唯一标识符 这样我就可以 在创建这个闹钟后跟踪它 我创建了一个 OpenInApp Intent 的实例 并传入闹钟的唯一标识符
现在 创建 alarmConfiguration 时 我会添加 secondaryIntent 向系统表明 我要在用户 轻点二级按钮时 运行这个 Intent
现在 我已经在提醒中添加了 一个自定打开按钮 定义了一个 App Intent 来打开我的 App 并指示系统在用户轻点打开按钮时 运行这个 Intent 接下来 我们花点时间讨论一下 如何配置闹钟的声音 如果你没有指定 sound 参数 闹钟就会使用默认的系统声音 还可以为闹钟提供自定声音 由于 AlarmKit 使用 ActivityKit 来实现闹钟画面效果 你可以使用它的 AlertSound 结构 来定义自定声音
你需要指定声音文件的名称 声音文件应该位于 App 的主套装中 或是 App 数据容器的 Library/Sounds 文件夹中 设置完闹钟后 现在 可以在系统中对它进行管理 为此可以使用 AlarmManager 类 配置好闹钟后 我就可以通过系统来设定闹钟了 为此 我可以使用闹钟的唯一标识符 以及我之前创建的配置 在闹钟的整个生命周期中都应该 使用这个标识符对闹钟进行跟踪
你可以完全控制闹钟的生命周期 可以将它转换为倒计时状态 也可以对它执行取消、停止、 暂停或继续操作
我想告诉大家一些 使用 AlarmKit 的最佳实践 闹钟非常适合具有特定时间间隔的 倒计时 比如烹饪计时器 也非常适合遵循日程表的重复提醒 比如叫醒闹钟 闹钟不能取代其他醒目通知 比如重要提醒或具有时效性的通知
提醒画面效果要尽量清晰明了 提醒会以醒目的方式显示 而且你也希望用户能够 轻松看懂闹钟的内容 以及他们可以执行的操作
如果你的闹钟支持倒计时 请考虑在实时活动中 包含倒计时的主要元素 包括剩余时长、关闭按钮 以及暂停按钮或恢复按钮 今天 你了解了如何 使用 AlarmKit 创建闹钟 以及如何在系统中 管理闹钟的生命周期 现在 你可以在 App 中试一试 使用 AlarmKit 为 App 的用例配置闹钟 使用实时活动 在锁定屏幕上 和灵动岛中打造自定倒计时体验 使用 App Intents 为闹钟添加自定操作
我讲完了 谢谢各位的参与
-
-
2:41 - Check authorization status
// Check authorization status import AlarmKit func checkAuthorization() { switch AlarmManager.shared.authorizationState { case .notDetermined: // Manually request authorization case .authorized: // Proceed with scheduling case .denied: // Inform status is not authorized } }
-
4:08 - Set up the countdown duration
// Set up the countdown duration import AlarmKit func scheduleAlarm() { /* ... */ let countdownDuration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60)) /* ... */ }
-
4:40 - Set a fixed schedule
// Set a fixed schedule import AlarmKit func scheduleAlarm() { /* ... */ let keynoteDateComponents = DateComponents( calendar: .current, year: 2025, month: 6, day: 9, hour: 9, minute: 41) let keynoteDate = Calendar.current.date(from: keynoteDateComponents)! let scheduleFixed = Alarm.Schedule.fixed(keynoteDate) /* ... */ }
-
5:13 - Set a relative schedule
// Set a relative schedule import AlarmKit func scheduleAlarm() { /* ... */ let time = Alarm.Schedule.Relative.Time(hour: 7, minute: 0) let recurrence = Alarm.Schedule.Relative.Recurrence.weekly([ .monday, .wednesday, .friday ]) let schedule = Alarm.Schedule.Relative(time: time, repeats: recurrence) /* ... */ }
-
5:43 - Set up alert appearance with dismiss button
// Set up alert appearance with dismiss button import AlarmKit func scheduleAlarm() async throws { typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData> let id = UUID() let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60)) let stopButton = AlarmButton( text: "Dismiss", textColor: .white, systemImageName: "stop.circle") let alertPresentation = AlarmPresentation.Alert( title: "Food Ready!", stopButton: stopButton) let attributes = AlarmAttributes<CookingData>( presentation: AlarmPresentation( alert: alertPresentation), tintColor: Color.green) let alarmConfiguration = AlarmConfiguration( countdownDuration: duration, attributes: attributes) try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration) }
-
7:17 - Set up alert appearance with repeat button
// Set up alert appearance with repeat button import AlarmKit func scheduleAlarm() async throws { typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData> let id = UUID() let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60)) let stopButton = AlarmButton( text: "Dismiss", textColor: .white, systemImageName: "stop.circle") let repeatButton = AlarmButton( text: "Repeat", textColor: .white, systemImageName: "repeat.circle") let alertPresentation = AlarmPresentation.Alert( title: "Food Ready!", stopButton: stopButton, secondaryButton: repeatButton, secondaryButtonBehavior: .countdown) let attributes = AlarmAttributes<CookingData>( presentation: AlarmPresentation(alert: alertPresentation), tintColor: Color.green) let alarmConfiguration = AlarmConfiguration( countdownDuration: duration, attributes: attributes) try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration) }
-
9:15 - Create a Live Activity for a countdown
// Create a Live Activity for a countdown import AlarmKit import ActivityKit import WidgetKit struct AlarmLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: AlarmAttributes<CookingData>.self) { context in switch context.state.mode { case .countdown: countdownView(context) case .paused: pausedView(context) case .alert: alertView(context) } } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { leadingView(context) } DynamicIslandExpandedRegion(.trailing) { trailingView(context) } } compactLeading: { compactLeadingView(context) } compactTrailing: { compactTrailingView(context) } minimal: { minimalView(context) } } } }
-
10:26 - Create custom metadata for the Live Activity
// Create custom metadata for the Live Activity import AlarmKit struct CookingData: AlarmMetadata { let method: Method init(method: Method) { self.method = method } enum Method: String, Codable { case frying = "frying.pan" case grilling = "flame" } }
-
10:43 - Provide custom metadata to the Live Activity
// Provide custom metadata to the Live Activity import AlarmKit func scheduleAlarm() async throws { typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData> let id = UUID() let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60)) let customMetadata = CookingData(method: .frying) let stopButton = AlarmButton( text: "Dismiss", textColor: .white, systemImageName: "stop.circle") let repeatButton = AlarmButton( text: "Repeat", textColor: .white, systemImageName: "repeat.circle") let alertPresentation = AlarmPresentation.Alert( title: "Food Ready!", stopButton: stopButton, secondaryButton: repeatButton, secondaryButtonBehavior: .countdown) let attributes = AlarmAttributes<CookingData>( presentation: AlarmPresentation(alert: alertPresentation), metadata: customMetadata, tintColor: Color.green) let alarmConfiguration = AlarmConfiguration( countdownDuration: duration, attributes: attributes) try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration) }
-
11:01 - Use custom metadata in the Live Activity
// Use custom metadata in the Live Activity import AlarmKit import ActivityKit import WidgetKit struct AlarmLiveActivity: Widget { var body: some WidgetConfiguration { /* ... */ } func alarmIcon(context: ActivityViewContext<AlarmAttributes<CookingData>>) -> some View { let method = context.attributes.metadata?.method ?? .grilling return Image(systemName: method.rawValue) } }
-
12:03 - Set up the system countdown appearance
// Set up the system countdown appearance import AlarmKit func scheduleAlarm() async throws { typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData> let id = UUID() let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60)) let customMetadata = CookingData(method: .frying) let stopButton = AlarmButton( text: "Dismiss", textColor: .white, systemImageName: "stop.circle") let repeatButton = AlarmButton( text: "Repeat", textColor: .white, systemImageName: "repeat.circle") let alertPresentation = AlarmPresentation.Alert( title: "Food Ready!", stopButton: stopButton, secondaryButton: repeatButton, secondaryButtonBehavior: .countdown) let pauseButton = AlarmButton( text: "Pause", textColor: .green, systemImageName: "pause") let countdownPresentation = AlarmPresentation.Countdown( title: "Cooking", pauseButton: pauseButton) let attributes = AlarmAttributes<CookingData>( presentation: AlarmPresentation( alert: alertPresentation, countdown: countdownPresentation), metadata: customMetadata, tintColor: Color.green) let alarmConfiguration = AlarmConfiguration( countdownDuration: duration, attributes: attributes) try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration) }
-
12:43 - Set up the system paused appearance
// Set up the system paused appearance import AlarmKit func scheduleAlarm() async throws { typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData> let id = UUID() let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60)) let customMetadata = CookingData(method: .frying) let stopButton = AlarmButton( text: "Dismiss", textColor: .white, systemImageName: "stop.circle") let repeatButton = AlarmButton( text: "Repeat", textColor: .white, systemImageName: "repeat.circle") let alertPresentation = AlarmPresentation.Alert( title: "Food Ready!", stopButton: stopButton, secondaryButton: repeatButton, secondaryButtonBehavior: .countdown) let pauseButton = AlarmButton( text: "Pause", textColor: .green, systemImageName: "pause") let countdownPresentation = AlarmPresentation.Countdown( title: "Cooking", pauseButton: pauseButton) let resumeButton = AlarmButton( text: "Resume", textColor: .green, systemImageName: "play") let pausedPresentation = AlarmPresentation.Paused( title: "Paused", resumeButton: resumeButton) let attributes = AlarmAttributes<CookingData>( presentation: AlarmPresentation( alert: alertPresentation, countdown: countdownPresentation, paused: pausedPresentation), metadata: customMetadata, tintColor: Color.green) let alarmConfiguration = AlarmConfiguration( countdownDuration: duration, attributes: attributes) try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration) }
-
14:09 - Add a custom button
// Add a custom button import AlarmKit import AppIntents func scheduleAlarm() async throws { typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData> let id = UUID() let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60)) let customMetadata = CookingData(method: .frying) let secondaryIntent = OpenInApp(alarmID: id.uuidString) let stopButton = AlarmButton( text: "Dismiss", textColor: .white, systemImageName: "stop.circle") let openButton = AlarmButton( text: "Open", textColor: .white, systemImageName: "arrow.right.circle.fill") let alertPresentation = AlarmPresentation.Alert( title: "Food Ready!", stopButton: stopButton, secondaryButton: openButton, secondaryButtonBehavior: .custom) let pauseButton = AlarmButton( text: "Pause", textColor: .green, systemImageName: "pause") let countdownPresentation = AlarmPresentation.Countdown( title: "Cooking", pauseButton: pauseButton) let resumeButton = AlarmButton( text: "Resume", textColor: .green, systemImageName: "play") let pausedPresentation = AlarmPresentation.Paused( title: "Paused", resumeButton: resumeButton) let attributes = AlarmAttributes<CookingData>( presentation: AlarmPresentation( alert: alertPresentation, countdown: countdownPresentation, paused: pausedPresentation), metadata: customMetadata, tintColor: Color.green) let alarmConfiguration = AlarmConfiguration( countdownDuration: duration, attributes: attributes, secondaryIntent: secondaryIntent) try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration) } public struct OpenInApp: LiveActivityIntent { public func perform() async throws -> some IntentResult { .result() } public static var title: LocalizedStringResource = "Open App" public static var description = IntentDescription("Opens the Sample app") public static var openAppWhenRun = true @Parameter(title: "alarmID") public var alarmID: String public init(alarmID: String) { self.alarmID = alarmID } public init() { self.alarmID = "" } }
-
16:10 - Add a custom sound
// Add a custom sound import AlarmKit import AppIntents func scheduleAlarm() async throws { typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData> let id = UUID() let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60)) let customMetadata = CookingData(method: .frying) let secondaryIntent = OpenInApp(alarmID: id.uuidString) let stopButton = AlarmButton( text: "Dismiss", textColor: .white, systemImageName: "stop.circle") let openButton = AlarmButton( text: "Open", textColor: .white, systemImageName: "arrow.right.circle.fill") let alertPresentation = AlarmPresentation.Alert( title: "Food Ready!", stopButton: stopButton, secondaryButton: openButton, secondaryButtonBehavior: .custom) let pauseButton = AlarmButton( text: "Pause", textColor: .green, systemImageName: "pause") let countdownPresentation = AlarmPresentation.Countdown( title: "Cooking", pauseButton: pauseButton) let resumeButton = AlarmButton( text: "Resume", textColor: .green, systemImageName: "play") let pausedPresentation = AlarmPresentation.Paused( title: "Paused", resumeButton: resumeButton) let attributes = AlarmAttributes<CookingData>( presentation: AlarmPresentation( alert: alertPresentation, countdown: countdownPresentation, paused: pausedPresentation), metadata: customMetadata, tintColor: Color.green) let sound = AlertConfiguration.AlertSound.named("Chime") let alarmConfiguration = AlarmConfiguration( countdownDuration: duration, attributes: attributes, secondaryIntent: secondaryIntent, sound: sound) try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration) } public struct OpenInApp: LiveActivityIntent { public func perform() async throws -> some IntentResult { .result() } public static var title: LocalizedStringResource = "Open App" public static var description = IntentDescription("Opens the Sample app") public static var openAppWhenRun = true @Parameter(title: "alarmID") public var alarmID: String public init(alarmID: String) { self.alarmID = alarmID } public init() { self.alarmID = "" } }
-
-
- 0:00 - 开场介绍
AlarmKit 是一个全新的框架,可用于创建计时器和闹钟。这个讲座介绍了相关体验,以及如何获取授权、如何创建闹钟并管理闹钟的生命周期。
- 0:32 - 概览
闹钟是一种定时提醒,会打破静音模式、显示自定标题和 App 名称,还可以显示倒计时 UI。 用户可以选择停止、稍后提醒,也可以与自定按钮互动。闹钟可以显示在锁定屏幕、灵动岛、待机界面和 Apple Watch 上,用户必须为各个 App 分别启用。
- 1:39 - 授权
要在 App 中启用定时闹钟,必须获得用户授权。这可以在创建第一个闹钟时自动完成,也可以通过 AlarmManager 的“requestAuthorization”API 手动完成。 必须将“NSAlarmKitUsageDescription”添加到 Info.plist 中,说明闹钟的使用方式。用户可以在“设置”中更改授权状态。 在设定闹钟之前,App 可以检查授权状态;如果遭到拒绝,则应该告知用户不会设定闹钟。
- 3:06 - 创建
创建闹钟涉及几项关键组成部分。首先,闹钟可以设置倒计时持续时间,并且可以设置在响起前后分别有多长时间的倒计时。设定倒计时闹钟后,会有一个显示响起前倒计时持续时间的倒计时 UI。过了这段时间后,闹钟将响起,显示自定的提醒 UI。如果让闹钟稍后提醒,倒计时 UI 会再次显示响过后的倒计时,并在结束后再次响起。 闹钟可以使用固定或相对时间表来设定。固定时间表指定未来的某个日期和时间,而相对时间表则指定一天中的某个时间,并可让它每周重复,确保闹钟根据时区变化正确调整。 除了设定时间之外,还可以自定闹钟的外观。这包括创建和配置闹钟按钮,例如“关闭”按钮,以及设置闹钟标题。对于具有倒计时功能的闹钟,还可以添加“重复”按钮。然后定义提醒画面和属性,以指定闹钟响起时的显示画面和行为。 如果闹钟包含倒计时功能,则必须实现实时活动,以便在锁定屏幕、灵动岛和待机界面上显示倒计时 UI。这就要为倒计时创建自定界面,并使用相应的闹钟属性设置“ActivityConfiguration”。实时活动可以根据倒计时是正在进行还是已暂停来显示不同的视图,从而在各种设备状态下提供无缝的用户体验。 这个示例引入了自定元数据,以向实时活动传递额外信息。这些元数据包括一个自定枚举,让闹钟能够在倒计时期间根据枚举的值在锁定屏幕和灵动岛中显示一个图标。 这个讲座介绍了如何创建自定 App Intents,以便在用户轻点按钮 (例如打开 App) 时运行特定代码。此外还介绍了如何配置闹钟声音,使用户能够选择自定声音或使用默认的系统声音。
- 16:32 - 生命周期
通过 AlarmKit,你可以使用“AlarmManager”类创建、设定和管理闹钟。你可以配置闹钟、通过唯一标识符进行跟踪,以及让闹钟进入不同的状态 (倒计时、暂停、停止等)。最佳实践包括:将闹钟用于倒计时和重复提醒;确保提醒画面一目了然;以及在倒计时实时活动中包含所有基本信息和操作。