← Wszystkie wpisy

HealthKit w iOS 27: strefy treningowe, nowe typy

Przez lata HealthKit traktował treningowe strefy tętna jak nieoficjalnego obywatela: każda aplikacja do biegania i jazdy na rowerze, która rysowała pięciostrefowy wykres słupkowy, sama wyliczała te strefy — pobierała surowe odczyty tętna z HKQuantitySample, przypisywała je do przedziałów wyznaczonych przez zakodowane na sztywno albo wprowadzone przez użytkownika progi i ręcznie sumowała czas spędzony w każdym z nich. iOS 27 kończy z tym samodzielnym majsterkowaniem. Strefy stają się ustrukturyzowanym typem HealthKit, który system generuje sam, który dana osoba może edytować w ustawieniach Zdrowia, a który aplikacja odczytuje z już naliczonym czasem w strefie. Ta sama wersja dodaje dwa kategoryjne typy ze sfery zdrowia reprodukcyjnego — menopausalState i bleedingAfterMenopause — które domykają lukę w modelu śledzenia cyklu w HealthKit.1

Nowości w iOS 27 prowadzą dwa wątki o wspólnym kształcie. Wątek stref treningowych bierze obliczenia, które dawniej należały do aplikacji, i przenosi je do frameworka, gdzie raz zdefiniowana strefa danej osoby pozostaje spójna w każdej aplikacji, która o nią zapyta. Wątek nowych typów daje etapom życia, których HealthKit nie potrafił wcześniej przedstawić, typowaną próbkę kategoryjną. Oba podążają za istniejącą gramatyką HealthKit: typy ilościowe dla wartości mierzonych, typy kategoryjne dla zdarzeń i stanów. Ten wpis omawia każdy z nich w oparciu o dokumentację Apple, w stałym ujęciu charakterystycznym dla całego cyklu: co musi dodać aplikacja, która już korzysta z HealthKit, aby zyskać daną możliwość.

W skrócie

  • HKWorkoutZoneConfiguration definiuje kompletny zestaw stref dla typu ilościowego. System generuje strefy automatycznie na podstawie wskaźników zdrowotnych danej osoby, osoba ta może je edytować w ustawieniach Zdrowia, a aplikacja może dostarczyć własne strefy dla konkretnego treningu.2
  • HKWorkoutZoneGroup łączy konfigurację stref z danymi o czasie spędzonym w każdej strefie. Odczytuje się ją z instancji ukończonych treningów, aby uzyskać czasy trwania dla poszczególnych stref, które framework już policzył, zamiast samodzielnie przypisywać próbki tętna do przedziałów.3
  • preferredWorkoutZoneConfiguration(for:) na HKHealthStore zwraca preferowaną konfigurację stref danej osoby dla typu ilościowego, dzięki czemu wykresy w aplikacji pokrywają się z progami, które widzi ona wszędzie indziej.4
  • menopausalState wraz z wyliczeniem HKCategoryValueMenopausalState zapisuje stan menopauzalny w danym momencie; data początkowa i końcowa próbki muszą być identyczne, w przeciwnym razie zapis się nie powiedzie.56
  • bleedingAfterMenopause zapisuje krwawienie po menopauzie jako przedział z wartością intensywności, traktowane klinicznie odrębnie od krwawienia miesiączkowego.7

Strefy treningowe zawsze były na Twojej głowie

Przed iOS 27 aplikacja treningowa, która chciała pokazać strefy tętna, wykonywała całą robotę. Odpytywała dane tętna HKQuantitySample dla przedziału czasu treningu, decydowała, gdzie leżą granice stref (220 minus wiek, procenty progu mleczanowego albo wartość wpisana przez użytkownika na ekranie ustawień), przechodziła próbki po kolei i kumulowała sekundy spędzone w każdym przedziale. Każda aplikacja wybierała własną matematykę granic, więc osoba biegająca z dwiema aplikacjami widziała dwie różne „strefę 3”. Żadne dane stref nie znajdowały się w HealthKit, więc żadne z nich się nie synchronizowały ani nie były przenośne.

