WWDC Quick Look 💓 By SwiftGGTeam
What's new in HealthKit

What's new in HealthKit

观看原视频

Highlight

HealthKit 工程师 Karim 介绍了 iOS 16 和 watchOS 9 中 HealthKit 的四大更新:睡眠阶段追踪、Swift async 查询 API、多活动运动(如铁人三项)的增强表示、以及视力处方的数字存储。

核心内容

做健康应用时,麻烦常常出在数据不够细。睡眠应用只能知道用户“睡着了”,却难以区分 REM、核心睡眠和深度睡眠。运动应用能保存一场训练,却很难把铁人三项里的游泳、骑行、跑步和中间换项拆开分析。配镜应用还要让用户在纸质处方、照片和表单之间来回搬运信息。

iOS 16 和 watchOS 9 把这些问题放进 HealthKit 的数据模型里。Apple Watch 自动记录睡眠阶段,HealthKit 查询支持 Swift async,Workout API 用 HKWorkoutActivity 表达一场运动里的多个活动,视力处方可以作为 HealthKit sample 保存,并能附带静态图片或 PDF。

这些变化的价值在于:应用少维护一套私有格式,多复用系统级健康数据。睡眠阶段、训练分段、心率恢复、眼镜和隐形眼镜处方都走 HealthKit 权限模型。用户仍然控制授权范围,开发者拿到的是更接近真实场景的数据结构。

睡眠:从“睡着”到具体阶段

过去读取睡眠数据时,.asleep 足够表达用户在睡眠中。现在 Apple Watch 会把睡眠分成 REM、核心和深度三个阶段。HealthKit 对应新增 .asleepCore.asleepDeep.asleepREM,旧的 .asleep.asleepUnspecified 取代,用来表示没有指定阶段的睡眠。

保存数据时,连续的一段同类睡眠阶段要创建一个独立的 sleepAnalysis category sample。读取所有睡眠阶段时,旧代码如果只查 .asleep,会漏掉新的阶段值,需要改用 .allAsleepValues

运动:一次 workout 可以有多个活动

铁人三项不是一段连续的“运动类型”。它有游泳、换项、骑行、换项、跑步。HealthKit 新增的 HKWorkoutActivity 正是为这种时间线准备的。每个 activity 有自己的 workout configuration、事件和统计数据。activity 之间不能重叠,但不要求首尾相接,换项时间可以用 .transition 单独记录。

在 Apple Watch 上,HKWorkoutSession 负责实时追踪,关联的 workout builder 负责把数据写入 HealthKit。每次进入新的 activity,应用调用 beginNewActivity,并更新 builder data source,只采集当前活动需要的数据。比如游泳阶段采集游泳距离,换项阶段关闭这类数据。

处方:HealthKit 保存视力信息和附件

眼镜或隐形眼镜处方容易丢,也常常包含姓名、生日等敏感信息。iOS 16 允许应用把视力处方保存为 HealthKit sample:眼镜使用 HKGlassesPrescription,隐形眼镜使用 HKContactsPrescription。处方的开始日期对应签发日期,结束日期对应过期日期。

如果用户手上有纸质处方,应用可以用 HKAttachmentStore 给处方 sample 附加静态图片或 PDF。读取权限按单个处方对象授权,用户可以选择给应用开放哪几张处方,并随时修改选择。

详细内容

睡眠阶段的读写

01:33)Apple Watch 会自动追踪睡眠阶段,并把数据保存到 HealthKit。应用也可以读取和保存这些阶段。演讲明确列出三个阶段值:.asleepCore.asleepDeep.asleepREM

下面是根据 transcript 整理的调用顺序示意,不是 session 提供的完整代码。

1. 选择要读取的睡眠阶段,例如 asleepREM。
2. 用 predicateForSamples 创建睡眠阶段 predicate。
3. 把 sleepAnalysis sample type 和阶段 predicate 组合成查询条件。
4. 用 Swift async 的 result(for:) 执行查询,得到 REM 阶段的 sleep samples。

