What's new in CareKit
Health & Fitness 进阶 22m

CareKit 新特性概览

What's new in CareKit

2020年6月25日

在 Apple 官方观看视频

一句话判断

CareKit 2.0 的架构重构让它从”黑盒框架”变成了真正的开发工具包——如果你在做健康类应用,这次升级值得关注。

这场 Session 讲了什么

CareKit 是 Apple 推出的开源框架,用于构建健康管理类应用。WWDC 2020 带来了 CareKit 的重大更新,核心变化是从以 View 为中心的封闭架构转向以数据模型驱动的开放架构。新版本引入了 OCKStore 作为统一的数据持久层,支持同步、查询和监听数据变更。

Session 详细介绍了新的数据模型层次:OCKTask 定义”要做什么”(如每日服药提醒),OCKOutcome 记录”做了什么”(如实际服药时间),OCKSchedule 控制”什么时候做”(如每天早中晚各一次)。这套模型设计清晰,覆盖了健康管理场景中的核心需求。重要的是,所有数据模型都遵循 Identifiable 协议,可以无缝集成到 SwiftUI 的声明式 UI 中。

另一个重大变化是 CareKit 现在完全基于 Swift Package Manager 分发,不再依赖 CocoaPods。框架内部也大量采用了 Swift 的特性,如 async/await 风格的回调处理和泛型约束。

值得深挖的点

OCKStore 与 HealthKit 的双向同步

CareKit 2.0 内置了与 HealthKit 的集成能力。你可以配置 OCKHealthKitStore 来自动将任务完成情况同步到 HealthKit,也可以从 HealthKit 读取数据作为 outcome 的来源。这意味着用户在 Apple Watch 上记录的步数可以自动成为 CareKit 中某项任务的完成结果,极大减少了手动录入的负担。

CareKit 的同步机制

新架构支持通过 OCKRemoteStore 实现云端同步。虽然 Apple 没有提供现成的云存储后端,但定义了清晰的同步协议。开发者可以选择自己的后端(CloudKit、Firebase 或自建服务器),只要遵循 OCKStoreSynchronizable 协议即可。这种”提供同步接口但不绑定云服务”的做法很聪明,给了开发者最大的自由度。

代码片段

import CareKit

// 创建 OCKStore 并配置任务
let store = OCKStore(name: "MyCareStore")

// 定义一个每日服药任务
let medicationSchedule = OCKSchedule.dailyAtTime(
    hours: 8, minutes: 0,
    start: Date(), end: nil,
    text: "早餐后服用",
    duration: .hours(2),
    repeatResults: true
)

var medicationTask = OCKTask(
    id: "medication_morning",
    title: "服用降压药",
    carePlanUUID: nil,
    schedule: medicationSchedule
)
medicationTask.instructions = "请随餐服用,每次一片"
medicationTask.impactsAdherence = true

// 保存任务到 store
store.addTask(medicationTask) { result in
    switch result {
    case .success(let task):
        print("任务创建成功: \(task.id)")
    case .failure(let error):
        print("创建失败: \(error)")
    }
}
// 查询某任务今日的完成情况
let query = OCKOutcomeQuery()
query.taskIDs = ["medication_morning"]
query.dateInterval = DateInterval(start: Date().startOfDay, end: Date().endOfDay)

store.fetchOutcomeQuery(query) { result in
    switch result {
    case .success(let outcomes):
        if outcomes.isEmpty {
            print("今日尚未服药")
        } else {
            print("今日已服药,记录数: \(outcomes.count)")
            // 查看具体的服药时间
            for outcome in outcomes {
                for value in outcome.values {
                    print("服药时间: \(value.date ?? outcome.date)")
                }
            }
        }
    case .failure(let error):
        print("查询失败: \(error)")
    }
}
// 记录任务完成(用户标记已服药)
func markMedicationTaken(for taskID: String) {
    let scheduleEvent = OCKScheduleEvent(
        start: Date(),
        end: Date().addingTimeInterval(3600),
        element: 0,
        occurrence: 0
    )
    
    var outcome = OCKOutcome(
        taskUUID: UUID(),  // 对应任务的 UUID
        taskOccurrenceIndex: 0,
        values: [
            OCKOutcomeValue(
                boolean: true,
                kind: "medicationTaken"
            )
        ]
    )
    
    store.addOutcome(outcome) { result in
        switch result {
        case .success:
            print("服药记录已保存")
        case .failure(let error):
            print("保存失败: \(error)")
        }
    }
}

最佳实践

  • 在应用启动时就初始化 OCKStore,避免在 View 层重复创建实例导致数据不一致
  • 使用 OCKSchedule 的 builder API 构建复杂的时间表,不要手动拼接日期数组
  • 为每种健康任务定义清晰的 id 命名规范,方便后续查询和统计
  • 测试时使用内存 store(OCKStore(name: "Test", type: .inMemory)),避免污染真实数据
  • 考虑与 HealthKit 的集成时,先梳理清楚哪些数据应该由用户主动记录、哪些可以自动采集

还有什么值得关注

  • CareKit 现在是完全开源的,你可以在 GitHub 上查看源码并提交 issue
  • 框架内置了自适应提醒功能,可以根据用户的完成率自动调整提醒频率
  • Session 展示了一个配合 SwiftUI 使用的完整示例,新的 Card View 组件可以直接展示任务进度
WWDC 2020