iOS 27 wprowadza HKWorkoutZoneConfiguration — strukturę, która definiuje kompletny zestaw stref dla typu ilościowego.2 Konfiguracja niesie uporządkowaną tablicę stref i wskazuje typ ilościowy, którego dotyczy. Liczy się to, skąd strefy pochodzą. Dokumentacja Apple stwierdza, że system generuje strefy automatycznie na podstawie wskaźników zdrowotnych danej osoby; osoba ta może skonfigurować strefy ręcznie w ustawieniach Zdrowia; albo aplikacja może dostarczyć własne strefy dla konkretnych treningów.2 Definicje stref przestają być prywatnym szczegółem każdej aplikacji i stają się współdzielonym stanem, który należy do frameworka.

import HealthKit

let heartRateType = HKQuantityType(.heartRate)

// A custom configuration an app supplies for one workout.
// makeIntervalZones is your own builder; construct the configuration
// from the ordered zones your workout uses, per HKWorkoutZoneConfiguration's
// declaration. The configuration identifies the quantity type the zones apply to.
let configuration: HKWorkoutZoneConfiguration = makeIntervalZones(for: heartRateType)

Towarzyszącą strukturą jest HKWorkoutZoneGroup, która zawiera konfigurację stref oraz dane o czasie spędzonym w strefie dla typu ilościowego.3 Grupa stref łączy HKWorkoutZoneConfiguration z danymi czasowymi dla poszczególnych stref, więc odczyt grupy daje zarówno granice, jak i to, ile czasu osoba spędziła w każdej z nich. Dokumentacja Apple opisuje dostęp do grup stref z instancji ukończonych treningów, aby pobrać dane stref dla zakończonych treningów, oraz z aktywnego źródła treningu, aby uzyskać informacje o strefach w czasie rzeczywistym podczas trwającej sesji.3 Framework zajmuje się przypisywaniem do przedziałów. Twój kod odczytuje wynik.

Watch on Apple Developer ↗
Apple pokazuje, jak dane stref płyną z ustawień Zdrowia danej osoby do aplikacji, a czas spędzony w każdej strefie wylicza HealthKit na podstawie napływających próbek.

W sesji 207 Apple stwierdza, że gdy strefy treningowe są zintegrowane z HealthKit, czas w każdej strefie jest wyliczany automatycznie z próbek napływających podczas treningu, więc aplikacja odczytuje wcześniej zsumowane czasy trwania z zoneGroupsByType zamiast samodzielnie przypisywać próbki tętna do przedziałów.8

import HealthKit

func summarize(_ group: HKWorkoutZoneGroup) {
    // The group bundles the configuration (the zone boundaries)
    // with the time-in-zone data the framework already computed.
    let configuration = group.configuration

    for zone in group.zones {
        // Render each zone's accumulated time. No sample-walking,
        // no manual bucketing — the durations arrive pre-summed.
        render(zone)
    }
}

Trzeci element wiąże konfigurację z osobą, a nie z aplikacją. preferredWorkoutZoneConfiguration(for:) to metoda instancyjna na HKHealthStore, która zwraca preferowaną konfigurację stref danej osoby dla wskazanego typu ilościowego:4

func preferredWorkoutZoneConfiguration(
    for quantityType: HKQuantityType
) async throws -> HKWorkoutZoneConfiguration?