关键点:

  • asleepREM 选择 REM 阶段。
  • predicateForSamples 是演讲提到的新 predicate,用来按睡眠阶段过滤 sample。
  • sleepAnalysis 是 HealthKit 中保存睡眠 category sample 的 identifier。
  • result(for:) 是 Swift async 查询入口,返回当前匹配的睡眠 sample。

04:13)如果应用想读取所有睡眠阶段,查询条件要覆盖新的阶段值和未指定阶段。演讲要求使用 .allAsleepValues

下面是根据 transcript 整理的调用顺序示意,不是 session 提供的完整代码。

1. 用 allAsleepValues 表示所有睡眠阶段和 unspecified。
2. 用 predicateForSamples 创建覆盖全部 asleep values 的 predicate。
3. 用 Swift async 查询 sleepAnalysis samples。
4. 按 sample value 区分 REM、核心、深度和 unspecified。

关键点:

  • .allAsleepValues 包含 REM、核心、深度和 unspecified。
  • 旧的 .asleep 已经被弃用,继续使用会漏掉阶段数据。
  • 每个连续阶段保存为单独 sample,读取结果天然是一段一段的时间区间。

Swift async 查询 HealthKit

04:36)HealthKit 查询从闭包回调迁移到 query descriptor。HKStatisticsCollectionQuery 对应 HKStatisticsCollectionQueryDescriptor。只要当前结果时,调用 result(for:);想继续监听更新时,调用 results(for:),它返回 AsyncSequence

下面是根据 transcript 整理的调用顺序示意,不是 session 提供的完整代码。

1. 创建 statistics collection query descriptor。
2. predicate 匹配卡路里 samples。
3. options 使用 cumulativeSum,统计总量。
4. anchorDate 选择 thisSunday,interval 设置为一周。
5. 调用 result(for:) 获取当前 statistics collection。
6. 如果需要持续更新,遍历 results(for:) 返回的 AsyncSequence。
7. 不再监听时跳出循环,查询停止。

关键点:

  • HKStatisticsCollectionQueryDescriptor 对应旧的 HKStatisticsCollectionQuery
  • .cumulativeSum 用来计算一段时间内的总量,演讲用它统计本周消耗卡路里。
  • anchorDateintervalComponents 决定统计区间,示例里是一周。
  • result(for:) 给一次性快照。
  • results(for:) 返回可遍历的更新流,跳出循环会停止查询。

多活动 workout 与分段统计

07:32)iOS 16 和 watchOS 9 的 Workout API 可以表达多活动训练。每个 HKWorkoutActivity 都有自己的 HKWorkoutConfiguration,并保存在 HKWorkout.workoutActivities

下面是根据 transcript 整理的调用顺序示意,不是 session 提供的完整代码。

1. 为游泳阶段准备 workout configuration,activity type 是 swimming。
2. 用该 configuration、start date、end date 和可选 metadata 创建 HKWorkoutActivity。
3. 把这个 activity 添加到 workout builder。
4. 对骑行、跑步或 transition 阶段重复同样流程。

关键点:

  • workout configuration 指定这一段的 activity type。
  • start date 和 end date 定义 activity 的时间范围。
  • activity 之间不能重叠,换项时间可以留出空档。
  • addWorkoutActivity 把分段写入 workout builder,最终随 HKWorkout 保存。

10:21)Apple Watch 上的实时记录流程是:创建 swimBikeRun 类型的 workout session,开始 session 和 builder,然后在每段活动开始时调用 beginNewActivity

下面是根据 transcript 整理的调用顺序示意,不是 session 提供的完整代码。

1. 用 swimBikeRun workout configuration 创建 HKWorkoutSession。
2. 在 workout 开始时,启动 session 的 activity。
3. 同时让关联的 workout builder 开始 collection。
4. 游泳开始时,调用 beginNewActivity,并传入 swimming configuration 和开始时间。
5. 更新 builder data source,只采集游泳距离等当前阶段需要的数据。
6. 游泳结束时,调用 endCurrentActivity。
7. 如果要分析换项时间,立即开始 transition activity,并关闭游泳距离采集。
8. 对骑行和跑步阶段重复切换流程。
9. workout 结束时结束 session,再 finish workout builder 保存 HKWorkout。

