# Apple Health (HealthKit) do FHIR

> Odczytaj dane z Apple Watch i Apple Health za pomocą HealthKit w Swift, dla lokalnego dnia, i zapisz je do rekordu FHIR R4: uprawnienia, synchronizacja w tle, zasady Apple.

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

**Dane z Apple Watch znajdują się w Apple Health na sparowanym iPhonie, a jedynym sposobem na ich odczytanie jest HealthKit, z aplikacji na tym iPhonie, za zgodą użytkownika.** Aby umieścić je w rekordzie FHIR, odczytaj dzienne statystyki za pomocą `HKStatisticsCollectionQuery`, wyślij wartości do swojego backendu i zapisz każdą metrykę jako Observation z kodem LOINC i jednostką UCUM. Ta strona pokazuje kod Swift (sprawdzony pod kątem typu z SDK iOS 17), uprawnienia i zasady ustalone przez Apple oraz sposób, w jaki wartości stają się FHIR R4 Observations.

## Co znajduje się w Apple Health

Apple Health gromadzi dane mierzone przez iPhone'a i Apple Watch — tętno, tętno spoczynkowe, zmienność rytmu serca (SDNN), poziom tlenu we krwi, częstość oddechów, kroki, zużycie energii, fazy snu, powiadomienia o nieregularnym rytmie, zapisy EKG — oraz dane zapisywane przez inne aplikacje: Garmin Connect, Oura, Withings Health Mate, inteligentne wagi, monitory ciśnienia krwi, glukometry. Odczytując dane z Apple Health, uzyskujesz dostęp do szerszego zakresu informacji niż tylko te z Apple Watch.

Nie ma serwera HealthKit ani interfejsu API sieciowego. Dane pozostają na urządzeniu (oraz w zaszyfrowanej kopii zapasowej iCloud użytkownika); Twoja aplikacja odczytuje je lokalnie.

## Uprawnienia

1. Włącz funkcję **HealthKit** w Xcode i dodaj `NSHealthShareUsageDescription` do `Info.plist` (oraz `NSHealthUpdateUsageDescription`, jeśli piszesz).
2. Poproś o potrzebne typy. Użytkownicy mogą zezwolić na niektóre, a odmówić innych, a **HealthKit nie informuje Twojej aplikacji, czy odmówiono dostępu do odczytu**: odmówiony typ po prostu zwraca brak danych. Zaprojektuj ekrany dla scenariusza "brak danych", a nie dla błędu.
3. Poproś w momencie, gdy użytkownik się połączy, z wyjaśnieniem, dlaczego, a nie przy pierwszym uruchomieniu.