Dokumentacja Apple precyzuje kontrakt zwracanej wartości: metoda zwraca ręcznie skonfigurowane strefy danej osoby z ustawień Zdrowia, albo strefy wygenerowane przez system, jeśli osoba nie ustawiła własnych wartości, albo nil, jeśli w ogóle nie skonfigurowała stref dla tego typu ilościowego.4 Strefy generowane przez system aktualizują się okresowo, w miarę jak zmieniają się wskaźniki zdrowotne danej osoby; strefy ustawione ręcznie pozostają stałe, dopóki osoba nie zmieni ich ponownie. Udokumentowanym zamysłem jest, by aplikacje wyświetlały informacje o strefach zgodne z preferencjami danej osoby w każdym treningu.4 Po tę metodę warto sięgnąć, gdy chce się, aby strefa 3 w aplikacji odpowiadała strefie 3, którą osoba widzi wszędzie indziej, zamiast wymyślać własną.

import HealthKit

let store = HKHealthStore()
let heartRateType = HKQuantityType(.heartRate)

func loadPreferredZones() async throws -> HKWorkoutZoneConfiguration? {
    // nil means the person has not configured zones for heart rate.
    // Fall back to your own defaults only in that case.
    try await store.preferredWorkoutZoneConfiguration(for: heartRateType)
}

Warto nazwać tę asymetrię. preferredWorkoutZoneConfiguration(for:) odczytuje stałą preferencję danej osoby, co rysuje spójne wykresy przed treningiem, w jego trakcie i po nim. Własna HKWorkoutZoneConfiguration, którą budujesz samodzielnie, obejmuje przypadek, gdy konkretny trening potrzebuje stref odmiennych od domyślnych ustawień osoby — na przykład ustrukturyzowana sesja interwałowa z własnymi przedziałami. Większość aplikacji chce konfiguracji preferowanej; ścieżka z konfiguracją własną jest dla sytuacji, gdy struktura treningu wymaga odrębnych stref.

Nowe typy kategoryjne: menopausalState i bleedingAfterMenopause

Model śledzenia cyklu w HealthKit obsługiwał miesiączkę, ale nie miał typowanego sposobu, by zapisać, na jakim etapie przejścia menopauzalnego znajduje się dana osoba, ani sposobu, by zapisać krwawienie występujące po ustaniu miesiączki. iOS 27 dodaje oba jako typy kategoryjne, podążając za tym samym wzorcem HKCategorySample, który wpis o HealthKit w iOS 26 omawiał dla sesji uważności i snu.

menopausalState to identyfikator typu kategoryjnego dla próbek, które zapisują stan menopauzalny danej osoby.5 Każda próbka to wpis dla pojedynczego momentu, a dokumentacja Apple narzuca twarde ograniczenie: data początkowa musi być równa dacie końcowej, a zapisanie próbki, w której obie się różnią, daje błąd.5 Wartością każdej próbki jest jeden z przypadków wyliczenia HKCategoryValueMenopausalState, które wskazuje stan menopauzalny w zapisanym momencie.6 Ponieważ lista przypadków z karty informacyjnej jest pominięta w omówieniu Apple, należy traktować to wyliczenie jako źródło prawdy co do prawidłowych wartości, zamiast zgadywać nazwy przypadków; udokumentowane zachowanie jest takie, że każda próbka przechowuje jeden taki przypadek w określonej dacie.6

import HealthKit

let store = HKHealthStore()
let menopausalType = HKCategoryType(.menopausalState)

func record(state: HKCategoryValueMenopausalState, on date: Date) async throws {
    // Point-in-time sample: start and end MUST be identical.
    // A mismatched start/end is a save error, by documented design.
    let sample = HKCategorySample(
        type: menopausalType,
        value: state.rawValue,
        start: date,
        end: date
    )
    try await store.save(sample)
}

Model pojedynczego momentu kształtuje sposób interpretacji danych. Dokumentacja Apple zauważa, że aplikacje mogą odczytać wiele próbek w czasie, aby wyprowadzić interpretacje wyższego rzędu: aktywne okresy, przejścia albo stan bieżący.6 Pojedyncza próbka stwierdza, że dany stan obowiązywał w danej dacie, a framework pozostawia aplikacji pracę nad złożeniem próbek w oś czasu. Apple ujmuje każdy wpis jako zmianę stanu, potwierdzenie, że dany stan obowiązywał w tej dacie, albo jedno i drugie — zależnie od tego, jak aplikacja odczytuje całą serię.5