关键点:

  • startActivitybeginCollection 标记整场 workout 开始。
  • beginNewActivity 切换到新的活动段。
  • 每段开始时更新 builder data source,只采集当前活动相关的数据。
  • endCurrentActivity 结束当前活动;整场 workout 结束时,session 会结束仍在运行的 activity。

12:29totalEnergyBurnedtotalDistancetotalSwimmingStrokeCount 这类固定属性不再适合所有 workout。HealthKit 改为提供一个按 quantity type 返回 statistics 的方法,HKWorkoutHKWorkoutActivity 都能用。

下面是根据 transcript 整理的调用顺序示意,不是 session 提供的完整代码。

1. 选择要读取的 quantity type,例如 distance 或 heart rate。
2. 在 HKWorkout 上请求该 quantity type 的 statistics,得到整场 workout 的统计。
3. 在某个 HKWorkoutActivity 上请求同一 quantity type 的 statistics,得到该阶段的统计。
4. 用分段 statistics 驱动分析或可视化。

关键点:

  • statistics(for:) 按 quantity type 读取统计数据。
  • HKWorkout 上调用时,范围是整场 workout。
  • HKWorkoutActivity 上调用时,范围缩小到某个活动段。
  • 这些 statistics 来自 workout builder 或 live workout builder 收集的 samples。

心率恢复和新运动指标

15:14)Apple Watch Series 6、SE 和更新机型会自动采集新的跑步指标,比如 stride length 和 power。游泳 workout 增加 SWOLF 分数,按 lap event 和 segment event 计算。

16:23)iOS 16 新增 Cardio Recovery 数据类型,HealthKit identifier 是 .heartRateRecoveryOneMinute。如果使用 HKLiveWorkoutBuilder,心率恢复 sample 和上下文会在 workout 结束后自动保存。

下面是根据 transcript 整理的调用顺序示意,不是 session 提供的完整代码。

1. 选择 heartRateRecoveryOneMinute quantity type。
2. 记录运动后一分钟心率下降值;演讲示例是 50 beats per minute。
3. 设置 sample 的 start date 和 end date。
4. 在 metadata 中加入 recovery test type,示例为 maxExercise。
5. 在 metadata 中加入 activity type,示例为 swimBikeRun。
6. 在 metadata 中加入 workout duration 和 maximum heart rate,演讲示例最高心率为 184。
7. 保存 heart rate recovery quantity sample。

关键点:

  • .heartRateRecoveryOneMinute 表示运动后一分钟心率下降值。
  • 示例里的 50 来自演讲案例:运动后一分鐘心率下降 50 次。
  • metadata 保存测试类型、运动类型、运动时长和最高心率。
  • .maxExercise 表示这次恢复测试来自全力运动。

视力处方与附件授权

19:31)视力处方在 HealthKit 中是 visionPrescriptionType sample。眼镜处方用 HKGlassesPrescription,隐形眼镜处方用 HKContactsPrescription。每个处方由左右眼 lens specification 组成。

下面是根据 transcript 整理的调用顺序示意,不是 session 提供的完整代码。

1. 为左眼创建 glasses lens specification。
2. 为右眼创建 glasses lens specification。
3. 用左右眼 specification 创建 glasses prescription sample。
4. prescription 的 start date 对应签发日期。
5. prescription 的 end date 对应过期日期。
6. 如果是阅读眼镜,把用途写入 description。
7. 调用 HealthKit 保存 prescription sample。

关键点:

  • HKGlassesLensSpecification 保存单只眼睛的镜片参数。
  • HKGlassesPrescription 把左右眼规格组合成一张眼镜处方。
  • start date 对应处方签发日期,end date 对应过期日期。
  • description 可记录“reading glasses”这类用途说明。