## Odczytywanie dziennych wartości w 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
}
```

Dlaczego zapytania statystyczne zamiast odczytywania próbek: `HKStatisticsCollectionQuery` zwraca dzienne sumy i średnie, które Apple Health już zdeduplikowało między źródłami, dzięki czemu kroki policzone zarówno przez iPhone'a, jak i zegarek nie są dodawane dwukrotnie. Odczytywanie surowych próbek i samodzielne ich sumowanie jest najczęstszym źródłem zawyżonych liczników kroków.

Dni to lokalne dni kalendarzowe osoby (`Calendar.current`). Zachowaj je w ten sposób aż do rekordu FHIR, gdzie `effectivePeriod` obiektu Observation to ten lokalny dzień z przesunięciem strefy czasowej.

## Utrzymanie aktualności rekordu

- **Po otwarciu:** odczytaj liczbę dni od ostatniej udanej synchronizacji za każdym razem, gdy aplikacja wraca na pierwszy plan. Jest to niezawodna podstawa.
- **W tle:** `HKObserverQuery` z `enableBackgroundDelivery(for:frequency:)` budzi aplikację po nadejściu nowych danych; wymaga to uprawnienia HealthKit do dostarczania danych w tle, a iOS decyduje o faktycznym czasie.
- **Pierwsze połączenie:** zaimportuj ograniczoną historię (na przykład 30 dni) zamiast lat, a następnie przesuwaj się do przodu.

## Od wartości Swift do obserwacji FHIR

Wyślij tablicę `DailyValue` do swojego zaplecza i zapisz jedną obserwację dla każdej osoby, metryki i dnia ze stabilnym identyfikatorem, aktualizując ją, gdy ten sam dzień nadejdzie ponownie. Tabela mapowania (typ HealthKit → LOINC → UCUM), zweryfikowany przykład JSON oraz kod TypeScript wykonujący tę operację upsert, uruchomiony na platformie, znajdują się w [Dane z urządzeń noszonych w FHIR](https://developers.anpheros.com/pl/guides/wearable-data-fhir). Kody dla dwóch metryk tutaj to:

| HealthKit | LOINC | UCUM | Wartość na dzień |
|---|---|---|---|
| `heartRate` | 8867-4 Heart rate | `/min` | średnia, z minimum i maksimum jako komponentami |
| `stepCount` | 41950-7 Number of steps in 24 hour | `{steps}` | suma |

## Zasady firmy Apple dotyczące danych o stanie zdrowia

- Wytyczna dotycząca przeglądu aplikacji 5.1.3: dane o stanie zdrowia nie mogą być wykorzystywane do celów reklamowych ani analizy danych, nie mogą być udostępniane stronom trzecim bez zgody osoby, a osobiste informacje o stanie zdrowia nie mogą być przechowywane w iCloud.
- Twoja polityka prywatności musi określać, co odczytujesz i dlaczego; Apple to sprawdza.
- Dane zapisane przez Twoją aplikację w Apple Health są przypisywane Twojej aplikacji; dane, które odczytujesz, zachowują swoje oryginalne źródło (`HKSource`), które powinieneś uwzględnić w rekordzie jako system źródłowy.

## Apple Health Records

Oprócz danych dotyczących kondycji, Apple Health może przechowywać dane kliniczne pobrane z uczestniczących systemów opieki zdrowotnej (głównie w Stanach Zjednoczonych), a HealthKit udostępnia je jako obiekty `HKClinicalRecord`, które zawierają oryginalny zasób FHIR. Odczytanie ich wymaga osobnego uprawnienia i zgody. Jest to inna integracja niż dane z urządzeń noszonych, a Anpheros Daily ich dzisiaj nie odczytuje.

## Gdzie Anpheros się mieści

Platforma Anpheros przechowuje wynik: rekord FHIR R4 osoby, z każdą wartością z urządzenia noszonego jako Observation oznaczoną jako dane urządzenia, zgodą na inne aplikacje, pochodzeniem danych i dziennikiem dostępu. Nie odczytuje bezpośrednio Apple Health — dzieje się to w Twojej aplikacji iOS. Własna aplikacja Anpheros, **Anpheros Daily**, odczytuje Apple Health na iPhonie (plany Plus i Max) i zapisuje codzienne podsumowania do rekordu z powyższymi kodami.

## Często zadawane pytania

### Czy mogę odczytywać dane z Apple Watch z serwera?
Nie. Dane z Apple Watch są synchronizowane z Apple Health na sparowanym iPhonie, a HealthKit może być używany tylko przez aplikację na tym urządzeniu. Aplikacja odczytuje dane i wysyła je na Twój serwer.

### Czy istnieje internetowe lub REST API dla Apple Health?
Nie. Apple nie oferuje internetowego ani REST API dla Apple Health: HealthKit działa tylko wewnątrz aplikacji na iPhonie (lub Apple Watch) użytkownika. Aby używać danych na serwerze, Twoja aplikacja odczytuje je za zgodą użytkownika i wysyła, na przykład jako FHIR Observations do Anpheros.

### Czy potrzebuję klucza API lub płacę za Apple Health API?
Żaden klucz ani opłata: HealthKit jest częścią iOS. Potrzebujesz możliwości HealthKit w swojej aplikacji, opisu użycia dla każdego typu danych i zgody użytkownika; recenzja App Store sprawdza, jak używasz danych.

### Skąd mam wiedzieć, czy użytkownik odmówił dostępu do typu HealthKit?
Nie możesz, w przypadku dostępu do odczytu: HealthKit ukrywa to w celu ochrony prywatności, a odmówiony typ nie zwraca żadnych danych. Traktuj "brak danych" jako normalny stan i zaoferuj sposób na przeglądanie uprawnień w aplikacji Zdrowie.

### Dlaczego moje liczniki kroków są wyższe niż w aplikacji Zdrowie?
Prawdopodobnie sumujesz surowe próbki z kilku źródeł (iPhone i Watch). Użyj `HKStatisticsCollectionQuery` z `.cumulativeSum`, które stosuje własne usuwanie duplikatów Apple Health.

### Który kod LOINC odpowiada zmienności tętna Apple Watch?
Apple Health przechowuje HRV jako SDNN, co odpowiada LOINC 80404-7. Health Connect przechowuje RMSSD, inną statystykę, więc nie mieszaj tych dwóch pod jednym kodem.

## Powiązane

- [Dane z urządzeń noszonych w FHIR](https://developers.anpheros.com/pl/guides/wearable-data-fhir): tabela mapowania i kod backendowy
- [Health Connect do FHIR](https://developers.anpheros.com/pl/guides/health-connect-fhir): to samo na Androida
- [Jak zbudować osobistą kartę zdrowia](https://developers.anpheros.com/pl/guides/personal-health-record-app)
- [Jak zbudować aplikację medyczną we Flutterze](https://developers.anpheros.com/guides/healthcare-app-flutter)
