Beyond counting steps
Health & Fitness 进阶 16m

超越计步:深入 HealthKit 运动数据

Beyond counting steps

2020年6月24日

在 Apple 官方观看视频

一句话判断

HealthKit 不只是存步数——iOS 14 新增了 Mobility(移动能力)数据类型和更精细的运动指标,配合 Apple Watch 的传感器数据,可以做真正的运动科学分析而不只是「今天走了多少步」。

这场 Session 讲了什么

这场 Session 聚焦于 HealthKit 在运动健康领域的深度应用,特别是 iOS 14 / watchOS 7 中新增的健康数据类型。

核心内容是 HealthKit 新增的 Mobility 数据类别,包含步速(Walking Speed)、步长(Step Length)、步行不对称性(Walking Asymmetry)、双支撑时间占比(Double Support Percentage)等精细指标。这些数据由 Apple Watch 的加速计和陀螺仪自动采集,无需 app 做任何传感器计算。

Session 还讲解了如何用 HKStatisticsCollectionQuery 查询长时间序列的运动数据、如何构建趋势图表、以及如何利用这些数据为用户提供有意义的健康洞察(比如「你的步速比上月下降了 5%,可能需要关注」)。

值得深挖的点

Mobility 指标的临床意义

步速(Walking Speed)是老年医学中评估整体健康状态的核心指标。研究表明步速低于 0.8m/s 与跌倒风险显著相关。Apple Watch 现在自动采集这个数据,你的 app 可以直接读取并做风险提示。步行不对称性(左右脚步长差异)则可以反映运动损伤或康复进展。这些数据以前需要专业设备在实验室里测,现在手表就能采集。

数据采集的自动化

这些 Mobility 数据是 Apple Watch 自动采集的,不需要 app 在后台运行或申请额外权限。你的 app 只需要请求 HealthKit 的读取权限(HKObjectType 对应的 sharing request),就可以读取历史数据。这意味着即使用户没有安装你的 app,Apple Watch 也在默默记录这些指标。

代码片段

import HealthKit

// 请求 HealthKit 权限并读取 Mobility 数据
class HealthDataManager {
    let healthStore = HKHealthStore()
    
    func requestAuthorization() {
        let typesToRead: Set<HKObjectType> = [
            HKObjectType.quantityType(forIdentifier: .walkingSpeed)!,
            HKObjectType.quantityType(forIdentifier: .stepLength)!,
            HKObjectType.quantityType(forIdentifier: .walkingAsymmetryPercentage)!,
            HKObjectType.quantityType(forIdentifier: .walkingDoubleSupportPercentage)!,
            HKObjectType.quantityType(forIdentifier: .stepCount)!
        ]
        
        healthStore.requestAuthorization(toShare: nil, read: typesToRead) { success, error in
            if success {
                self.fetchMobilityTrend()
            }
        }
    }
    
    func fetchMobilityTrend() {
        let speedType = HKQuantityType.quantityType(forIdentifier: .walkingSpeed)!
        
        // 查询最近 30 天的步速数据
        let startDate = Calendar.current.date(byAdding: .day, value: -30, to: Date())!
        let predicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: .strictStartDate)
        
        // 按天聚合,取平均值
        let query = HKStatisticsCollectionQuery(
            quantityType: speedType,
            quantitySamplePredicate: predicate,
            options: .discreteAverage,
            anchorDate: startDate,
            intervalComponents: DateComponents(day: 1)
        )
        
        query.initialResultsHandler = { _, results, error in
            guard let results = results else { return }
            
            results.enumerateStatistics(from: startDate, to: Date()) { statistics, _ in
                if let avg = statistics.averageQuantity() {
                    let speed = avg.doubleValue(for: HKUnit.meter().unitDivided(by: .second()))
                    print("\(statistics.startDate): 步速 \(String(format: "%.2f", speed)) m/s")
                }
            }
        }
        
        healthStore.execute(query)
    }
}
// 检测步速下降趋势并给出提示
func analyzeSpeedTrend() {
    let speedType = HKQuantityType.quantityType(forIdentifier: .walkingSpeed)!
    
    // 获取本月和上月的平均步速
    let calendar = Calendar.current
    let now = Date()
    let thisMonthStart = calendar.date(from: calendar.dateComponents([.year, .month], from: now))!
    let lastMonthStart = calendar.date(byAdding: .month, value: -1, to: thisMonthStart)!
    
    // 比较两个月的数据...
    // 如果本月步速比上月低超过 5%,提醒用户
    let threshold: Double = 0.05
    let warningSpeed: Double = 0.8  // 临床风险阈值
}

最佳实践

  1. HKStatisticsCollectionQuery 而不是逐条查询。HealthKit 中可能有成千上万条步速记录,按时间区间聚合后再分析,比把所有原始数据拉到内存再计算高效得多。
  2. 展示数据时给参考范围。单纯告诉用户「你的步速是 1.2m/s」没有意义。应该标注同年龄段平均水平(可以用「高于 70% 同龄人」这种形式),或者用绿/黄/红色标记风险等级。
  3. 注意数据稀疏性。用户不是每天都戴手表,有些天可能没有数据。查趋势时跳过无数据的天数,不要把缺失值当 0 处理。

还有什么值得关注

  • watchOS 7 新增了 HKWheelchairUse 对应的运动指标调整,轮椅用户的数据采集和展示有独立逻辑。
  • HealthKit 的 Mobility 数据在 Apple Health app 的「移动能力」类别中也可以看到,你的 app 不需要独自承担数据采集。
  • 所有 Mobility 数据都受 HealthKit 权限控制,用户可以选择性地授权你的 app 读取部分数据。
WWDC 2020