构建研究与护理应用(二):调度研究任务
Build a research and care app, part 2: Schedule tasks
2021年6月10日
一句话判断
CareKit 的任务调度系统比想象中灵活得多 —— 支持条件触发、自定义间隔、甚至基于 HealthKit 数据的事件驱动,但这套 API 的学习曲线不低,文档也不够完善。
这场 Session 讲了什么
作为系列的第二部分,这集聚焦于如何在 CareKit 中创建和管理研究任务的调度。Session 深入讲解了 OCKSchedule 的各种配置方式:固定时间触发、条件触发、自定义间隔,以及如何把这些调度和 ResearchKit 的任务(Survey、Active Task)结合。
演示了一个心血管研究 app 中常见的任务类型:每日心率检测(Active Task)、每周健康问卷(Survey)、以及根据 HealthKit 数据触发的异常提醒。Session 强调了调度灵活性和用户负担之间的平衡 —— 太频繁的任务会导致参与者退出研究。
还介绍了 OCKOutcome 的概念:任务完成后的数据记录。每条 Outcome 绑定到具体的 Schedule Event,可以包含多个值(如心率数值、问卷答案等)。
值得深挖的点
Schedule Event 和 Outcome 的对应关系。CareKit 的调度不是简单的”每天几点”,而是生成一系列离散的 OCKScheduleEvent,每个事件有唯一 ID。参与者完成任务后,OCKOutcome 会精确绑定到触发它的那个 Event。这意味着你可以在用户补做漏掉的任务时,准确追溯到原始调度时间点,这对研究数据的完整性至关重要。
条件任务的实现。CareKit 支持基于 HealthKit 数据的触发条件,比如”当步数低于 1000 时提醒用户做活动”。这需要把 OCKHealthKitCondition 附加到 Schedule 上,系统会在后台监控 HealthKit 数据变化。但要注意,条件检查依赖于后台刷新(Background Fetch),不是实时的,延迟可能在 15 分钟到数小时之间。
代码片段
创建自定义间隔的调度计划:
import CareKit
func createWeeklySchedule() -> OCKSchedule {
// 每周一次,周三上午 9 点触发,持续 12 周
let startDate = Calendar.current.date(
byAdding: .day, value: 1, to: Date()
)!
let endDate = Calendar.current.date(
byAdding: .month, value: 3, to: startDate
)!
let elements: [OCKScheduleElement] = [
OCKScheduleElement(
start: startDate,
end: endDate,
interval: DateComponents(weekOfMonth: 1),
text: "本周健康问卷已发布",
targetValues: [],
duration: .hours(24) // 24 小时内完成有效
)
]
return OCKSchedule(composing: elements)
}
记录任务结果:
import CareKit
func recordOutcome(taskEvent: OCKTaskEvent, heartRate: Double) {
let store = OCKStore(name: "CardioStudy")
// 创建结果,绑定到具体的调度事件
let outcome = OCKOutcome(
taskUUID: taskEvent.task.uuid,
taskOccurrenceIndex: taskEvent.scheduleEvent.occurrence,
values: [
OCKOutcomeValue(Double(heartRate), units: "bpm")
]
)
store.addOutcome(outcome) { result in
switch result {
case .success:
print("心率数据记录成功: \(heartRate) bpm")
case .failure(let error):
print("记录失败: \(error)")
}
}
}
创建带条件的任务:
import CareKit
import HealthKit
func createConditionalTask() {
// 当用户当天步数低于目标时,提醒做简短运动
let stepCondition = OCKHealthKitCondition(
quantityType: HKQuantityType.quantityType(forIdentifier: .stepCount)!,
comparator: .lessThan,
value: HKQuantity(unit: .count(), doubleValue: 5000)
)
var task = OCKTask(
id: "activity_reminder",
title: "该活动一下了",
carePlanUUID: nil,
schedule: OCKSchedule.dailyAtTime(
hours: 18, minutes: 0,
start: Date(), end: nil,
text: "今天步数不足,做一组拉伸吧"
)
)
// 附加 HealthKit 条件
task.impactsAdherence = true
// 条件满足时才在任务列表中显示
}
最佳实践
- 任务的 duration 设置要给用户留足余地。如果任务是早上 8 点触发、duration 设 1 小时,上班族大概率错过。建议至少设 12 小时窗口,甚至全天有效。
- 用
OCKSchedule.composing组合多个调度元素,实现”前两周每天一次,之后每周一次”这种递减式研究设计,避免参与者疲劳。 - Outcome 数据尽早同步到服务端。CareKit 本地存储在 app 被删除后就没了,对于研究数据这是不可接受的。建议每次记录后立即推送到你的后端。
还有什么值得关注
- CareKit 的任务完成度会自动计算 adherence(遵从率),可以直接展示给研究者和参与者。
- 系列第三集(Session 10081 之外的 part 3)覆盖了数据收集和分析流程。
OCKContact可以用来在 app 内展示研究团队联系方式,增加参与者信任度。