Highlight
CareKit 2020 更新让这个开源健康管理框架获得 SwiftUI 任务卡、watchOS 支持、HealthKit 驱动任务、FHIR 映射和远程同步 API,开发者可以把护理计划、健康数据、临床记录和 iPhone/Apple Watch 状态同步放进同一套应用结构。
核心内容
做健康管理 App 时,界面和数据很容易分裂。用户看到的是每日拉伸、用药、问卷、症状记录和图表;开发者背后要维护任务日程、完成状态、HealthKit 数据、临床系统记录,还要处理 iPhone 与 Apple Watch 的同步。CareKit 的目标,是把这些护理场景拆成可复用的 UI、数据模型和同步层。
这场 session 先给出 CareKit 的模块边界:CareKitStore 提供健康领域的数据模型和 Core Data 持久层,CareKitUI 提供展示任务、图表和联系人的静态视图,CareKit 把 UI 和 store 连接起来,让 store 中的数据变化自动反映到界面上。(00:31)
2020 年的变化,是把这些边界继续向外扩展。UI 层新增 SwiftUI 版本的任务卡和信息卡;同步视图可以把 store 中的任务自动映射成界面;CareKit、CareKitUI 和 CareKitStore 都能构建到 watchOS;store 层新增 HealthKit passthrough、FHIR coder 和远程同步协议。结果是,开发者不必把健康数据、护理任务和跨设备状态分别做成三套孤立流程。
Session 的演示很具体:同一个护理 App 里,用户在 iPhone 上记录肌肉抽筋,在 Apple Watch 上完成拉伸任务,iPhone 的任务卡、adherence ring 和图表会同步更新。CareKit 在这里承担的是应用结构,而不只是几张默认卡片。
详细内容
CareKitUI 新增 SwiftUI 任务卡
(02:23)SimpleTaskView 已经有 UIKit API,2020 年新增 SwiftUI API。最小用法只需要 title、detail 和完成状态。
import CareKitUI
import SwiftUI
struct MySimpleTaskView: View {
var body: some View {
SimpleTaskView(
title: Text("Stretches"),
detail: Text("15 minutes"),
isComplete: false)
}
}
关键点:
CareKitUI提供的是静态视图,适合展示已经准备好的任务数据。title和detail接收Text,所以可以继续使用 SwiftUI 的 view modifier。isComplete控制任务卡的完成状态,适合预览、静态 feed 或自定义数据源。
(03:42)这张卡不是封闭组件。开发者可以注入自定义 header,也可以把内容附加到任意方向。演讲中用 HeaderView 和一条 accent bar 改造了默认头部,又用 CardView 包住任务卡、分割线和说明文字,把一张简单完成卡变成带说明的护理任务。
(05:08)同一批 UI 更新还包括 LabeledValueTaskView、NumericProgressTaskView、OCKFeaturedContentView、OCKDetailView 和 LinkView。这些视图覆盖心率数值、运动目标进度、健康内容推荐、HTML 详情页和站内外链接入口,都是护理 App feed 中常见的卡片形态。
同步视图把 store 数据映射到 SwiftUI
(08:56)静态视图解决展示问题,同步视图解决数据变化问题。CareKit.SimpleTaskView 接收 taskID、OCKEventQuery 和 OCKSynchronizedStoreManager,再从 store 中定位当天任务并自动更新 UI。
// Synchronized Task View
import CareKit
import CareKitUI
import SwiftUI
struct MySynchronizedTaskView: View {
let storeManager: OCKSynchronizedStoreManager
var body: some View {
CareKit.SimpleTaskView(
taskID: "stretch",
eventQuery: OCKEventQuery(for: Date()),
storeManager: storeManager)
}
}
关键点:
taskID是 store 中任务的稳定标识,视图靠它找到护理任务。OCKEventQuery(for: Date())把查询范围收敛到某一天的 event。OCKSynchronizedStoreManager监听 store 变化,任务完成状态改变后会驱动界面刷新。
(09:26)如果默认映射不够,初始化器还提供 closure。closure 中可以访问 controller、task data 和 view model,再自己创建 CareKitUI.SimpleTaskView。演讲用这个入口把一个 CareKit 任务改造成 ResearchKit survey 的入口,用户点任务卡时弹出 ResearchKitSurvey()。
HealthKit 驱动任务进入 CareKit store
(12:52)CareKit 以前主要依赖 OCKStore。2020 年新增 OCKHealthKitPassthroughStore,它以 HealthKit 作为 source of truth,再和 Core Data 版本的 OCKStore 一起挂到 OCKStoreCoordinator 上。读数据时,coordinator 会聚合内部 stores;写数据时,它一次只写入一个 store,以保持事务边界清晰。
// Setting up the Store
import CareKit
import CareKitStore
let coreDataStore = OCKStore(name: "core-data-store")
let healthKitPassthroughStore = OCKHealthKitPassthroughStore(name: "hk-passthrough—store")
let coordinator = OCKStoreCoordinator()
coordinator.attach(store: coreDataStore)
coordinator.attach(eventStore: healthKitPassthroughStore)
let storeManager = OCKSynchronizedStoreManager(wrapping: coordinator)
关键点:
OCKStore仍然负责 CareKit 自己的数据模型和持久化。OCKHealthKitPassthroughStore让 HealthKit 数据以 event store 的身份参与 CareKit 查询。OCKStoreCoordinator把两个 store 包在一起,外部使用方式接近单一 store。- 最后仍然用
OCKSynchronizedStoreManager提供给同步视图。
(14:15)HealthKit 驱动任务的例子是每天早上 8 点的 30 分钟运动目标。任务通过 OCKHealthKitLinkage 连接到 .appleExerciseTime,类型是 cumulative,单位是 minute。HealthKit 中的运动分钟数达到目标后,CareKit 任务可以自动完成。
// Adding HealthKit Linked Tasks to the Store
let schedule = OCKSchedule.dailyAtTime(
hour: 8,
minutes: 0,
start: Date(),
end: nil,
text: nil,
duration: .allDay,
targetValues: [OCKOutcomeValue(30.0, units: "Minutes")])
let link = OCKHealthKitLinkage(
quantityIdentifier: .appleExerciseTime,
quantityType: .cumulative,
unit: .minute())
let steps = OCKHealthKitTask(
id: "exerciseMinutes",
title: "Exercise Minutes",
carePlanUUID: nil,
schedule: schedule,
healthKitLinkage: link)
storeManager.store.addAnyTask(steps)
关键点:
OCKSchedule.dailyAtTime定义护理任务的发生时间和目标值。targetValues中的OCKOutcomeValue(30.0, units: "Minutes")会被任务视图用来展示目标。OCKHealthKitLinkage把 CareKit task 绑定到 HealthKit quantity。OCKHealthKitTask进入 store 后,可以用NumericProgressTaskView展示进度和目标。
FHIR coder 负责临床数据映射
(15:23)健康行业里很多系统使用 FHIR 交换数据。CareKit 2020 增加 FHIR 兼容性,并通过新的开源框架 FHIRModels 映射 FHIR release。演讲明确提到 CareKit 支持 DSTU2 和 R4,并提供 coder 在 CareKit entity 与 FHIR data 之间转换。
// Encoding and Decoding FHIR Data
import CareKitStore
import CareKitFHIR
let coder = OCKR4PatientCoder()
// CareKit entity to FHIR data
let patient = OCKPatient(...)
let json = try! coder.encode(patient)
// FHIR data to CareKit entity
let data: Data = //...
let resourceData = OCKFHIRResourceData<R4, JSON>(data: data)
let patient: OCKPatient = try! coder.decode(resourceData)
关键点:
CareKitFHIR是包含 coder 的 Swift Package Manager 包。OCKR4PatientCoder负责 R4 release 中 Patient 资源与OCKPatient的转换。OCKFHIRResourceData<R4, JSON>在类型层标记 FHIR release 和编码格式,避免把错误版本的数据交给 coder。- 这段能力适合把临床系统或 HealthKit clinical records 中的 FHIR 数据带入护理 App。
(17:49)FHIR 和 CareKit 的模型不总是一一对应。Session 用 patient name 举例:如果默认映射会丢字段,开发者可以提供 closure,自行决定 CareKit name 如何写入 FHIR HumanName。
// Customizing the Data Mapping
// Encoding process
coder.setFHIRName = { name, fhirPatient in // ModelsR4.Patient
let humanName = HumanName()
humanName.family = name.familyName.map { FHIRPrimitive(FHIRString($0)) }
humanName.given = name.givenName.map { [FHIRPrimitive(FHIRString($0))] }
fhirPatient.name = [humanName]
}
关键点:
setFHIRName暴露 CareKit name 和 FHIRModels 的 patient 对象。HumanName.family和HumanName.given需要按 FHIRModels 的 primitive 类型封装。- 自定义映射的目的,是防止临床数据在 CareKit 与 FHIR 往返时丢失语义。
远程同步覆盖服务器和 Apple Watch
(19:16)CareKit 新增远程同步 API,解决的问题是“如何把 CareKit App 中的数据同步到服务器”。App 侧的入口在 OCKStore:初始化时传入 remote,即可启用远程同步;调用 synchronize 会触发本地 store 与 remote store 同步。服务端或云提供商要实现 OCKRemoteSynchronizable,处理 delegate、自动同步开关、fetch changes、push revisions 和冲突解决策略。
(25:54)演讲随后用同一套 remote sync API 做 iPhone 与 Apple Watch 同步。关键类是 OCKWatchConnectivityPeer。它遵守远程同步协议,让 iPhone 和 Apple Watch 可以把彼此当成 remote store。
private lazy var remote = OCKWatchConnectivityPeer()
关键点:
OCKWatchConnectivityPeer是 CareKit 提供的 WatchConnectivity remote。- watchOS 端把它传给
OCKStore,store 就具备与配对 iPhone 同步的入口。 - 同步协议的抽象没有绑定服务器,所以同一组概念也能用于设备间同步。
(26:33)WatchConnectivity 的 session delegate 仍由开发者拥有。CareKit 需要开发者在收到消息时把 message 转交给 remote,再把 CareKit 生成的 reply 发回 iPhone。
class SessionDelegate: NSObject, WCSessionDelegate {
let remote: OCKWatchConnectivityPeer
let store: OCKStore
init(remote: OCKWatchConnectivityPeer, store: OCKStore) {
self.remote = remote
self.store = store
}
func session(
_ session: WCSession,
activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?) {
print("New session state: \(activationState)")
}
func session(
_ session: WCSession,
didReceiveMessage message: [String: Any],
replyHandler: @escaping ([String: Any]) -> Void) {
print("Received message from peer!")
}
}
关键点:
activationDidCompleteWith是首次同步的合适位置,演讲后续在这里调用store.synchronize。didReceiveMessage是 CareKit 消息进入 watchOS app 的入口。- 真实转发逻辑使用
remote.reply(to:store:)生成 reply,再调用 WatchConnectivity 的replyHandler。 - 这段配合完成后,Apple Watch 上完成拉伸任务,iPhone 上订阅同一 store 的视图会同步更新。
核心启发
-
做一个术后康复任务 feed
- 做什么:把每日拉伸、冰敷、服药和疼痛记录做成 CareKit 任务卡。
- 为什么值得做:
SimpleTaskView、InstructionsTaskView和NumericProgressTaskView已经覆盖任务说明、完成按钮和目标进度。 - 怎么开始:先用
OCKStore存任务和 outcome,再用OCKSynchronizedStoreManager驱动当天的 SwiftUI 任务列表。
-
做一个 HealthKit 自动完成的运动目标
- 做什么:让用户设置每天 30 分钟运动目标,达到 HealthKit 的 exercise minutes 后自动完成 CareKit 任务。
- 为什么值得做:session 中的
OCKHealthKitTask正是这个模式,HealthKit 数据成为任务完成依据。 - 怎么开始:创建
OCKHealthKitPassthroughStore,通过OCKHealthKitLinkage(quantityIdentifier: .appleExerciseTime, quantityType: .cumulative, unit: .minute())绑定任务。
-
做一个问卷触发型护理流程
- 做什么:用户点 CareKit 任务卡后,打开 ResearchKit 问卷记录症状或副作用。
- 为什么值得做:同步视图的 closure 可以自定义 task data 到 UI 的映射,演讲示例直接弹出
ResearchKitSurvey()。 - 怎么开始:用
CareKit.SimpleTaskView的自定义 initializer 读取controller.viewModel,在 action 中切换@State并呈现 survey。
-
做一个 iPhone 与 Apple Watch 同步的护理任务
- 做什么:iPhone 展示完整护理计划和图表,Apple Watch 只展示当天最重要的两张任务卡。
- 为什么值得做:CareKit 2020 支持 watchOS,并提供
OCKWatchConnectivityPeer复用远程同步 API。 - 怎么开始:在 watchOS extension 中创建
OCKStore(name:remote:),实现WCSessionDelegate,把收到的 message 交给remote.reply(to:store:)。
-
做一个临床记录导入后的护理计划
- 做什么:把医疗机构返回的 FHIR patient 或 medication order 转成 CareKit entity,用于生成后续护理任务。
- 为什么值得做:CareKitFHIR 和 FHIRModels 让 CareKit 能处理 DSTU2、R4 等 FHIR release。
- 怎么开始:根据数据版本选择 coder,例如
OCKR4PatientCoder,必要时用setFHIRName这类 closure 修正字段映射。
关联 Session
- What’s new in ResearchKit — ResearchKit 的 onboarding、survey 和 active task 更新,补足 CareKit 中呈现 ResearchKit survey 的数据采集侧。
- What’s new in HealthKit — HealthKit 在 iOS 14 和 watchOS 7 中新增 ECG、症状与 mobility 数据,解释 CareKit 接入健康数据的来源。
- Synchronize health data with HealthKit — 深入讲解 HealthKit 数据版本、锚定查询和跨设备同步,补充 CareKit 远程同步场景。
- Handling FHIR without getting burned — 介绍 FHIRModels 如何处理 HealthKit 或临床系统中的 FHIR 数据,是理解 CareKit FHIR coder 的前置内容。
评论
GitHub Issues · utterances