bleedingAfterMenopause to identyfikator typu kategoryjnego dla próbek, które zapisują krwawienie po menopauzie.7 Rozróżnienie kliniczne jest powodem, dla którego istnieje on jako osobny typ. Po menopauzie miesiączka ustaje, więc krwawienie po menopauzie nie jest krwawieniem miesiączkowym ani krwawieniem międzymiesiączkowym; dokumentacja Apple nazywa je klinicznie odrębnym od obu.7 W odróżnieniu od próbki stanu menopauzalnego dla pojedynczego momentu, próbka bleedingAfterMenopause reprezentuje przedział krwawienia i przechowuje wartość intensywności.7

import HealthKit

let store = HKHealthStore()
let bleedingType = HKCategoryType(.bleedingAfterMenopause)

// `intensityValue` is the raw value of the intensity enum Apple defines for
// the bleedingAfterMenopause type; confirm the concrete case set in its declaration.
func record(intensityValue: Int, from start: Date, to end: Date) async throws {
    // An interval sample, not point-in-time: start and end differ,
    // and the value carries the bleeding intensity.
    let sample = HKCategorySample(
        type: bleedingType,
        value: intensityValue,
        start: start,
        end: end
    )
    try await store.save(sample)
}

Podział między tymi dwoma typami odzwierciedla podział, który wpis o iOS 26 wyznaczył między próbkami kategoryjnymi dla pojedynczego momentu a próbkami dla przedziału. menopausalState zwęża swoje okno do jednej chwili i niesie stan. bleedingAfterMenopause zachowuje realne okno [start, end] i niesie intensywność. Wybór złego kształtu dla któregokolwiek z nich to błąd zapisu albo pozbawiony sensu rekord, więc wybrany typ koduje naturę danych jeszcze zanim napiszesz linijkę kodu zapytania.

Ścieżka adopcji

Aplikacja treningowa lub zdrowotna, która już korzysta z HealthKit, dodaje powierzchnię iOS 27 stopniowo; nic z tego nie przepisuje instalacji autoryzacji ani zapisu próbek, którą omawiał wpis o wzorcach iOS 26.

  1. Usuń swoją matematykę stref. Każda aplikacja, która ręcznie wylicza strefy tętna, powinna wywołać preferredWorkoutZoneConfiguration(for:) dla odpowiedniego typu ilościowego i wyrenderować zwróconą konfigurację, sięgając po własne wartości domyślne tylko wtedy, gdy wywołanie zwróci nil.4 Twoje strefy odpowiadają teraz temu, co osoba widzi w całym systemie.
  2. Odczytuj HKWorkoutZoneGroup zamiast przypisywać próbki do przedziałów. Dla podsumowań ukończonych treningów pobierz grupę stref i wyrenderuj jej wstępnie policzone dane o czasie w strefie, zamiast samodzielnie przechodzić surowe próbki tętna i kumulować czasy trwania.3
  3. Dostarczaj własną HKWorkoutZoneConfiguration tylko wtedy, gdy trening potrzebuje własnych przedziałów. Ustrukturyzowane sesje interwałowe z indywidualnymi strefami to przypadek dla konfiguracji własnej; wszystko inne korzysta z konfiguracji preferowanej przez osobę.2
  4. Dodaj nowe typy kategoryjne do swojego żądania autoryzacji. Aplikacja do śledzenia cyklu dodaje menopausalState i bleedingAfterMenopause do swoich zbiorów udostępniania i odczytu, a następnie zapisuje menopausalState jako próbki dla pojedynczego momentu (start równy końcowi), a bleedingAfterMenopause jako próbki przedziałowe z intensywnością.57
  5. Zweryfikuj daty początku i końca dla każdego typu. Zapis stanu menopauzalnego się nie powiedzie, jeśli start i koniec się różnią; próbka krwawienia jest przedziałem i oczekuje, że będą się różnić. Ustaw właściwy kształt okna, zanim wdrożysz ścieżkę zapisu.57

