WWDC Quick Look 💓 By SwiftGGTeam
Build a research and care app, part 2: Schedule tasks

Build a research and care app, part 2: Schedule tasks

观看原视频

Highlight

ResearchKit 与 CareKit 联动,让开发者可以在几行代码内完成问卷创建、数据收集和进度追踪,患者无需再到诊所填写纸质问卷。

核心内容

从纸质问卷到数字化随访

做临床研究或患者随访时,最头疼的是数据收集。研究者需要设计问卷、打印分发、回收整理,患者则要在诊所排队填写。整个过程低效且容易出错。

Apple 给出的方案是 ResearchKit + CareKit 的组合。ResearchKit 负责呈现专业的调查问卷和主动任务(如测量关节活动度),CareKit 负责管理任务日程和持久化数据。两者通过一套简单的转换机制衔接。

多问题表单:一次完成多项评估

01:46

Jamie 的物理治疗 App 需要每天收集两项数据:疼痛程度和睡眠时长。ResearchKit 的 ORKFormStep 可以把多个问题放在同一页,减少用户的操作步骤。

每个问题用一个 ORKFormItem 定义,通过 ORKScaleAnswerFormat 呈现为滑块。疼痛评分范围 1-10,睡眠时长范围 0-12 小时。滑块比纸质问卷的填涂圆圈更直观,用户也更容易接受。

数据流转:从问卷答案到 CareKit 结果

04:12

ResearchKit 完成问卷后会返回一个 ORKTaskResult,这是一个嵌套结构。根节点是任务结果,子节点对应每个表单步骤,再往下是每个问题的答案。

开发者需要编写一个转换函数,从嵌套结果中提取具体数值,包装成 OCKOutcomeValue,存入 CareKit 的本地数据库。一旦保存成功,CareKit 会自动更新界面上的完成环和任务卡片状态。

动态日程:随时间递减的康复任务

10:01

康复训练的频率通常会随时间降低。Jamie 的需求是:关节活动度测量在第一周每天一次,之后每周一次,一个月后完全停止。

CareKit 的 OCKScheduleElement 支持这种复合日程。开发者可以定义多个日程元素,每个元素有独立的起始时间、结束时间和重复间隔,然后用 OCKSchedule.composing 将它们合并成一个完整的日程。

主动任务:在家完成医学测量

12:00

ResearchKit 内置了关节活动度(Range of Motion)测量任务。用户按照屏幕提示将设备放在膝盖上,完成屈伸动作,系统通过传感器自动计算活动角度。这原本需要在诊所由治疗师辅助完成,现在患者在家就能自行测量。

详细内容

创建多问题表单

import ResearchKit

func createCheckInSurvey() -> ORKTask {
    // 定义疼痛问题
    let painItem = ORKFormItem(
        identifier: "pain",
        text: "How much pain are you experiencing?",
        answerFormat: ORKScaleAnswerFormat(
            maximumValue: 10,
            minimumValue: 1,
            defaultValue: 5,
            step: 1,
            vertical: false,
            maximumValueDescription: "Severe",
            minimumValueDescription: "None"
        )
    )
    painItem.isOptional = false

    // 定义睡眠问题
    let sleepItem = ORKFormItem(
        identifier: "sleep",
        text: "How many hours of sleep did you get?",
        answerFormat: ORKScaleAnswerFormat(
            maximumValue: 12,
            minimumValue: 0,
            defaultValue: 7,
            step: 1,
            vertical: false,
            maximumValueDescription: "12+ hours",
            minimumValueDescription: "None"
        )
    )
    sleepItem.isOptional = false

    // 创建表单步骤
    let formStep = ORKFormStep(
        identifier: "checkin.form",
        title: "Daily Check-In",
        text: "Please answer the following questions."
    )
    formStep.formItems = [painItem, sleepItem]
    formStep.isOptional = false

    // 组装为有序任务
    return ORKOrderedTask(identifier: "checkin", steps: [formStep])
}

关键点:

  • ORKScaleAnswerFormat 创建滑块输入,比文本框更适合数值型问卷
  • isOptional = false 确保问题不可跳过,保证数据完整性
  • ORKFormStep 将多个问题合并到一页,减少用户操作步骤

解析 ResearchKit 结果并转换为 CareKit 数据

import CareKit
import ResearchKit