21:25)处方 sample 可以附带静态图片或 PDF。附件由 HKAttachment 表示,用 HKAttachmentStore 保存和读取。

下面是根据 transcript 整理的调用顺序示意,不是 session 提供的完整代码。

1. 用 healthStore 创建 HKAttachmentStore。
2. 选择已经保存的 prescription sample。
3. 设置 attachment name,例如 Prescription.png。
4. 传入静态图片或 PDF 文件的 URL。
5. 把 attachment 添加到 prescription sample。

关键点:

  • HKAttachmentStore 需要用同一个 healthStore 创建。
  • addAttachment(to:) 把文件挂到已经保存的 prescription sample 上。
  • HealthKit 只允许静态图片或 PDF 附加到处方。
  • 处方附件可能包含姓名、生日等敏感信息,读取权限按单个处方对象授权。

23:07)请求读取处方时,应用调用 requestPerObjectReadAuthorization。系统会显示匹配 predicate 的处方列表,用户选择允许应用读取的对象。

下面是根据 transcript 整理的调用顺序示意,不是 session 提供的完整代码。

1. 准备 vision prescription type。
2. 如果只需要部分处方,先准备 predicate。
3. 调用 requestPerObjectReadAuthorization,请求读取匹配的 prescription objects。
4. 系统显示处方列表,用户选择允许应用读取哪些对象。
5. 用 HealthKit query 读取应用已经获得授权的处方。

关键点:

  • 授权范围是每个 prescription object,而非整个处方类型。
  • predicate 可把授权请求限制在当前任务相关的处方范围内。
  • 授权提示总会显示,适合放在用户明确要导入或选择处方的上下文中。
  • 查询只能返回应用已获得读取权限的处方。

核心启发

1. 睡眠阶段趋势卡片

  • 做什么:为用户展示最近 7 天 REM、核心、深度睡眠的时长变化。
  • 为什么值得做:watchOS 9 会把睡眠阶段保存到 HealthKit,应用不需要自己推断阶段。
  • 怎么开始:用 sleepAnalysis 查询 .allAsleepValues,按 sample value 分组,再用 Swift Charts 画堆叠柱状图。

2. 铁人三项复盘页面

  • 做什么:把一次 swim-bike-run workout 拆成游泳、换项、骑行、跑步几段,分别显示配速、距离、心率。
  • 为什么值得做HKWorkoutActivity 让一场 workout 自带多段结构,分段 statistics 可直接来自 HealthKit。
  • 怎么开始:读取 HKWorkout.workoutActivities,对每个 activity 调用 statistics(for:),把 .transition 段单独标记。

3. 间歇跑训练分析

  • 做什么:为跑步 workout 显示每个 interval 的平均心率、距离和恢复情况。
  • 为什么值得做:演讲建议用同一 activity type 的多个 HKWorkoutActivity 表达 interval workout。
  • 怎么开始:把 running workout 的 activities 作为 interval 列表,读取每段的心率和距离 statistics,再关联 .heartRateRecoveryOneMinute

4. 视力处方保险箱

  • 做什么:让用户保存眼镜和隐形眼镜处方,并附加诊所给的图片或 PDF。
  • 为什么值得做:iOS 16 支持 HKGlassesPrescriptionHKContactsPrescription 和处方附件。
  • 怎么开始:用左右眼 lens specification 创建 prescription sample,保存后用 HKAttachmentStore 添加图片或 PDF。

5. 处方按需共享流程

  • 做什么:在用户购买眼镜时,只请求读取当前要使用的那张处方。
  • 为什么值得做:HealthKit 的处方读取授权是 per-object,能避免一次性读取全部处方。
  • 怎么开始:在“选择处方”按钮之后调用 requestPerObjectReadAuthorization,用 predicate 限制候选处方,再展示授权后的查询结果。

关联 Session

评论

GitHub Issues · utterances