HealthKit + SwiftUI auf iOS 26: Autorisierung, Sample-Typen und plattformübergreifende Muster aus zwei ausgelieferten Apps
HealthKit gehört zu den schwierigeren Apple-Frameworks, die in einer SwiftUI-App korrekt ausgeliefert werden müssen. Der Autorisierungsablauf birgt eine Falle mit transienten Fehlern, die Benutzer dauerhaft aussperren kann. Die Sample-Typen teilen sich unbeholfen zwischen quantitativen Daten (HKQuantitySample für Wasseraufnahme, Schritte, Kalorien) und kategorischen Daten (HKCategorySample für Achtsamkeitssitzungen, Schlaf, Menstruationsfluss) auf. Die asynchrone API-Oberfläche erfordert das Umhüllen callback-basierter HealthKit-Aufrufe in withCheckedThrowingContinuation. Und auf watchOS verschieben sich die Muster erneut.
Ich habe HealthKit in zwei Produktions-Apps ausgeliefert: Water (Wasseraufnahme-Tracking, ~192-zeiliger HealthKitService)1 und Return (Protokollierung von Achtsamkeitssitzungen, ~171-zeiliger HealthKitManager plus ein 155-zeiliger HealthKitPermissionSheet).2 Zusammen decken sie beide Hauptformen von HealthKit-Samples, beide Richtungen (Lesen + Schreiben vs. Nur-Schreiben) und sowohl Single-Platform- als auch plattformübergreifende Bereitstellung ab.
Dieser Aufsatz führt durch die Muster, die in der Produktion überlebt haben: Pre-Permission-UX, der SwiftUI-Modifier .healthDataAccessRequest vs. die alte API requestAuthorization, der Async-Wrapper für Sample-Abfragen und die watchOS-spezifischen Unterschiede.
TL;DR
- Die Berichterstattung zum Autorisierungsstatus ist asymmetrisch. Die API von Apple meldet zuverlässig „Schreiben autorisiert”, sagt Ihnen aus Datenschutzgründen aber nicht „Lesen verweigert”. Der Artikel behandelt, wie man „Benutzer wurde gefragt, hat aber nicht gewährt” erkennt, ohne den Lesestatus abzuleiten.
- Quantitative Daten verwenden
HKQuantitySamplemitHKQuantityTypeundHKUnit(Water nutzt.literUnit(with: .milli)für die Wasseraufnahme). Kategorische Daten verwendenHKCategorySamplemitHKCategoryType(Return nutzt.mindfulSession). - Das Pre-Permission-Sheet ist das am häufigsten übersprungene Muster. Apples System-Berechtigungsdialog ist steril; eine zuerst gezeigte benutzerdefinierte
Viewerklärt den Nutzen und verbessert die Zustimmungsraten dramatisch. - watchOS-HealthKit benötigt eine separate
HKHealthStore-Instanz, hat strengere Regeln für erneute Autorisierungsanfragen und kann kein SwiftUI-Sheet für Pre-Permission-UX anzeigen. - Das Muster
recordAuthorizationAttempt()in Return verhindert, dass transiente Präsentationsfehler als dauerhafte Verweigerung behandelt werden.
Die zwei Sample-Formen
HealthKit teilt sein Sample-Modell entlang einer Achse, die jede Codezeile beeinflusst, die Sie schreiben:3
| Sample-Form | Typische Verwendung | API |
|---|---|---|
HKQuantitySample |
Wasser, Schritte, Kalorien, Körpergewicht, Herzfrequenz | HKQuantityType + HKUnit + HKQuantity |
HKCategorySample |
Achtsamkeitssitzungen, Schlaf, Menstruationsfluss, sexuelle Aktivität | HKCategoryType + HKCategoryValue |
HKWorkout |
strukturiertes Training (Laufen, Schwimmen) | HKWorkoutBuilder, HKWorkoutSession |
Wasser ist eine Quantität. Echter Produktionscode aus HealthKitService.swift:1
import HealthKit
@Observable
final class HealthKitService {
static let shared = HealthKitService()
private let healthStore = HKHealthStore()
private let waterType = HKQuantityType(.dietaryWater)
func logWater(amount: Double, date: Date = .now) async throws -> UUID {
guard isAuthorized else { throw HealthKitError.notAuthorized }
let quantity = HKQuantity(unit: .literUnit(with: .milli), doubleValue: amount)
let sample = HKQuantitySample(
type: waterType,
quantity: quantity,
start: date,
end: date,
metadata: [HKMetadataKeyWasUserEntered: true]
)
try await healthStore.save(sample)
return sample.uuid
}
}
Drei Details aus der Produktion:
.literUnit(with: .milli)ist die kanonische Einheit für Wasser in Millilitern. HealthKit akzeptiert jede beliebige Einheit (US-Flüssigunzen, Liter), aber der Konstruktor ist wichtig, weil Apple intern alles auf Liter normalisiert. Die Wahl von.millierlaubt Ihnen, Ganzzahlwerte (240, 500) statt fraktionaler Liter (0,240, 0,500) zu speichern.HKMetadataKeyWasUserEntered: truemarkiert das Sample als manuell eingegeben statt gemessen. Die Health-App zeigt einen kleinen Hinweis „manuell eingegeben” auf diesen Samples; der Benutzer vertraut manuell eingegebenen Daten anders als waagengemessenen Daten.startundendsind dasselbeDatefür Sofort-Samples. Quantitäten akkumulieren über ein[start, end]-Fenster, aber bei „Ich habe gerade 240 ml getrunken” kollabiert das Fenster zu einem Punkt.
Eine Achtsamkeitssitzung ist kategorisch. Echter Produktionscode aus Returns HealthKitManager.swift:2
import HealthKit
@MainActor
class HealthKitManager {
static let shared = HealthKitManager()
let healthStore = HKHealthStore()
let mindfulType = HKCategoryType(.mindfulSession)
func saveMindfulSession(start: Date, end: Date) async -> Bool {
guard isAvailable else { return false }
guard end > start else { return false }
let sample = HKCategorySample(
type: mindfulType,
value: HKCategoryValue.notApplicable.rawValue,
start: start,
end: end
)
// ... save via healthStore.save(sample) ...
}
}
Zwei Details:
HKCategoryValue.notApplicable.rawValueist der tragende Sentinel-Wert. Achtsamkeitssitzungen haben keinen sinnvollen „Wert” (sie sind Dauer-Markierungen), daher verlangt HealthKit einen Sentinel-Kategoriewert (Int(0)), damit das Feld dem Typsystem genügt. Andere Kategorie-Samples haben reichhaltigere Werte: Schlaf nutztHKCategoryValueSleepAnalysis.asleepusw.- Das Fenster
start/endist bei Kategorie-Samples real. Eine 10-minütige Achtsamkeitssitzung hatstartundendmit 10 Minuten Abstand, und die tägliche Achtsamkeitsminuten-Bilanz von HealthKit summiert diese Dauern.
Der Autorisierungsablauf (und seine Falle)
HealthKit-Autorisierung ist absichtlich asymmetrisch. authorizationStatus(for:) meldet den Schreib-/Freigabestatus wahrheitsgemäß (.sharingAuthorized, .sharingDenied, .notDetermined), aber Lesegenehmigung oder -verweigerung ist über diese API nicht direkt beobachtbar. Apple verbirgt den Lesestatus absichtlich, um zu verhindern, dass Apps daraus ableiten, welche Daten im Health-Profil eines Benutzers existieren.4 Sie erfahren die Lese-Entscheidung indirekt: Abfragen liefern Daten zurück, wenn der Benutzer Lesen gewährt hat, und Abfragen liefern leere Ergebnisse, wenn der Benutzer Lesen verweigert hat. Es gibt kein „Lesen wurde verweigert”-Signal, auf das Ihr Code verzweigen könnte.
Beide Apps umgehen dies unterschiedlich.
Water liest seine eigenen Samples. Da Water Wassereinträge sowohl schreibt als auch liest (um den „heutigen Verlauf” zu befüllen), muss sein Autorisierungsablauf den Fall „Lese-Verweigerung-ist-unsichtbar” handhaben:1
private var typesToShare: Set<HKSampleType> { [waterType] }
private var typesToRead: Set<HKSampleType> { [waterType] }
func requestAuthorization() async throws {
guard isHealthDataAvailable else { throw HealthKitError.notAvailable }
try await healthStore.requestAuthorization(toShare: typesToShare, read: typesToRead)
await MainActor.run { checkAuthorizationStatus() }
}
func checkAuthorizationStatus() {
authorizationStatus = healthStore.authorizationStatus(for: waterType)
isAuthorized = authorizationStatus == .sharingAuthorized
}
Beachten Sie, was checkAuthorizationStatus NICHT tut: nach dem Status der Lese-Autorisierung fragen. Water prüft nur den Schreib-Status. Wenn der Benutzer Schreiben gewährt, aber Lesen verweigert, zeigt Waters Benutzeroberfläche „keine Einträge” an, weil Lesen leer zurückkehrt (nicht wegen eines expliziten Fehlers). Water vertraut der Schreib-Entscheidung und lässt das Fehlen von Daten für sich selbst sprechen. Der Benutzer kann es in den Einstellungen beheben, wenn es ihm wichtig ist.
Return schreibt nur. Return protokolliert Achtsamkeitssitzungen, liest sie aber nie zurück; die angezeigte Sitzungsliste stammt aus dem eigenen NSUbiquitousKeyValueStore, nicht aus HealthKit. Daher ist Returns Autorisierungsanfrage nur-schreibend:2
func requestAuthorization() async -> Bool {
guard isAvailable else { return false }
do {
try await healthStore.requestAuthorization(
toShare: [mindfulType],
read: [] // We only need write access
)
hasRequestedHealthKit = authorizationStatus != .notDetermined
return isAuthorizedToWrite()
} catch {
hasRequestedHealthKit = authorizationStatus != .notDetermined
return false
}
}
Die leere Menge read: [] ist beabsichtigt. Wenn Sie Lesezugriff anfordern, den Sie nicht benötigen, erweitert das den Berechtigungsumfang, den Sie im App Review rechtfertigen müssen, und verwirrt Benutzer, die sehen „Return möchte Ihre Achtsamkeitssitzungen lesen”, obwohl die App das offensichtlich nicht braucht.
Die Falle. Beide Apps konvergierten auf dasselbe defensive Muster: einen Autorisierungsversuch nur dann als „abgeschlossen” markieren, wenn sich der Status tatsächlich über .notDetermined hinaus bewegt hat. Der naive Code ist:
hasRequestedHealthKit = true // ❌ wrong: treats transient failures as denial
Das korrekte Muster aus Return:2
hasRequestedHealthKit = authorizationStatus != .notDetermined // ✓ checks real state
Warum das wichtig ist: Der SwiftUI-Modifier .healthDataAccessRequest und die zugrunde liegende API requestAuthorization können unter bestimmten Bedingungen den Systemdialog nicht präsentieren (Sheet-Konflikte, Unterbrechungen im View-Lebenszyklus, transienter OS-Zustand). Wenn Sie den Versuch als „abgeschlossen” markieren, bevor Sie den tatsächlichen Status prüfen, sperren Sie Benutzer in einem Zustand, in dem Ihre App denkt, sie hätten abgelehnt, obwohl niemals ein Systemdialog erschienen ist. Sie haben keinen Weg zurück, es sei denn, sie gehen in Einstellungen → Datenschutz → Health und gewähren manuell, woran sie nicht denken werden, weil sie Ihre Anfrage nie gesehen haben. Returns recordAuthorizationAttempt() existiert speziell für diesen Fall.
Das Pre-Permission-Sheet
Apples HealthKit-Berechtigungsdialog ist korrekt, aber unverkauft. Er zeigt eine Liste von Typen an, die Ihre App freigeben oder lesen möchte, mit Schaltern. Es gibt keinen Kontext für das Warum die App die Daten möchte, keine Erklärung der Vorteile, keine App-Marke. Benutzer tippen auf „Nicht erlauben”, weil die Anfrage dekontextualisiert ist.
Return liefert ein HealthKitPermissionSheet, das vor dem Systemdialog erscheint. Das Sheet zeigt das Return-App-Symbol neben dem Apple-Health-Symbol (HIG-konform: das Apple-Health-Symbol darf nicht beschnitten oder beschattet werden),5 nennt den Vorteil („Track Your Practice” / „Save your meditation sessions as Mindful Minutes in Apple Health”), listet drei Vorteilszeilen auf („See your practice in Apple Health”, „Syncs across all your devices”, „Works with other wellness apps”) und endet mit einer einzigen Vorwärts-Schaltfläche Continue, die die System-Anfrage auslöst. Echte Struktur aus HealthKitPermissionSheet.swift:6
struct HealthKitPermissionSheet: View {
var onEnableRequested: () -> Void
var theme: Theme
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 16) {
Image("ReturnAppIcon")
.resizable().scaledToFit().frame(width: 80, height: 80)
.clipShape(RoundedRectangle(cornerRadius: 18))
Text("+").font(.title)
Image("AppleHealthIcon")
.resizable().scaledToFit().frame(width: 80, height: 80)
// No clipShape; Apple HIG forbids altering the Health icon
}
// Title + subtitle + benefits list
// (See discussion below for the production comment.)
Button { onEnableRequested() } label: {
Text("Continue")
}
}
.interactiveDismissDisabled(true)
}
}
Das interactiveDismissDisabled(true) zusammen mit dem Fehlen einer Abbrechen-Schaltfläche ist eine bewusste Designentscheidung. Apples App-Review-Richtlinie 5.1.1 verlangt von Apps, die Datenschutzeinstellungen der Benutzer zu respektieren, und verbietet das Manipulieren, Täuschen oder Erzwingen von Zustimmung.10 Der Kommentar im Produktionscode über der Schaltfläche Continue lautet:
Single forward action: the system HealthKit dialog owns the real yes/no choice. Apple Guideline 5.1.1(iv): the priming screen may not include any exit/dismiss path that bypasses the system permission request.
Diese Formulierung ist strenger als der wörtliche Richtlinientext (der von Respekt vor der Zustimmung und davor spricht, sie nicht zu erzwingen, aber keine Priming-Screen-UX in diesen Worten vorschreibt). Es ist Returns Interpretation, kodifiziert im Quellcode. Die Absicht: Ein Benutzer, der ablehnt, soll dies auf Apples Bildschirm tun, nicht auf Returns. Das Ergebnis ist ein Sheet mit einer Vorwärtsaktion und keinem Notausgang. Der Text und die Vorteilszeilen müssen den Tap verdienen; es gibt keinen „Ich denk drüber nach”-Zweig.
Der Ablauf:
- Benutzer tippt auf „Connect Health” in Returns Einstellungen.
- Das Pre-Permission-Sheet erscheint. Der Benutzer liest die Erklärung.
- Benutzer tippt auf Continue. Das Sheet bleibt eingehängt, während
requestAuthorizationausgeführt wird und der Systemdialog erscheint. - Benutzer akzeptiert (oder lehnt ab) im Systemdialog. Return ruft
recordAuthorizationAttempt()auf, um das Ergebnis zu erfassen, und das Sheet wird geschlossen.
Warum die Mühe. Apple hat keine offiziellen Zahlen zur Zustimmungsraten-Steigerung durch Pre-Permission-Sheets veröffentlicht, aber jeder iOS-Entwickler, mit dem ich gesprochen habe, der sowohl einen A/B-Test als auch die Geduld hatte, ihn durchzuführen, hat dieselbe Richtung berichtet: Pre-Permission-Sheets erhöhen die Zustimmungsrate für Schreib-Autorisierungen dramatisch. Das Muster ist mittlerweile so verbreitet, dass Apples eigene Vorlagen (Photos, Camera, Location) zunehmend ihre eigene Pre-Permission-UX enthalten.
Die watchOS-Variante
watchOS-HealthKit teilt dieselbe API-Oberfläche wie iOS-HealthKit (HKHealthStore, Sample-Typen, Autorisierung), allerdings mit drei strukturellen Unterschieden:
- Ein neuer
HKHealthStorepro Watch-App. Die Watch-App und die gepaarte iPhone-App haben jeweils eigeneHKHealthStore-Instanzen. Beide können in die HealthKit-Datenbank des Benutzers schreiben, beide können ihre eigenen Samples lesen. Der Store wird nicht zwischen dem Paar geteilt. - Keine SwiftUI-Sheets für Pre-Permission. watchOS-View-Hierarchien unterstützen Sheets nicht so wie iOS. Die Pre-Permission-UX muss ein Vollbild sein.
- Strengere Regeln für erneute Anfragen. Der watchOS-Aufruf
requestAuthorizationist konservativer beim erneuten Präsentieren des Systemdialogs, wenn der Benutzer zuvor abgelehnt hat; möglicherweise müssen Sie Benutzer auf die Watch-App auf ihrem iPhone verweisen, um die Einstellung zu ändern, da die Uhr selbst keine Einstellungen → Datenschutz → Health-Benutzeroberfläche hat.
Echter Produktionscode aus WatchHealthKitManager.swift:7
@MainActor
class WatchHealthKitManager {
static let shared = WatchHealthKitManager()
let healthStore = HKHealthStore()
let mindfulType = HKCategoryType(.mindfulSession)
private init() {}
var isAvailable: Bool { HKHealthStore.isHealthDataAvailable() }
/// Returns true if the system request completed (success or already-authorized).
/// Caller checks isAuthorizedToWrite() separately for the actual share state.
func requestAuthorization() async -> Bool {
guard isAvailable else { return false }
do {
try await healthStore.requestAuthorization(toShare: [mindfulType], read: [])
return true
} catch {
return false
}
}
func isAuthorizedToWrite() -> Bool {
guard isAvailable else { return false }
return healthStore.authorizationStatus(for: mindfulType) == .sharingAuthorized
}
// ... saveMindfulSession identical to iOS variant ...
}
Die Aufteilung ist beabsichtigt: requestAuthorization meldet, ob der Systemaufruf erfolgreich war, und isAuthorizedToWrite meldet das Ergebnis, das der Benutzer gewählt hat. Diese auf der Uhr aufzuteilen, macht den Code leichter nachvollziehbar; auf iOS zeigt sich die äquivalente Aufteilung als Paar recordAuthorizationAttempt() plus isAuthorizedToWrite() auf HealthKitManager.
Die Watch-Klasse ist etwa halb so groß wie die iOS-Klasse, weil sie nicht benötigt:
- Das
hasRequestedHealthKit-UserDefaults-Flag (die Watch-UX hat weniger Pfade für einen zweiten Versuch). - Die
HealthKitPermissionSheet-Verdrahtung (keine Sheet-Benutzeroberfläche auf watchOS). - Die Methode
recordAuthorizationAttempt()(der schmalere Watch-Ablauf hat weniger Grenzfälle für transiente Fehler).
Der Kompromiss ist, dass die watchOS-App manchmal einen Zustand „verweigert ohne Ausweg” für Benutzer trifft, die den Systemdialog abgelehnt haben und nicht erkennen, dass sie es über die Watch-App auf dem iPhone beheben können. Return zeigt in diesem Fall eine kleine In-App-Anweisung („Watch-App auf iPhone öffnen → Meine Watch → Datenschutz → Health”), anstatt zu versuchen, erneut anzufragen.
Was ich anders bauen würde
Drei Lehren aus dem Ausliefern von HealthKit in zwei Produktions-Apps.
Beide APIs funktionieren; die Wahl hängt vom Präsentationskontext ab. SwiftUIs Modifier .healthDataAccessRequest umhüllt die alte API in einer deklarativeren Form und behandelt den Präsentationskontext auf iPadOS korrekt. Return verwendet den Modifier und stellt seine Freigabe-Typen über eine öffentliche Eigenschaft mindfulShareTypes auf HealthKitManager bereit, sodass eine SwiftUI-Elternansicht ihn verdrahten kann. Water verwendet die alte healthStore.requestAuthorization direkt, weil Waters Autorisierungsablauf aus einem Nicht-View-Kontext (einem @Observable-Service) läuft. Die Aufteilung ist ein nützliches Muster: Bevorzugen Sie den Modifier, wenn die Anfrage aus einem SwiftUI-Lebenszyklusereignis stammt (Tap auf eine Schaltfläche innerhalb eines Sheets), und greifen Sie auf die alte API zurück, wenn die Anfrage aus einem Service stammt.
Pre-Permission-Sheets sind den Engineering-Aufwand auf iOS wert, auf watchOS nicht. Das Pre-Permission-Sheet fügt vielleicht vier Stunden Arbeit hinzu (Sheet-View, Text, Theming, Integration). Der Anstieg der Zustimmungsrate auf iOS ist groß genug, dass die vier Stunden eine offensichtliche Investition sind. Auf watchOS ist das äquivalente Vollbild-Pre-Permission aufdringlicher (es übernimmt den gesamten Watch-Bildschirm, anstatt ein Sheet zu sein), der Benutzer liest auf einem kleinen Bildschirm seltener langen Text, und der Watch-UX-Ablauf hat weniger Einstiegspunkte, an denen der Benutzer eine Funktion anfordert, die HealthKit erfordert. Ich habe Return ohne dieses Element auf watchOS ausgeliefert und es nicht bereut.
Verfolgen Sie hasRequestedHealthKit getrennt vom Live-Autorisierungsstatus. Die HealthKit-API teilt Ihnen den aktuellen Autorisierungsstatus mit, sagt Ihnen aber nicht, ob Sie jemals gefragt haben. Die Unterscheidung ist wichtig, weil davon das richtige Verhalten beim zweiten Tap abhängt: Der erste Tap sollte requestAuthorization aufrufen; der zweite Tap sollte, wenn der Benutzer zuvor abgelehnt hat, einen Hinweis „Einstellungen → Datenschutz → Health” anzeigen, anstatt die API erneut aufzurufen (die bei einem verweigerten Zustand stillschweigend nichts tut). Das hasRequestedHealthKit-UserDefaults-Flag ist das, was den zweiten Tap nützlich macht.
Wann HealthKit nicht zu verwenden ist
Verzicht ist Teil des Designs.
Schreiben Sie nicht in HealthKit, nur weil Sie es können. Eine Fokus-Timer-App muss kein Workout schreiben. Eine Notiz-App muss keine Achtsamkeit protokollieren. HealthKit hinzuzufügen, weil es eine „kostenlose Integration” ist, erweitert Ihren Datenschutz-Footprint, Ihre Autorisierungs-Reibung und den Umfang Ihres App-Review-Fragebogens, ohne dem Benutzer materiellen Wert zu bieten.
Lesen Sie HealthKit nicht, wenn Sie es vermeiden können. Lesezugriff ist im App Review schwerer zu rechtfertigen und Benutzern schwerer zu erklären. Viele Apps, die HealthKit lesen, könnten nur schreiben und Benutzer ihre Daten in Apple Health einsehen lassen; der Lese-Ablauf verdoppelt die Oberfläche bei vernachlässigbarem UX-Gewinn.
Verwenden Sie HealthKit auf Mac nicht für reine Cross-Device-Daten. macOS unterstützt HealthKit seit macOS 13, aber die meisten Health-Daten entstehen auf iPhone oder Apple Watch. Wenn Ihre Mac-App dieselben Daten benötigt, schreiben Sie auf dem iPhone in HealthKit und lassen Sie Apples geräteübergreifende Synchronisation sie auf dem Mac sichtbar machen. Direkte HealthKit-Schreibvorgänge vom Mac sind gültig, in der Praxis aber selten.
Liefern Sie nicht aus, ohne den Pfad „abgelehnt-dann-widerrufen” zu testen. Benutzer erteilen die Erlaubnis und widerrufen sie dann in den Einstellungen → Datenschutz → Health. Ihre App muss den Widerruf elegant handhaben, normalerweise durch Anzeigen der Einstellungs-Deeplink-Anweisung, wenn der Benutzer das nächste Mal versucht, die Funktion zu nutzen. Sowohl Water als auch Return liefern diesen Pfad aus; keine der beiden Apps hat es beim ersten Versuch richtig gemacht.
Was das für SwiftUI-Apps bedeutet, die HealthKit auf iOS 26+ ausliefern
Drei Erkenntnisse.
- Entscheiden Sie sich vor dem UI-Design für nur-schreibend vs. lesen+schreiben. Nur-schreibend ist eine kleinere Oberfläche und ein schnelleres App Review. Lesen+Schreiben ist flexibler, fügt aber die Asymmetrie „Lese-Verweigerung-ist-unsichtbar” hinzu.
- Liefern Sie ein Pre-Permission-Sheet auf iOS aus. Der Anstieg der Zustimmungsrate ist real. Verwenden Sie Apples Symbol korrekt (kein Beschneiden, kein Schatten), nennen Sie den Vorteil und rufen Sie dann die System-API auf.
- Behandeln Sie watchOS-HealthKit als kleinere, einfachere Variante des iOS-Musters. Weniger Zeremonie, keine Sheets, einzweck-orientierte Autorisierung. Verweisen Sie Benutzer für die erneute Erteilung auf die Watch-App auf dem iPhone.
Lesen Sie diesen Beitrag zusammen mit meinen früheren Artikeln zur selben App-Familie: typisierte App Intents für Apple Intelligence; MCP-Server für plattformübergreifende LLM-Agenten; Liquid-Glass-Muster für die visuelle Schicht; plattformübergreifendes Ausliefern für Cross-Device-Reichweite. HealthKit ist die Datenquellen-Schicht, die unter der visuellen Schicht und den Integrationsoberflächen sitzt.8
FAQ
Kann ich Autorisierung zwischen einer iOS-App und ihrer watchOS-Erweiterung teilen?
Nein. Der iOS-HKHealthStore und der watchOS-HKHealthStore sind unabhängig. Der Benutzer erteilt die Autorisierung auf jeder Plattform separat über separate Systemdialoge. Ihr Code prüft auf jeder Seite seinen eigenen Autorisierungsstatus; Sie können den iOS-Status nicht von der Uhr aus oder umgekehrt lesen.
Was passiert mit meinen Samples, wenn der Benutzer den Schreibzugriff widerruft?
Vorhandene Samples bleiben in HealthKit. Der Benutzer kann sie manuell aus der Health-App löschen, wenn er möchte, aber das Widerrufen des Zugriffs Ihrer App stoppt nur neue Schreibvorgänge. Ihre App kann keine Samples mehr speichern oder ändern, aber die historischen Daten des Benutzers bleiben erhalten.
Ist der SwiftUI-Modifier .healthDataAccessRequest für den Produktionseinsatz sicher?
Er funktioniert in iOS 17.4+ und wurde in nachfolgenden Releases verfeinert. Return verwendet den Modifier (er stellt mindfulShareTypes auf HealthKitManager für SwiftUI bereit). Water verwendet die alte healthStore.requestAuthorization direkt, weil Waters Anfrage aus einem Nicht-View-@Observable-Service stammt. Wählen Sie basierend darauf, wo die Anfrage initiiert wird: SwiftUI-Lebenszyklusereignis → Modifier; Service oder Nicht-View-Kontext → alte API.
Warum sagt der HealthKit-Autorisierungsstatus .sharingAuthorized, obwohl ich nie um Schreibzugriff gebeten habe?
Tut er nicht. Der Status ist pro HKObjectType. Wenn Sie authorizationStatus(for: heartRateType) prüfen und nie nach Herzfrequenz gefragt haben, erhalten Sie .notDetermined. Der Status wechselt erst nach erfolgreicher Autorisierung für diesen spezifischen Typ zu .sharingAuthorized.
Benötige ich ein Privacy-Manifest für HealthKit?
Ja. Apps, die HealthKit berühren, müssen die Verwendung in PrivacyInfo.xcprivacy (dem Privacy-Manifest) und im Datenschutz-Fragebogen von App Store Connect deklarieren. Die relevanten Einträge sind NSHealthShareUsageDescription und NSHealthUpdateUsageDescription in Info.plist, plus die entsprechenden Deklarationen im Privacy-Manifest.9
Referenzen
-
Produktionscode in
Water/Water/Services/HealthKitService.swift(192 Zeilen). Water des Autors, eine SwiftUI-Wassertracking-App, verfügbar auf iOS, iPadOS, macOS, watchOS und visionOS. VerwendetHKQuantitySamplemitHKQuantityType(.dietaryWater)und das FlagHKMetadataKeyWasUserEntered. ↩↩↩ -
Produktionscode in
Return/Return/HealthKitManager.swift(171 Zeilen). Return des Autors, eine SwiftUI-Meditationstimer-App, verfügbar auf iOS, iPadOS, macOS, watchOS und tvOS. VerwendetHKCategorySamplemitHKCategoryType(.mindfulSession)undHKCategoryValue.notApplicable.rawValue. ↩↩↩↩ -
Apple Developer, „HealthKit framework”. Die zwei Hauptproben-Typen des Frameworks sind
HKQuantitySample(für messbare Mengen wie Wasser, Schritte, Kalorien) undHKCategorySample(für nicht-quantitative Ereignisse wie Achtsamkeitssitzungen, Schlaf, Menstruationsfluss).HKWorkoutdeckt strukturiertes Training ab. ↩ -
Apple Developer, „Authorizing access to health data”. Apple verbirgt absichtlich den Status der Lese-Verweigerung, um zu verhindern, dass Apps daraus ableiten, welche Daten im Health-Profil eines Benutzers existieren. Die Methode
authorizationStatus(for:)liefert nur für den Schreibzugriff ehrliche Ergebnisse. ↩ -
Apple Developer, „Apple Health icon usage” Human Interface Guidelines. Apples Health-Symbol darf nicht modifiziert, beschnitten, beschattet oder umgefärbt werden; reproduzieren Sie es 1:1 in werblichen und Pre-Permission-Benutzeroberflächen. ↩
-
Produktionscode in
Return/Return/HealthKitPermissionSheet.swift(155 Zeilen). Pre-Permission-View, der vor dem Auslösen des System-HealthKit-Dialogs angezeigt wird. Paart das Return-App-Symbol mit dem Apple-Health-Symbol, erklärt den Vorteil und löst die Autorisierung über einen Callback-Closure beim Tap des Benutzers aus. ↩ -
Produktionscode in
Return/ReturnWatch Watch App/WatchHealthKitManager.swift(86 Zeilen). Die watchOS-Variante vonHealthKitManager. EigenständigerHKHealthStore, keinhasRequestedHealthKit-Flag, keine Permission-Sheet-Verdrahtung. ↩ -
Analyse des Autors: HealthKit ist die Datenquellen-Schicht einer iOS-App, App Intents sind die System-KI-Oberfläche, MCP ist die plattformübergreifende LLM-Agenten-Oberfläche, Liquid Glass ist die visuelle Oberfläche. Die vier Schichten setzen sich zu einem einzigen ausgelieferten Produkt über alle fünf Apple-Plattformen hinweg zusammen. ↩
-
Apple Developer, „Privacy manifest files”. HealthKit-Verwendung muss in
PrivacyInfo.xcprivacyplus den SchlüsselnNSHealthShareUsageDescriptionundNSHealthUpdateUsageDescriptioninInfo.plistdeklariert werden. ↩ -
Apple Developer, „App Review Guideline 5.1.1”. Apps müssen die Datenschutzeinstellungen der Benutzer respektieren, vor dem Sammeln personenbezogener Daten Zustimmung einholen und dürfen Zustimmung nicht manipulieren, täuschen oder erzwingen. Die genaue Formulierung über Ausgangspfade in Priming-Bildschirmen in diesem Artikel spiegelt Returns Interpretation wider, nicht den wörtlichen Richtlinientext. ↩