func extractCheckInOutcomes(
    task: OCKAnyTask,
    result: ORKTaskResult
) -> [OCKOutcomeValue] {
    // 从根结果中找到表单步骤结果
    guard let formResult = result.stepResult(forStepIdentifier: "checkin.form"),
          let questionResults = formResult.results as? [ORKScaleQuestionResult]
    else {
        return []
    }

    var outcomes: [OCKOutcomeValue] = []

    // 提取疼痛评分
    if let painResult = questionResults.first(where: { $0.identifier == "pain" }),
       let painValue = painResult.scaleAnswer {
        let painOutcome = OCKOutcomeValue(painValue.doubleValue)
        painOutcome.kind = "pain"
        outcomes.append(painOutcome)
    }

    // 提取睡眠时长
    if let sleepResult = questionResults.first(where: { $0.identifier == "sleep" }),
       let sleepValue = sleepResult.scaleAnswer {
        let sleepOutcome = OCKOutcomeValue(sleepValue.doubleValue)
        sleepOutcome.kind = "sleep"
        outcomes.append(sleepOutcome)
    }

    return outcomes
}

关键点:

  • ORKTaskResult 是嵌套结构,需要通过 stepResult(forStepIdentifier:) 逐层向下查找
  • OCKOutcomeValuekind 属性用于后续按类型检索数据,建议始终设置
  • 每个问题结果需要匹配对应的 identifier 才能正确提取

创建复合日程

import CareKit

func createRangeOfMotionSchedule() -> OCKSchedule {
    let calendar = Calendar.current
    let thisMorning = calendar.startOfDay(for: Date())
    let nextWeek = calendar.date(byAdding: .day, value: 7, to: thisMorning)!
    let nextMonth = calendar.date(byAdding: .month, value: 1, to: thisMorning)!

    // 第一周:每天一次
    let dailyElement = OCKScheduleElement(
        start: thisMorning,
        end: nextWeek,
        interval: DateComponents(day: 1),
        text: "Daily measurement",
        targetValues: [],
        duration: .allDay
    )

    // 第二周到月底:每周一次
    let weeklyElement = OCKScheduleElement(
        start: nextWeek,
        end: nextMonth,
        interval: DateComponents(day: 7),
        text: "Weekly measurement",
        targetValues: [],
        duration: .allDay
    )

    // 合并两个日程元素
    return OCKSchedule.composing([dailyElement, weeklyElement])
}

关键点:

  • OCKScheduleElement 定义一个时间区间内的重复规则
  • interval 使用 DateComponents,支持天、周、月等多种粒度
  • OCKSchedule.composing 将多个元素合并,元素之间的时间区间互不重叠
  • 超过 end 日期后任务自动消失,无需手动清理

使用 ResearchKit 内置的关节活动度任务

import ResearchKit

func createRangeOfMotionTask() -> ORKTask {
    var predefinedTask = ORKOrderedTask.kneeRangeOfMotionTask(
        withIdentifier: "rom.left.knee",
        limbOption: .left,
        intendedUseDescription: nil,
        options: [.excludeConclusion]
    )

    // 添加自定义完成提示
    let completionStep = ORKCompletionStep(identifier: "completion")
    completionStep.title = "Great job!"
    completionStep.text = "Keep up the good work with your physical therapy."
    predefinedTask.appendSteps([completionStep])

    return predefinedTask
}

关键点:

  • ORKOrderedTask.kneeRangeOfMotionTask 是 ResearchKit 预定义的医学测量任务
  • limbOption: .left 指定测量左腿,excludeConclusion 移除默认的完成提示
  • 可以追加自定义步骤,让提示语更贴合具体应用场景

核心启发

  1. 做什么:为慢性病患者打造一款居家随访 App,替代纸质日记本 为什么值得做:ResearchKit 的表单和主动任务让数据收集标准化,CareKit 的日程系统让随访计划自动化 怎么开始:从 ORKFormStep 设计每日问卷入手,用 OCKSchedule 管理提醒频率

  2. 做什么:在健康类 App 中加入关节活动度或步态分析功能 为什么值得做:ResearchKit 内置了多种医学测量任务,开发者无需自己实现传感器算法 怎么开始:调用 ORKOrderedTask.kneeRangeOfMotionTaskORKOrderedTask.walkingTask,通过 ORKTaskResult 提取测量数据

  3. 做什么:构建一个支持递减频率的康复训练计划 为什么值得做:CareKit 的复合日程可以用多个 OCKScheduleElement 拼接出任意复杂的时序规则 怎么开始:定义多个日程元素,分别设置不同的 startendinterval,用 OCKSchedule.composing 合并

  4. 做什么:将 ResearchKit 问卷数据与 HealthKit 打通 为什么值得做:疼痛评分、睡眠质量等数据可以写入 HealthKit,与其他健康数据形成完整画像 怎么开始:在 extractCheckInOutcomes 中增加 HealthKit 写入逻辑,使用 HKQuantityType 保存数值

关联 Session

评论

GitHub Issues · utterances