Highlight
Apple 发布开源 Swift 包 FHIRModels,把 HealthKit 里的 FHIR 临床记录解码为 DSTU2、R4 等原生 Swift 模型,并在编解码时阻止结构无效的资源。
核心内容
Health app 从 2018 年开始支持 Fast Healthcare Interoperability Resources(FHIR)临床记录。流程从 Health app 开始:它直接连接医疗机构 API,把 FHIR 数据下载到 iOS 设备,再安全地存入 HealthKit。开发者拿到的是 HealthKit API 暴露出来的临床记录。
真正麻烦的部分在 App 这一侧。FHIR 资源里有字符串、受限 code、ISO-8601 风格的日期字符串、布尔值、数字和多层嵌套结构。只看处方的 status、date、medication 时,自己写 Swift Codable 似乎够用;一旦面对完整处方资源,模型数量和字段复杂度会迅速放大。
Apple 给出的解法是 FHIRModels。它是一个开源 Swift package,提供同一 FHIR release 下所有 resource 的原生数据模型,并在 encode、decode 时检查 resource integrity。package 还包含日期时间解析、code 字段枚举、value polymorphic 属性枚举,以及 DSTU2、R4 和 latest build release 的库。
这场 session 的例子很小:做一个 medication list app,只请求 medication 访问权限,然后把 HKClinicalRecord 里的 FHIR JSON 解码成处方模型。这个例子也暴露了两个真实边界:HealthKit 授权要遵守只请求当前功能需要的数据原则,FHIR release 在外部世界会长期并存,App 需要按 release 选择对应模型。
详细内容
从 HealthKit 临床记录解码 FHIR 模型
(04:32)FHIRModels 的第一条路径,是把 HealthKit 返回的临床记录变成 Swift 模型。session 使用 medication prescription 举例:App 拿到 HKClinicalRecord,读取关联的 fhirResource,再用 JSONDecoder 解码成 DSTU2 里的 MedicationOrder。
// Use with Health Records FHIR data from HealthKit
import HealthKit
import ModelsDSTU2
// Grab HKClinicalRecord from HealthKit API
let clinicalRecord: HKClinicalRecord
let resource = clinicalRecord.fhirResource!
// Print the prescription note
let decoder = JSONDecoder()
let prescription = try decoder.decode(MedicationOrder.self, from: resource.data)
print("\(prescription.note)")
关键点:
import HealthKit对应临床记录来源,import ModelsDSTU2对应当前 FHIR release 的模型库。HKClinicalRecord来自 HealthKit API,clinicalRecord.fhirResource保存原始 FHIR JSON 数据。JSONDecoder不再解码到开发者手写的结构体,而是解码到 FHIRModels 提供的MedicationOrder。prescription.note说明解码后的对象已经是 Swift 模型,后续读取字段不需要手动走 JSON 字典。
给嵌套结构补一层业务展示
(05:04)FHIR 资源会把实际业务字段放在很深的嵌套里。session 用 dosage instruction 举例:处方说明里可能包含一个有效日期区间,日期区间位于 TimingRepeat 的 bounds period 里。示例用 extension 把这个嵌套访问封装成展示字符串。
// Make using "TimingRepeat" period dates easier by writing an extension
extension TimingRepeat {
var periodDisplayString: String? {
if case .period(let period) = bounds {
return "\(period.start) - \(period.end)"
}
return nil
}
}
// Collect all dosage instructions on medication prescriptions
let instructions: [String] = prescription.dosageInstruction?.map { dosage in
guard let period = dosage.timing?.repeat?.periodDisplayString else {
return "\(dosage.text)"
}
return "\(period): \(dosage.text)"
}
关键点:
TimingRepeat是 FHIRModels 提供的类型,extension 只负责增加 App 自己的展示逻辑。if case .period(let period) = bounds处理 value polymorphic 属性里的 period 分支。period.start和period.end来自嵌套 period,示例把它们拼成日期范围。prescription.dosageInstruction?.map遍历处方的所有用药说明。guard let period = ...保留没有日期范围的情况,直接返回dosage.text。- 返回字符串把日期范围和用药说明放在一起,适合 medication list 这类界面展示。
同时支持多个 FHIR release
(06:20)FHIR release 不会随着新版本发布而统一迁移。session 特别点名 DSTU2 还会流行几年,所以 App 需要支持多个 release。FHIRModels 的做法是按 release 暴露不同 module,App 根据资源所属 release 选择对应模型。
// Supporting multiple releases
import ModelsDSTU2
import ModelsR4
let decoder = JSONDecoder()
let release: FHIRRelease
let data: Data
let note: String? = nil
switch release {
case .dstu2:
let model = try decoder.decode(ModelsDSTU2.MedicationOrder.self, from: data)
note = model.note?.value?.string
case .r4:
let model = try decoder.decode(ModelsR4.MedicationRequest.self, from: data)
note = model.note?.compactMap({ $0.text.value?.string }).joined(separator: "\n")
default:
note = "Unsupported FHIR release \(release)"
}
关键点:
import ModelsDSTU2和import ModelsR4表示同一个 App 可以同时引用多个 release 的模型库。release: FHIRRelease是示例假设已有的 release 判断入口。switch release把解析分支显式写出来,避免把 DSTU2 数据塞进 R4 模型。- DSTU2 示例解码到
MedicationOrder,R4 示例解码到MedicationRequest,这反映了不同 release 的资源模型差异。 - 两个分支读取 note 的路径不同,说明 release 差异会影响字段访问方式。
default分支给出 unsupported release 文案,避免无声失败。
授权和数据范围要按功能收窄
(01:13)在拿到 HKClinicalRecord 前,App 必须先向用户请求授权。session 的 medication list app 只请求 medication 访问权限,遵守 “proportional to what you need” 原则。授权界面还会展示 App 在 Info.plist 里提供的说明文字和 privacy policy 链接,Apple 会审核这份 privacy policy。
关键点:
- FHIR 临床数据默认只分享设备上已有的数据。
- 每次查询 FHIR 数据前都应该请求授权,让用户有机会批准新数据访问。
- 如果没有新的内容需要授权,系统 sheet 不会出现。
- 这个授权模型和 FHIRModels 解码流程相连:先让用户批准精确的数据范围,再解析 HealthKit 返回的 FHIR resource。
核心启发
- Medication list:做什么:显示用户处方里的 note 和 dosage instruction。为什么值得做:session 的代码直接从
HKClinicalRecord解码MedicationOrder,并演示如何展示处方说明。怎么开始:只请求 medication 访问权限,拿到fhirResource.data后用ModelsDSTU2.MedicationOrder解码,再把 note 和 dosage instruction 映射到列表。 - 用药说明时间轴:做什么:把每条 dosage instruction 按有效日期范围展示。为什么值得做:
TimingRepeat的boundsperiod 能给说明补上起止日期。怎么开始:为TimingRepeat增加展示 extension,读取period.start和period.end,没有 period 时回退到原始说明文字。 - 多 release 处方解析器:做什么:在同一个 App 中处理 DSTU2 与 R4 处方数据。为什么值得做:session 明确说已有 API 不会随新 release 自动更新,DSTU2 还需要继续支持。怎么开始:保存或判断资源的
FHIRRelease,用switch选择ModelsDSTU2.MedicationOrder或ModelsR4.MedicationRequest。 - FHIR 数据质量检查面板:做什么:在调试或导入流程中标出无法解码、release 不支持或结构无效的 resource。为什么值得做:FHIRModels 在 encode 和 decode 时会检查 resource integrity,阻止结构无效的资源进入业务流程。怎么开始:把解码错误和 unsupported release 分支集中记录,给开发者或测试人员一个按 resource 查看问题的列表。
- Care app 的临床记录入口:做什么:在 care app 中把用户授权的 HealthKit 临床记录转成可展示的 care 数据。为什么值得做:session 结尾建议继续看 CareKit 如何结合 FHIR 构建 care apps。怎么开始:先完成 HealthKit 授权和 FHIRModels 解码,再把处方 note、用药说明、日期范围接到 CareKit 或自定义照护界面。
关联 Session
- Getting started with HealthKit — 先掌握 HealthKit 授权、读写和 query,再处理临床记录里的 FHIR 数据。
- What’s new in HealthKit — 了解同一年 HealthKit 新增的数据类型和健康数据能力。
- Synchronize health data with HealthKit — 补上 HealthKit 数据变化检测、同步和跨设备管理。
- What’s new in CareKit — session 结尾推荐的后续内容,展示如何把 FHIR 与 CareKit 用在 care app。
评论
GitHub Issues · utterances