Highlight
Apple 在 iOS 17、watchOS 10 和 iPadOS 17 中为 HealthKit 加入了三项核心能力:Apple Watch 与 iPhone 之间的实时运动镜像会话、骑行专用数据类型(踏频、功率、速度、FTP)、以及 iPad 端 HealthKit 支持,开发者可以用一套 API 构建覆盖三端设备的运动 App。
核心内容
问题:运动 App 只能活在手表上
如果你开发过 Apple Watch 运动 App,你一定经历过这个场景:用户在户外骑车,手表绑在手腕上,想看骑行数据得低头抬腕——骑行时这个动作既不安全也不方便。更大的屏幕(比如固定在车把上的 iPhone 或室内骑行架前的 iPad)放着不用,因为 HealthKit 的实时数据只能在手表上获取。
更麻烦的是,骑行产生的数据类型远不止心率和卡路里。踏频(每分钟踏板转数)、功率(瓦特输出)、速度——这些数据在 watchOS 9 之前,HealthKit 没有直接支持。开发者要么放弃这些指标,要么自己对接蓝牙传感器、手动解析数据、再自己想办法存回 HealthKit。
蓝牙传感器的兼容性是另一个坑。每个品牌的数据格式和配对流程都不同,用户需要单独购买、配对、校准。对开发者来说,这意味着维护一套与健身无关的蓝牙通信代码。
Apple 的方案:三端联动 + 骑行原生支持
在 WWDC 2023,Apple 一次性解决了这些问题,给出了三个关键能力:
- 镜像运动会话(Mirrored Workout Session):手表上的实时运动数据可以镜像到 iPhone,状态自动同步,iPhone 端可以展示更丰富的数据面板。
- 骑行专用数据类型:HealthKit 直接支持踏频、功率、速度、FTP(功能性阈值功率),手表可以自动从蓝牙传感器采集这些数据并存入 HealthKit。
- iPad 端 HealthKit:HealthKit 和 Health App 登陆 iPad,通过 iCloud 同步健康数据,iPad 可以作为大屏运动数据面板。
这三个能力互相配合:手表负责采集数据,iPhone 做实时镜像显示,iPad 做大屏回顾和分析。一次骑行产生的数据,在所有设备上都可用。
详细内容
镜像运动会话:手表采集,手机显示
这是本次更新中最重要的新 API。它的工作方式是这样的:手表上运行一个 HKWorkoutSession 作为主会话(Primary Session),调用 startMirroringToCompanionDevice() 后,配对的 iPhone 会自动启动配套 App,接收一个镜像会话(Mirrored Session)(04:15)。
镜像会话会同步主会话的所有状态变更——开始、暂停、恢复、结束——开发者不需要手动管理两边的状态。
在手表端发起镜像:
// Apple Watch — 启动运动并开启镜像
let configuration = HKWorkoutConfiguration()
configuration.activityType = .cycling
configuration.locationType = .outdoor
let session = HKWorkoutSession(
healthStore: healthStore,
configuration: configuration
)
let builder = session.associatedWorkoutBuilder()
builder.dataSource = HKLiveWorkoutDataSource(
healthStore: healthStore,
workoutConfiguration: configuration
)
// 开始运动
session.startActivity(with: Date())
builder.beginCollection(withStart: Date()) { success, error in
// 数据采集已开始
}
// 开启向 iPhone 的镜像
try await healthStore.startMirroringToCompanionDevice(session)
关键点:
HKWorkoutConfiguration定义运动类型和场地(户外/室内),影响系统如何采集 GPS 和高度数据。HKLiveWorkoutDataSource是手表端实时数据的入口,它自动从手表传感器和已配对的蓝牙设备收集数据。startMirroringToCompanionDevice()只能在手表端调用,要求配套 iPhone App 已安装且同一 iCloud 账号。- 手表 App 必须保持在前台,镜像才会持续活跃。
在 iPhone 端,通过锚点查询接收实时数据:
// iPhone — 接收镜像数据
class MirroringManager: ObservableObject {
let healthStore = HKHealthStore()
@Published var heartRate: Double = 0
func observeMirroredData() {
let heartRateType = HKObjectType.quantityType(forIdentifier: .heartRate)!
let query = HKAnchoredObjectQuery(
type: heartRateType,
predicate: nil,
anchor: nil,
limit: HKObjectQueryNoLimit
) { _, samples, _, _, _ in
guard let quantitySamples = samples as? [HKQuantitySample] else { return }
for sample in quantitySamples {
let bpm = sample.quantity.doubleValue(for: HKUnit(from: "count/min"))
DispatchQueue.main.async {
self.heartRate = bpm
}
}
}
healthStore.execute(query)
}
}
关键点:
HKAnchoredObjectQuery会在新数据到达时自动回调,不需要轮询。- 镜像会话启动后,iPhone App 会在后台被拉起,有约 10 秒的时间来处理会话设置。
- 所有 HealthKit 查询结果在后台线程返回,更新 UI 前必须切换到主线程。
手表和 iPhone 之间还可以通过 sendData 互相发送自定义数据(06:30):
// 手表向 iPhone 发送自定义事件
let lapData = "lapCompleted".data(using: .utf8)!
try await healthStore.sendData(lapData, toRemoteWorkoutSession: session)
// iPhone 端接收 — 通过 HKWorkoutSessionDelegate
func workoutSession(_ workoutSession: HKWorkoutSession,
didReceiveDataFromRemoteWorkoutSession data: [Data]) {
for item in data {
if let message = String(data: item, encoding: .utf8) {
print("收到手表消息: \(message)")
}
}
}
关键点:
sendData支持在两端之间传递任意二进制数据,适合发送自定义事件(圈数标记、区间切换、用户提示)。- 接收端通过
HKWorkoutSessionDelegate的didReceiveDataFromRemoteWorkoutSession回调获取数据。 - 这不是高频数据传输通道,适合少量事件级数据,不适合传输连续的传感器数据流。
骑行数据类型:不再需要自己对接传感器
watchOS 10 中,HealthKit 新增了四个骑行专用数据类型(08:20):
| 数据类型 | HealthKit Identifier | 单位 |
|---|---|---|
| 踏频 | cyclingCadence | 转/分钟 (count/min) |
| 功率 | cyclingPower | 瓦特 (W) |
| 速度 | cyclingSpeed | 米/秒 (m/s) |
| FTP | cyclingFunctionalThresholdPower | 瓦特 (W) |
Apple Watch 可以直接连接蓝牙骑行传感器(功率计、踏频传感器),自动采集数据并写入 HealthKit。开发者不需要写一行蓝牙代码。
配置数据源时,启用对应的数据类型即可:
let dataSource = HKLiveWorkoutDataSource(
healthStore: healthStore,
workoutConfiguration: configuration
)
// 启用踏频采集
dataSource.enableCollection(
for: HKQuantityType(.cyclingCadence),
predicate: nil
)
// 启用功率采集
dataSource.enableCollection(
for: HKQuantityType(.cyclingPower),
predicate: nil
)
// 启用速度采集
dataSource.enableCollection(
for: HKQuantityType(.cyclingSpeed),
predicate: nil
)
关键点:
enableCollection告诉HKLiveWorkoutDataSource开始采集指定类型的数据。如果不调用,即使传感器已连接,数据也不会被记录。- 手表会自动处理蓝牙传感器的配对和数据读取,配对流程在系统设置中完成,不需要 App 介入。
- 这些数据类型在请求授权时需要加入
typesToRead集合。
实时处理骑行数据——通过 HKWorkoutBuilder 的 addSamplesHandler(10:45):
builder.addSamplesHandler = { [weak self] samples in
for sample in samples {
switch sample.sampleType {
case HKQuantityType(.cyclingCadence):
if let quantity = (sample as? HKQuantitySample)?.quantity {
let rpm = quantity.doubleValue(for: HKUnit(from: "count/min"))
self?.cadence = Int(rpm)
}
case HKQuantityType(.cyclingPower):
if let quantity = (sample as? HKQuantitySample)?.quantity {
let watts = quantity.doubleValue(for: .watt())
self?.power = Int(watts)
}
case HKQuantityType(.cyclingSpeed):
if let quantity = (sample as? HKQuantitySample)?.quantity {
let mps = quantity.doubleValue(for: HKUnit.meter().unitDivided(by: .second()))
self?.speed = mps
}
default:
break
}
}
}
关键点:
addSamplesHandler在每次有新样本时回调,适合实时 UI 更新。- 每个
HKQuantitySample包含数值和时间戳,可以用doubleValue(for:)转换为需要的单位。 - FTP 由 HealthKit 根据多次骑行数据自动估算,不需要用户手动做 20 分钟全力骑测试。
[weak self]捕获列表避免循环引用——builder持有 handler,handler 不应强引用持有 builder 的对象。
HealthKit on iPad:大屏健康数据面板
iPadOS 17 首次引入了 HealthKit 支持和 Health App。虽然 iPad 没有心率传感器和 GPS,但它可以通过 iCloud 同步获取其他设备上的健康数据(14:10)。
iPad 上请求 HealthKit 授权的方式与 iPhone 不同,需要使用 HealthKitUI 框架:
import HealthKitUI
import SwiftUI
struct WorkoutView: View {
let healthStore = HKHealthStore()
let typesToShare: Set<HKSampleType> = [.workoutType()]
let typesToRead: Set<HKObjectType> = [
HKObjectType.quantityType(forIdentifier: .cyclingCadence)!,
HKObjectType.quantityType(forIdentifier: .cyclingPower)!,
HKObjectType.quantityType(forIdentifier: .cyclingSpeed)!,
]
var body: some View {
VStack {
// 运动数据展示 UI
}
.healthDataAccessRequest(
store: healthStore,
shareTypes: typesToShare,
readTypes: typesToRead,
trigger: true,
completion: { result in
switch result {
case .success:
print("授权成功")
case .failure(let error):
print("授权失败: \(error)")
}
}
)
}
}
关键点:
- iPad 上 HealthKit 数据来自 iCloud 同步,不是实时采集,适合做运动后的回顾和分析。
- 授权 UI 必须使用
HealthKitUI框架的healthDataAccessRequest(SwiftUI)或HKHealthStore的authorizationViewControllerPresenter(UIKit)。 - 在 iPad 上需要处理多窗口场景——用户可能从不同窗口触发授权请求。
HKHealthStore.isHealthDataAvailable()在 iPadOS 17 之前的 iPad 上返回false,需要做版本检查。
在 iPad 上查询历史骑行数据:
let predicate = HKQuery.predicateForWorkouts(with: .cycling)
let sortDescriptor = NSSortDescriptor(
key: HKSampleSortIdentifierStartDate,
ascending: false
)
let query = HKSampleQuery(
sampleType: .workoutType(),
predicate: predicate,
limit: 1,
sortDescriptors: [sortDescriptor]
) { _, samples, error in
guard let workout = samples?.first as? HKWorkout else { return }
let distance = workout.totalDistance?.doubleValue(
for: HKUnit.meterUnit(with: .kilo)
)
let energy = workout.totalEnergyBurned?.doubleValue(
for: .kilocalorie()
)
DispatchQueue.main.async {
self.distance = distance
self.calories = energy
}
}
healthStore.execute(query)
关键点:
predicateForWorkouts(with:)按运动类型过滤,返回所有该类型的 workout。HKWorkout自带totalDistance和totalEnergyBurned属性,不需要额外查询。- 要获取踏频、功率等详细指标,需要用
HKStatisticsQuery按 workout 的 UUID 过滤查询。
核心启发
1. 做一个车把手机仪表盘
做什么:iPhone App 作为骑行时的实时仪表盘,通过镜像会话从手表获取踏频、功率、速度、心率,以大字体和全屏模式显示在固定在车把上的 iPhone 屏幕上。
为什么值得做:startMirroringToCompanionDevice() 让 iPhone 可以零延迟获取手表传感器数据。配合 HKLiveWorkoutDataSource 的 enableCollection,所有骑行指标自动到位,不需要自己写任何传感器对接代码。
怎么开始:手表端创建 .cycling 类型的 HKWorkoutSession,调用 startMirroringToCompanionDevice()。iPhone 端用 HKAnchoredObjectQuery 监听实时数据,SwiftUI 用大号 Text 和 Gauge 组件展示各项指标。
2. 做一个室内骑行 iPad 训练面板
做什么:iPad App 展示用户最近的骑行数据趋势,包括 FTP 变化曲线、功率区间分布、踏频稳定性分析,配合 Apple Watch 实时采集的数据做训练指导。
为什么值得做:iPad 大屏天然适合展示复杂数据。HealthKit 在 iPadOS 17 上可用后,所有手表采集的骑行数据通过 iCloud 自动同步到 iPad。FTP 由系统自动估算,用户不需要手动输入。
怎么开始:在 iPad 上用 HKSampleQuery 查询历史 .cycling 类型的 workout,提取 cyclingPower、cyclingCadence、cyclingFunctionalThresholdPower 等指标。用 Swift Charts 绘制功率曲线和 FTP 变化趋势。
3. 做一个骑行社交 App 的数据采集层
做什么:利用 HealthKit 骑行数据作为社交 App 的数据来源,自动记录每次骑行的路线、功率、速度,不需要用户手动输入或依赖第三方传感器 App。
为什么值得做:之前骑行 App 要么自己做传感器对接,要么依赖 Strava 等第三方数据。现在 HealthKit 直接提供踏频、功率、速度,开发者可以聚焦在社交功能上,数据采集层完全交给系统。
怎么开始:请求 .cyclingCadence、.cyclingPower、.cyclingSpeed 的读权限,用 HKStatisticsCollectionQuery 按天/周聚合数据。骑行路线通过 HKWorkoutRouteBuilder 关联 GPS 数据。注意用户隐私:只在用户明确授权后才读取数据。
4. 做一个 FTP 自动追踪工具
做什么:利用 HealthKit 自动估算的 FTP 值,追踪用户骑行能力的长期变化,在 FTP 提升时给出提醒和激励。
为什么值得做:FTP 是衡量骑行能力最核心的指标,但传统方式需要用户定期做 20 分钟全力骑测试——痛苦且不准确。HealthKit 根据日常骑行数据自动估算 FTP,数据更真实、更持续。
怎么开始:用 HKAnchoredObjectQuery 监听 cyclingFunctionalThresholdPower 的变化,记录每次 FTP 更新的时间点和数值。用 Swift Charts 绘制长期趋势线。在 FTP 突破新值时发送本地通知。
关联 Session
- Meet watchOS 10 — watchOS 10 的整体更新,包括新的设计语言和 Smart Stack,是理解本 session 中手表端 API 的前提。
- Update your app for watchOS 10 — 将现有 watchOS App 迁移到 watchOS 10 的实践指南,涵盖 SwiftUI 新组件和布局模式。
- Platforms State of the Union — WWDC 2023 平台技术总览,了解 HealthKit 更新在各平台生态中的位置。
评论
GitHub Issues · utterances