FAQ

Czy w iOS 27 nadal muszę samodzielnie wyliczać strefy tętna?

Nie, nie w typowym przypadku. preferredWorkoutZoneConfiguration(for:) zwraca preferowaną konfigurację stref danej osoby dla typu ilościowego (jej ręcznie skonfigurowane strefy z ustawień Zdrowia, albo strefy wygenerowane przez system, albo nil, jeśli żadnych nie skonfigurowała), a HKWorkoutZoneGroup niesie dane o czasie w strefie, które framework już policzył dla treningu.34 Strefy wyliczasz samodzielnie tylko wtedy, gdy konkretny trening potrzebuje własnych przedziałów, odmiennych od domyślnych ustawień osoby — wówczas budujesz HKWorkoutZoneConfiguration dla tego treningu.2

Czym różni się HKWorkoutZoneConfiguration od HKWorkoutZoneGroup?

HKWorkoutZoneConfiguration definiuje kompletny zestaw stref dla typu ilościowego: uporządkowane strefy oraz typ ilościowy, którego dotyczą.2 HKWorkoutZoneGroup zawiera zarówno tę konfigurację, jak i dane o czasie w strefie dla typu ilościowego, więc grupa mówi Ci, jakie są granice i ile czasu osoba spędziła w każdej strefie.3 Grupy stref odczytujesz z instancji ukończonych treningów dla zakończonych sesji oraz z aktywnego źródła dla informacji o strefach w czasie rzeczywistym.3

Co zwraca preferredWorkoutZoneConfiguration(for:), gdy osoba nigdy nie ustawiła stref?

Zwraca strefy wygenerowane przez system, jeśli osoba nie ustawiła własnych wartości, a nil tylko wtedy, gdy w ogóle nie skonfigurowała stref dla danego typu ilościowego.4 Gdy osoba ustawiła strefy ręcznie w ustawieniach Zdrowia, metoda zwraca te właśnie strefy i pozostają one stałe, dopóki osoba ich nie zmieni; strefy wygenerowane przez system aktualizują się okresowo wraz ze zmianami jej wskaźników zdrowotnych.4 Rozgałęź kod na nil, aby dostarczyć własne wartości awaryjne.

Dlaczego zapis próbki menopausalState kończy się błędem daty?

Ponieważ menopausalState zapisuje stan w pojedynczym momencie, framework wymaga, by data początkowa i końcowa próbki były identyczne, a próbka, której daty się różnią, daje błąd przy zapisie.5 Ustaw obie na tę samą Date. To ograniczenie odróżnia ją od bleedingAfterMenopause, która reprezentuje przedział i oczekuje, że start i koniec będą się różnić.7

Czym bleedingAfterMenopause różni się od krwawienia miesiączkowego?

Po menopauzie miesiączka ustaje, więc krwawienie po menopauzie jest klinicznie odrębne od krwawienia miesiączkowego i od krwawienia międzymiesiączkowego; z tego powodu Apple nadaje mu osobny typ kategoryjny.7 Każda próbka bleedingAfterMenopause przechowuje przedział i wartość intensywności, a do powiązanego śledzenia stanu używasz typu menopausalState.7

Pełny cykl o ekosystemie Apple znajduje się obok tego wpisu: wzorce autoryzacji HealthKit i typów próbek w iOS 26, na których ten wpis się opiera; cykl życia treningu w watchOS, gdzie dane stref powstają podczas aktywnej sesji; kontrakt środowiska wykonawczego watchOS, który rządzi wykonywaniem w tle na nadgarstku; oraz trzy powierzchnie aplikacji iOS, które umieszczają HealthKit jako warstwę źródła danych. Centrum całości to cykl o ekosystemie Apple. Szerszy kontekst iOS z agentami AI znajdziesz w przewodniku o tworzeniu aplikacji iOS z agentami.

