# Apple Health (HealthKit) в FHIR

> Читайте данные Apple Watch и Apple Health с помощью HealthKit на Swift, за каждый локальный день, и записывайте их в запись FHIR R4: разрешения, фоновая синхронизация, правила Apple.

Source: https://developers.anpheros.com/ru/guides/apple-health-fhir

**Данные Apple Watch хранятся в Apple Health на сопряженном iPhone, и единственный способ их прочитать — это HealthKit, из приложения на этом iPhone, с разрешения пользователя.** Чтобы поместить их в запись FHIR, читайте ежедневную статистику с помощью `HKStatisticsCollectionQuery`, отправляйте значения на ваш бэкенд и записывайте каждую метрику как Observation с кодом LOINC и единицей измерения UCUM. На этой странице показан код на Swift (проверенный на соответствие SDK iOS 17), разрешения и правила, установленные Apple, а также как значения становятся FHIR R4 Observations.

## Что содержится в Apple Health

Apple Health собирает данные, которые измеряют iPhone и Apple Watch — частоту сердечных сокращений, частоту сердечных сокращений в покое, вариабельность сердечного ритма (SDNN), уровень кислорода в крови, частоту дыхания, шаги, расход энергии, стадии сна, уведомления о нерегулярном ритме, записи ЭКГ — а также данные, которые записывают другие приложения: Garmin Connect, Oura, Withings Health Mate, умные весы, тонометры, глюкометры. Таким образом, чтение данных Apple Health охватывает больше, чем только данные Apple Watch.

Сервера HealthKit или веб-API не существует. Данные остаются на устройстве (и в зашифрованной резервной копии iCloud пользователя); ваше приложение считывает их локально.

## Разрешения

1. Включите возможность **HealthKit** в Xcode и добавьте `NSHealthShareUsageDescription` в `Info.plist` (плюс `NSHealthUpdateUsageDescription`, только если вы пишете).
2. Запросите необходимые типы. Пользователи могут разрешить одни типы и отказаться от других, при этом **HealthKit не сообщает вашему приложению, был ли отклонен доступ на чтение**: отклоненный тип просто возвращает пустые данные. Разработайте экраны для случая "нет данных", а не для ошибки.
3. Запрашивайте разрешения в момент подключения пользователя, с пояснением причины, а не при первом запуске приложения.

## Чтение ежедневных значений в Swift

```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, где `effectivePeriod` объекта Observation представляет собой этот местный день с его смещением часового пояса.

## Поддержание актуальности записи

- **При открытии:** каждый раз, когда приложение выходит на передний план, считывайте количество дней с момента последней успешной синхронизации. Это надежная основа.
- **В фоновом режиме:** `HKObserverQuery` с `enableBackgroundDelivery(for:frequency:)` пробуждает ваше приложение при поступлении новых данных; для этого требуется разрешение HealthKit на фоновую доставку, а фактическое время определяет iOS.
- **Первое подключение:** импортируйте ограниченную историю (например, 30 дней), а не за годы, затем двигайтесь вперед.

## От значений Swift к FHIR Observations

Отправьте массив `DailyValue` на ваш бэкенд и запишите одно Observation для каждого человека, метрики и дня со стабильным идентификатором, обновляя его, когда наступает тот же день снова. Таблица сопоставления (тип HealthKit → LOINC → UCUM), проверенный пример JSON и код TypeScript, выполняющий это обновление (upsert), запускаемый на платформе, находятся в разделе [Wearable data in FHIR](https://developers.anpheros.com/ru/guides/wearable-data-fhir). Коды для двух метрик здесь:

| HealthKit | LOINC | UCUM | Значение за день |
|---|---|---|---|
| `heartRate` | 8867-4 Heart rate | `/min` | среднее, с минимумом и максимумом в качестве компонентов |
| `stepCount` | 41950-7 Number of steps in 24 hour | `{steps}` | общее |

## Правила Apple для медицинских данных

- Руководство по обзору приложений 5.1.3: медицинские данные не могут использоваться для рекламы или анализа данных, не могут передаваться третьим лицам без согласия пользователя, а личная медицинская информация не может храниться в iCloud.
- Ваша политика конфиденциальности должна указывать, что вы читаете и почему; Apple проверяет это.
- Данные, записываемые вашим приложением в Apple Health, приписываются вашему приложению; данные, которые вы читаете, сохраняют свой исходный источник (`HKSource`), который вы должны указывать в записи как исходную систему.

## Apple Health Records

Отдельно от данных о физической активности, Apple Health может хранить клинические записи, загруженные из участвующих медицинских систем (в основном в США), и HealthKit предоставляет их в виде объектов `HKClinicalRecord`, которые содержат исходный ресурс FHIR. Для их чтения требуются отдельные разрешения и права доступа. Это другая интеграция по сравнению с данными с носимых устройств, и Anpheros Daily в настоящее время их не считывает.

## Место Anpheros в системе

Платформа Anpheros хранит результат: запись FHIR R4 о человеке, где каждое значение с носимого устройства представлено как Observation, помеченное как данные устройства, согласие для других приложений, информация о происхождении (provenance) и журнал доступа. Anpheros не читает Apple Health напрямую — это происходит в вашем iOS-приложении. Собственное приложение Anpheros, **Anpheros Daily**, читает Apple Health на iPhone (планы Plus и Max) и записывает ежедневные сводки в запись с указанными выше кодами.

## Часто задаваемые вопросы

### Могу ли я читать данные Apple Watch с сервера?
Нет. Данные Apple Watch синхронизируются с Apple Health на сопряженном iPhone, и HealthKit может использоваться только приложением на этом устройстве. Приложение считывает данные и отправляет их на ваш сервер.

### Существует ли веб-API или REST API для Apple Health?
Нет. Apple не предлагает веб- или REST API для Apple Health: 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.

### Какой код LOINC соответствует вариабельности сердечного ритма Apple Watch?
Apple Health хранит HRV как SDNN, что соответствует LOINC 80404-7. Health Connect хранит RMSSD, другую статистику, поэтому не смешивайте их под одним кодом.

## Связанные материалы

- [Данные носимых устройств в FHIR](https://developers.anpheros.com/ru/guides/wearable-data-fhir): таблица соответствия и бэкенд-код
- [Health Connect в FHIR](https://developers.anpheros.com/ru/guides/health-connect-fhir): то же самое на Android
- [Как создать приложение для личной медицинской карты](https://developers.anpheros.com/ru/guides/personal-health-record-app)
- [Как создать медицинское приложение с использованием Flutter](https://developers.anpheros.com/ru/guides/healthcare-app-flutter)
