HealthKit 新特性
What's new in HealthKit
2025年6月11日
一句话判断
HealthKitUI 的推出比新增的情绪数据类型影响更大——它宣告 Apple 认为健康数据可视化应该是系统级能力,不是每个 App 自己用 Charts 库画的活儿。
这场 Session 讲了什么
HealthKit 这次更新分两条线。第一条是数据层:新增了情绪(Mood)和运动恢复指数两类数据类型,把健康数据从”身体维度”拉到了”心理维度”。情绪数据用五级量表(veryPleasant 到 veryUnpleasant),这个粒度不是随便定的——心理学研究里太细的量表会让记录变成负担,太粗又丢信息,五级是个被验证过的平衡点。Apple Watch 的正念练习完成后会主动建议记录情绪,形成”被动采集 + 主动记录”的闭环。
第二条线是工具层,也是真正改变开发者工作方式的部分。HKStatisticsQueryDescriptor 把 HealthKit 查询从闭包回调拽进了 async/await 时代,配合 SwiftUI 的 .task 修饰符,视图消失时自动取消查询,不用手动管生命周期。更重头的是 HealthKitUI 框架,它提供 HealthChart 和 HealthSummaryCard 两个开箱即用的组件,你传入数据类型,它自动选图表样式——步数走柱状图、心率走折线图、活动量走环形图。
家庭共享方面,HKSharedSummary 允许在家庭组内安全同步健康摘要数据,但注意这里同步的是”摘要”不是原始数据,隐私边界守得比较紧。
值得深挖的点
HealthKitUI:Apple 在健康数据可视化上的一次”收编”
在 HealthKitUI 之前,健康类 App 的图表实现基本三条路:用 Swift Charts 自己画、接入第三方图表库、或者直接上 WebView 套 ECharts。三条路都有问题——Swift Charts 灵活但写起来繁琐,带阈值线的心率图、带颜色分区的睡眠图这种专业图表得写几十行;第三方库有维护风险和包体积成本;WebView 则完全脱离 SwiftUI 体系。
Apple 的解法很直接:既然健康数据的图表模式是可穷举的(步数就是柱状图、心率就是折线图、睡眠就是阶段图),那就别让每个开发者重复造轮子。HealthChart 接收一个 [HKStatistics] 数组和数据类型,自动完成图表选型、坐标轴适配、数据聚合。HealthSummaryCard 更进一步,直接面向 Widget 和 StandBy 场景,一个声明式调用就能在锁屏或桌面展示关键健康指标。
这个设计的 trade-off 也很明确:你获得了开发效率和系统一致性,但放弃了视觉自定义空间。如果你的 App 有强烈的品牌设计语言,HealthChart 可能不够用——目前没有暴露颜色、字体、标注的自定义接口。对于独立开发者和小团队来说,这笔交易非常划算;对于品牌感强的健康类大厂 App,可能还得继续用 Swift Charts 自己画。
查询 API 的 Swift 原生化:不只是语法糖
旧版 HKStatisticsQuery 基于闭包回调,多层嵌套后可读性差不说,还有个隐蔽的问题:它不支持 Swift Concurrency 的取消机制。也就是说,如果用户在查询完成前离开了页面,查询还在后台跑,既浪费资源又可能回调到已经不存在的视图上。
HKStatisticsQueryDescriptor 解决了这个问题。它是一个 struct 而不是 class,声明式构建查询条件,用 await descriptor.result(for: store) 获取结果。关键是它和 SwiftUI 的 .task 天然配合——task 取消时查询自动取消。对于需要同时获取多个统计维度的场景(比如同时要步数的总和、平均值和最大值),旧版需要多个 query 实例,新版的 options 参数一个描述符就搞定。
这不是”把回调包一层 async”那种表面迁移,而是把查询生命周期和 SwiftUI 视图生命周期绑定到一起了。写起来少了一半代码不是重点,重点是你再也不用操心”查询回调时视图还在不在”这个问题。
代码片段
// 用 HKStatisticsQueryDescriptor 查询今日步数
// 坑:options 里的 .cumulativeSum 和 .mostRecent 可以同时用,但 .discreteAverage 和 .discreteMax 不能和 .cumulativeSum 混用
let stepsType = HKQuantityType(.stepCount)
let predicate = HKQuery.predicateForSamples(
withStart: Calendar.current.startOfDay(for: .now),
end: .now
)
let descriptor = HKStatisticsQueryDescriptor(
predicate: .init(quantityType: stepsType, predicate: predicate),
options: [.cumulativeSum]
)
let stats = try await descriptor.result(for: store)
let steps = stats.sumQuantity()?.doubleValue(for: .count())
// 记录情绪日志,start 和 end 相同表示瞬时事件
// 坑:旧系统上 HKCategoryType(.mood) 不存在,调用前必须做 #available 检查
let moodType = HKCategoryType(.mood)
let sample = HKCategorySample(
type: moodType,
value: HKCategoryValueMoodSeverity.pleasant.rawValue,
start: Date(),
end: Date()
)
try await HKHealthStore().save(sample)
// HealthChart 展示本周步数趋势
// 坑:statistics 数组必须按日期升序排列,否则 X 轴会乱
import HealthKitUI
HealthChart(weeklySteps, type: .stepCount, style: .bar)
.frame(height: 200)
// HealthSummaryCard 适合 Widget 场景,一个声明搞定
HealthSummaryCard(type: .stepCount, style: .ring)
最佳实践
先把项目里所有的 HKStatisticsQuery 闭包调用列出来,按调用频率排优先级,从被调用最多的那个开始迁移到 HKStatisticsQueryDescriptor。这不是为了少写几行代码,而是为了消灭”视图消失后回调仍然触发”这一类潜在 crash。
情绪数据建议先观望。五级量表对于大多数健康管理场景够用,但如果是专业心理健康追踪,粒度可能不够。而且这个数据类型在旧系统上不存在,需要同时维护两套数据模型。等明年看看社区怎么用再决定也不迟。
HealthKitUI 的 HealthChart 直接用在内部工具、原型、Widget 这些对品牌要求不高的场景。如果主 App 的图表需要品牌定制,还是用 Swift Charts 自己画更可控。HealthSummaryCard 无脑用,没有理由不用。
HKSharedSummary 的家庭共享场景比较窄,除非产品明确涉及家庭健康管理,否则优先级放最后。
还有什么值得关注
- 运动恢复指数(Recovery Index)是配合 Apple Watch 新传感器的数据类型,适合健身 App 从”记录训练”升级到”指导恢复”,但数据源目前主要依赖 Apple Watch。
- HKAuthorizationRequest 新增了按时间段授权的能力,用户可以只授权最近三个月的数据而不是全部历史,这对隐私敏感型用户是个大加分。
- HealthKitUI 仅在 iOS 19+ 可用,没有向下兼容方案,如果你的 min deployment target 还在 iOS 17,这个框架暂时用不了。