Źródła


  1. Dokumentacja Apple Developer: HealthKit. Dokumentacja frameworka obejmująca typy ilościowe, typy kategoryjne, próbki oraz dodane w iOS 27 strefy treningowe i elementy ze sfery zdrowia reprodukcyjnego. 

  2. Dokumentacja Apple Developer: HKWorkoutZoneConfiguration (iOS 27.0). „A structure that defines a complete set of zones for a quantity type.” Zadeklarowana jako struct HKWorkoutZoneConfiguration; zawiera uporządkowaną tablicę stref, wskazuje swój typ ilościowy i obsługuje strefy generowane przez system, skonfigurowane przez osobę lub dostarczone przez aplikację. 

  3. Dokumentacja Apple Developer: HKWorkoutZoneGroup (iOS 27.0). „A structure that contains zone configuration and time-in-zone data for a quantity type.” Zadeklarowana jako struct HKWorkoutZoneGroup; łączy konfigurację z danymi o czasie w strefie, dostępna z instancji ukończonych treningów oraz z aktywnych źródeł treningu. 

  4. Dokumentacja Apple Developer: preferredWorkoutZoneConfiguration(for:) (iOS 27.0). „Returns someone’s preferred zone configuration for the specified quantity type.” Zadeklarowana jako func preferredWorkoutZoneConfiguration(for quantityType: HKQuantityType) async throws -> HKWorkoutZoneConfiguration?; zwraca strefy skonfigurowane ręcznie, strefy wygenerowane przez system albo nil

  5. Dokumentacja Apple Developer: menopausalState (iOS 27.0). „An identifier for samples that record a person’s menopausal state.” Zadeklarowana jako static let menopausalState: HKCategoryTypeIdentifier; próbki dla pojedynczego momentu wymagają identycznych dat początkowej i końcowej, inaczej zapis się nie powiedzie. 

  6. Dokumentacja Apple Developer: HKCategoryValueMenopausalState (iOS 27.0). „A value that indicates the menopausal state at a recorded point in time.” Zadeklarowana jako enum HKCategoryValueMenopausalState; aplikacje mogą odpytać wiele próbek w czasie, aby wyprowadzić aktywne okresy, przejścia lub stan bieżący. 

  7. Dokumentacja Apple Developer: bleedingAfterMenopause (iOS 27.0). „An identifier for samples that record bleeding after menopause.” Zadeklarowana jako static let bleedingAfterMenopause: HKCategoryTypeIdentifier; klinicznie odrębne od krwawienia miesiączkowego, każda próbka to przedział z wartością intensywności. 

  8. Apple, sesja WWDC26 nr 207, „Deliver workout insights with HealthKit workout zones.” developer.apple.com/videos/play/wwdc2026/207. Gdy strefy tętna i mocy w kolarstwie są zintegrowane z HealthKit w iOS 27, czas w każdej strefie jest wyliczany automatycznie na podstawie próbek napływających podczas treningu, a aplikacja odczytuje go ze słownika zoneGroupsByType na HKWorkout lub HKWorkoutActivity

Powiązane artykuły

SwiftData w iOS 27: Observation i historia

iOS 27 daje SwiftData pełnoprawną obserwację zmian dzięki ResultsObserver, obserwację trwałej historii dzięki HistoryObs…

10 min czytania

Co nowego w SwiftUI w systemie iOS 27

iOS 27 przebudowuje listy, dokumenty, paski narzędzi i błędy w SwiftUI: zmiana kolejności przeciąganiem, model dokumentu…

20 min czytania

The Robots Are Taking Exams in My Search Console

First-party GSC data: 91% of 3.8M impressions fail a human-query filter. Exam questions, pasted errors, and agent sweeps…

10 min czytania