Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no card
Apple Health (HealthKit) 转 FHIR
使用 Swift 读取 Apple Watch 和 Apple Health 数据(通过 HealthKit),按本地日期,并写入 FHIR R4 记录:权限、后台同步、Apple 的规则。
Apple Watch 数据存储在配对 iPhone 上的 Apple Health 中,读取它的唯一方法是使用 HealthKit,从该 iPhone 上的应用程序读取,并获得用户的许可。 要将其放入 FHIR 记录,请使用 HKStatisticsCollectionQuery 读取每日统计数据,将值发送到您的后端,并将每个指标作为具有 LOINC 代码和 UCUM 单位的 Observation 写入。本页展示了 Swift 代码(已针对 iOS 17 SDK 进行类型检查)、Apple 设置的权限和规则,以及这些值如何成为 FHIR R4 Observations。
Apple Health 中的内容
Apple Health 收集 iPhone 和 Apple Watch 测量的数据 — 心率、静息心率、心率变异性 (SDNN)、血氧、呼吸频率、步数、能量、睡眠阶段、心律不齐通知、ECG 记录 — 以及其他应用程序写入的数据:Garmin Connect、Oura、Withings Health Mate、智能秤、血压计、血糖仪。因此,读取 Apple Health 可以涵盖比 Apple Watch 更多的数据。
没有 HealthKit 服务器或 Web API。数据保留在设备上(以及用户的加密 iCloud 备份中);您的应用程序在本地读取它。
权限
- 在 Xcode 中开启 HealthKit 功能,并添加
NSHealthShareUsageDescription到Info.plist(如果您写入数据,则额外添加NSHealthUpdateUsageDescription)。 - 请求您需要的类型。用户可以选择允许某些类型而拒绝其他类型,HealthKit 不会告知您的应用读取权限是否被拒绝:被拒绝的类型只会返回空数据。请设计“无数据”的界面,而不是错误界面。
- 在用户连接时请求权限,并附带解释原因的句子,而不是在首次启动时请求。
在 Swift 中读取每日值
import Foundation
import HealthKit
/// One day of Apple Health data, ready to send to your backend.
struct DailyValue: Codable {
let metric: String // "heart_rate", "steps"
let day: String // "2026-10-01", the user's local day
let value: Double
let min: Double?
let max: Double?
}
let store = HKHealthStore()
let heartRate = HKQuantityType(.heartRate)
let steps = HKQuantityType(.stepCount)
func requestAccess() async throws {
// Read-only: the app never writes to Apple Health.
try await store.requestAuthorization(toShare: [], read: [heartRate, steps])
}
/// Daily statistics for the last `days` days, one value per local calendar day.
func dailyValues(_ type: HKQuantityType, metric: String, unit: HKUnit,
options: HKStatisticsOptions, days: Int) async throws -> [DailyValue] {
let calendar = Calendar.current
let end = calendar.startOfDay(for: Date()).addingTimeInterval(86_400)
let start = calendar.date(byAdding: .day, value: -days, to: end)!
let query = HKStatisticsCollectionQueryDescriptor(
predicate: HKSamplePredicate.quantitySample(type: type,
predicate: HKQuery.predicateForSamples(withStart: start, end: end)),
options: options,
anchorDate: start,
intervalComponents: DateComponents(day: 1))
let collection = try await query.result(for: store)
let fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd"
var out: [DailyValue] = []
collection.enumerateStatistics(from: start, to: end) { stats, _ in
let day = fmt.string(from: stats.startDate)
if options.contains(.cumulativeSum), let sum = stats.sumQuantity() {
out.append(DailyValue(metric: metric, day: day, value: sum.doubleValue(for: unit), min: nil, max: nil))
} else if let avg = stats.averageQuantity() {
out.append(DailyValue(metric: metric, day: day, value: avg.doubleValue(for: unit),
min: stats.minimumQuantity()?.doubleValue(for: unit),
max: stats.maximumQuantity()?.doubleValue(for: unit)))
}
}
return out
}
func collect() async throws -> [DailyValue] {
try await requestAccess()
let bpm = HKUnit.count().unitDivided(by: .minute())
let hr = try await dailyValues(heartRate, metric: "heart_rate", unit: bpm,
options: [.discreteAverage, .discreteMin, .discreteMax], days: 30)
let st = try await dailyValues(steps, metric: "steps", unit: .count(), options: .cumulativeSum, days: 30)
return hr + st // POST these to your backend, which writes them to the FHIR record
}
为何使用统计查询而非读取样本:HKStatisticsCollectionQuery 返回的每日总计和平均值是 Apple Health 已经跨来源去重的结果,因此 iPhone 和 Watch 计算的步数不会重复累加。直接读取原始样本并自行求和是导致步数统计膨胀的最常见原因。
这些天是用户的本地日历日(Calendar.current)。在将其写入 FHIR 记录时,请保持此格式,其中 Observation 的 effectivePeriod 是带有其时区偏移量的本地日期。
保持记录最新
- 打开时: 每次应用进入前台时,读取自上次成功同步以来的天数。这是可靠的基线。
- 在后台:
HKObserverQuery使用enableBackgroundDelivery(for:frequency:)在新数据到达时唤醒您的应用;这需要 HealthKit 后台交付权限,并且 iOS 会决定实际的触发时间。 - 首次连接: 导入有限的历史记录(例如 30 天),而不是多年,然后向前推进。
从 Swift 值到 FHIR 观测
将 DailyValue 数组发送到您的后端,并为每个人、每项指标和每一天写入一个具有稳定标识符的 Observation,当同一天再次到来时进行更新。映射表(HealthKit 类型 → LOINC → UCUM)、一个经过验证的 JSON 示例以及执行此 upsert 操作并在平台上运行的 TypeScript 代码,都可以在 Wearable data in FHIR 中找到。此处两个指标的代码是:
| HealthKit | LOINC | UCUM | 每日值 |
|---|---|---|---|
heartRate |
8867-4 Heart rate | /min |
平均值,包含最小值和最大值作为组件 |
stepCount |
41950-7 Number of steps in 24 hour | {steps} |
总计 |
Apple 的健康数据规则
- App Review Guideline 5.1.3:健康数据不得用于广告或数据挖掘,未经用户同意不得与第三方共享,个人健康信息不得存储在 iCloud 中。
- 您的隐私政策必须说明您读取了什么以及为什么读取;Apple 会进行检查。
- 您的应用写入 Apple Health 的数据将归因于您的应用;您读取的数据将保留其原始来源 (
HKSource),您应将其作为源系统带入记录。
Apple Health Records
除了健身数据之外,Apple Health 还可以存储从参与的医疗系统(主要在美国)下载的临床记录。HealthKit 将它们暴露为 HKClinicalRecord 对象,这些对象携带原始的 FHIR 资源。读取它们需要单独的权限和授权。这与可穿戴设备数据的集成不同,Anpheros Daily 目前不读取它们。
Anpheros 的定位
Anpheros 平台存储结果:即用户的 FHIR R4 记录,其中每个可穿戴设备值都作为一个 Observation 记录,标记为设备数据、其他应用的同意、来源信息和访问日志。它本身不读取 Apple Health — 这部分由您的 iOS 应用完成。Anpheros 自有的应用 Anpheros Daily 会在 iPhone 上读取 Apple Health(仅限 Plus 和 Max 套餐),并将每日摘要写入记录中,使用上述代码。
常见问题解答
我可以从服务器读取 Apple Watch 数据吗?
不可以。Apple Watch 数据会同步到配对的 iPhone 上的 Apple Health,而 HealthKit 只能由该设备上的应用程序使用。应用程序读取数据并将其发送到您的服务器。
有 Apple Health 的 Web API 或 REST API 吗?
没有。Apple 不提供 Apple Health 的 Web 或 REST API:HealthKit 仅在 iPhone(或 Apple Watch)上的应用程序内运行。要在服务器上使用数据,您的应用程序需要获得用户许可后读取数据,然后将其发送,例如作为 FHIR Observations 发送到 Anpheros。
我需要 API 密钥或为 Apple Health API 付费吗?
无需密钥,也无需付费:HealthKit 是 iOS 的一部分。您需要在应用程序中启用 HealthKit 功能,为每种数据类型提供使用说明,并获得用户的许可;App Store 审核会检查您如何使用数据。
我如何知道用户是否拒绝访问 HealthKit 类型?
对于读取访问,您无法知道:HealthKit 会隐藏此信息以保护隐私,而拒绝的类型将不返回任何数据。将“无数据”视为正常状态,并提供一种在 Health 应用中查看权限的方式。
为什么我的步数计数比 Health 应用中的多?
您可能正在汇总来自多个来源(iPhone 和 Watch)的原始样本。请使用 HKStatisticsCollectionQuery 和 .cumulativeSum,它们会应用 Apple Health 自带的去重功能。
Apple Watch 心率变异性的 LOINC 代码是什么?
Apple Health 将 HRV 存储为 SDNN,这与 LOINC 80404-7 匹配。Health Connect 存储 RMSSD,这是一个不同的统计量,因此不要将两者混淆在一个代码下。
相关资源
- FHIR 中的可穿戴设备数据:映射表和后端代码
- Health Connect 到 FHIR:Android 上的相同内容
- 如何构建个人健康记录应用
- 如何使用 Flutter 构建医疗保健应用
Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no card