App Services 进阶 0m
在 iOS 和 iPadOS 上用 HealthKit 追踪锻炼
Track workouts with HealthKit on iOS and iPadOS
2025年6月9日
一句话判断
HealthKit 的 Workout API 终于完整支持 iPhone 和 iPad 了——和 Apple Watch 几乎一样的代码,加上锁屏 Live Activity 和 Siri 控制,甚至还有 crash recovery。
这场 Session 讲了什么
如果你之前只在 Apple Watch 上做锻炼追踪,现在可以把同样的代码搬到 iPhone/iPad 上了。核心流程完全一致:
- 创建
HKWorkoutConfiguration(类型 + 位置) - 创建
HKWorkoutSession,获取 builder,设置 data source prepare()后 3 秒倒计时,让传感器就绪startActivity()+beginCollection()- 通过 builder delegate 获取实时指标
stopActivity()->endCollection()->finishWorkout()->end()
和 Watch 的关键区别:
- 传感器差异:iPhone/iPad 没有心率传感器,需要配对 BLE 心率带(如 Powerbeats Pro 2)。系统会自动处理心率数据。
- Generated vs Collected types:Generated types 是系统在锻炼期间自动产生的(卡路里、距离);Collected types 是你想观察并添加到锻炼样本的数据(比如锻炼期间喝的水)。可以用
enableCollection(for:)/disableCollection(for:)控制。 - 锁屏行为:iPhone 很可能在锻炼时锁屏。首次启动锻炼时系统会提示”即使锁屏也可访问锻炼数据”。你的 app 应该用 Live Activity 在锁屏显示关键指标。
- Siri 锁屏控制:新增 Siri Intent 支持在锁屏上启动、暂停、恢复、取消锻炼,不需要解锁。
- Crash Recovery:系统自动重启崩溃的 app,恢复 workout session 和 builder 状态,但你需要重建 data source。新增 scene delegate 处理恢复流程。
值得深挖的点
-
Builder delegate 是更新 UI 的正确方式。不需要 anchored object query,delegate 会在新数据到达时自动回调,而且自动处理 workout 保存时的指标同步。
-
锁屏隐私提示的 UX 影响。用户可以选择拒绝锁屏数据访问。如果你的 app 依赖心率,但设备锁屏且用户拒绝了访问,你应该优雅降级(比如只显示锻炼时长)。
-
Siri Intent 必须在 app 内处理,不能在 extension 里。这意味着锁屏 Siri 命令会唤醒你的主 app。
-
Crash Recovery 只需要重建 data source。Session 和 builder 状态由系统恢复,你只需要在 scene delegate 里重新关联 data source。
代码片段
完整的 workout session 生命周期:
let configuration = HKWorkoutConfiguration()
configuration.activityType = .running
configuration.locationType = .outdoor
let session = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
let builder = session.associatedWorkoutBuilder()
// 设置 data source
let device = HKDevice.local()
let dataSource = HKLiveWorkoutDataSource(healthStore: healthStore, workoutConfiguration: configuration)
builder.dataSource = dataSource
// 准备 + 开始
session.prepare()
// 3 秒倒计时...
session.startActivity()
try await builder.beginCollection(at: .now)
// 锁屏 Siri Intent 处理
class WorkoutIntentHandler: NSObject, StartWorkoutIntentHandling {
func handle(intent: StartWorkoutIntent) async -> StartWorkoutIntentResponse {
if workoutManager.isActive {
return .failure(failureReason: "已有锻炼在进行")
}
let config = HKWorkoutConfiguration()
config.activityType = .running
config.locationType = .outdoor
workoutManager.startWorkout(configuration: config)
return .success()
}
}
Crash Recovery:
func application(_ application: UIApplication,
shouldRestoreSecureApplicationState coder: NSCoder) -> Bool {
return coder.decodeBool(forKey: "shouldHandleActiveWorkoutRecovery")
}
// Scene delegate 中恢复
let recoveredSession = try await healthStore.recoveredWorkoutSession()
workoutManager.recoverSession(recoveredSession)
// 只需重建 dataSource
最佳实践
- 优先从 Watch 启动锻炼(如果用户有 Watch),用
healthStore.startWatchApp()启动,然后 mirror 到 iPhone。这样能获取心率等全部指标。 - 只请求你需要的数据类型授权。不要请求和锻炼无关的 HealthKit 权限。
- 始终用 Workout Builder API 创建和保存 workout。这是确保 Activity Rings 正确更新的唯一方式。
- 锻炼期间用 Live Activity 在锁屏显示关键指标。用 ActivityKit 配合 builder delegate 的实时数据。
- 处理锁屏隐私场景。检查是否有数据访问权限,没有则优雅降级 UI。
- Siri Intent handler 要处理”已有锻炼在进行”的情况,返回有意义的错误。
还有什么值得关注
- WWDC23 的 “Build a multi-device workout app” 讲了 Watch + iPhone 的 mirror 机制。
- WWDC24 的 “Bring your app’s core features to users with App Intents” 补充了 Siri Intent 的完整实现。
- WWDC23 的 “Meet ActivityKit” 是 Live Activity 的基础。
- HKQuantitySample 可能有 count > 1 的情况(更细粒度的数据),用
HKQuantitySeriesSampleQuery读取。 - Session 附带完整的 demo app,可以直接下载参考。
应用服务 Health & Fitness