Schritte, Distanz, Kalorien, Herzfrequenz und Schlaf müssen von der Plattform des Benutzers (Android Health Connect auf Android, Apple HealthKit auf iOS) von einer Gesundheits- und Ernährungs-App abgerufen und als ein sauberes, dedupliziertes Tagesprofil dargestellt werden. Es gibt sehr wenige Ähnlichkeiten zwischen den beiden APIs: separate Lese-Mechanismen, unterschiedliche Autorisierungsschemata und verschiedene Datenstrukturen. Wir haben die mobile Synchronisierungsschicht entwickelt, die sie wie eine Einheit erscheinen lässt.
Die Herausforderung
- Zwei native APIs, die sich über nichts einig sind. Apple HealthKit gibt tägliche Aggregationen über Callbacks zurück; Health Connect gibt Rohdaten, paginierte Samples mit umfangreichen Quell-Metadaten zurück. Gleiche Konzepte, völlig unterschiedliche Formen – und die App musste beide in ein einziges Schema normalisieren.
- Doppelte Zählung ist der Standard. Ein Telefon, eine Smartwatch und eine Drittanbieter-App können alle die gleichen 8.000 Schritte melden. Eine naive Summierung bläht jede Metrik auf. Die App musste überlappende Quellen erkennen und jede reale Aktivität nur einmal zählen.
- Akku und Netzwerk können die Aktualität nicht bezahlen. Gesundheitsdaten ändern sich den ganzen Tag über, aber das Auslesen der vollständigen Historie bei jeder Synchronisierung würde den Akku entladen und Mobilfunkverbindungen überlasten. Die Synchronisierungskadenz musste leicht bleiben und sich dennoch live anfühlen.
Berechtigungen können verwirrend sein. Dutzende von Datentypen, zwei Berechtigungsmodelle, Einschränkungen beim Hintergrundlesen und Benutzer, die einige Bereiche gewähren, andere aber nicht – all das muss die App handhaben, ohne den Ablauf zum Absturz zu bringen.
Unsere Lösung
Wir haben eine dreischichtige Synchronisierung entwickelt: Eine dünne native Schicht liest die API jeder Plattform, eine Normalisierungsschicht auf dem Gerät dedupliziert und fasst die Daten zu täglichen Zusammenfassungen zusammen, und das Backend speichert und gleicht sie in einem einzigen vertrauenswürdigen Ledger ab. Der Client versendet niemals Rohdaten – er versendet saubere, zeitzonenkorrekte tägliche Rollups.

Architektur
- iOS liest Apple HealthKit ĂĽber react-native-health; Android liest Health Connect ĂĽber react-native-health-connect.
- Die Normalisierung auf dem Gerät wandelt die Ausgabe beider Plattformen in ein einziges Schema um und dedupliziert, bevor etwas das Telefon verlässt.
- Synchronisierungs-Trigger — 30 Tage Historie beim ersten Start, dann leichte inkrementelle Lesezugriffe für 1 Tag in einem 10-Minuten-Intervall und bei jeder App-Wiederaufnahme.
- Transport — normalisierte tägliche Rollups werden per POST an den Hauptserver unter /user-health/system-activity gesendet, der sie (mit user ID + timezone) an den Health Microservice weiterleitet.
- Persistenz — jede Quelle/jeder Tag wird idempotent als HealthSystemAggregate gespeichert; ein MongoDB change stream gleicht sie dann in einem pro Tag geführten HealthLedger ab.
Expo / React Native managed workflow mit nativen HealthKit- und Health Connect-Modulen.
Hauptmerkmale
- Ein Normalisierungsvertrag für zwei APIs. Plattformspezifische Reader speisen einen einzigen formatHealthData-Schritt, der unabhängig von der Quelle die gleiche Form ausgibt – sodass Backend und UI nie zwischen iOS und Android unterscheiden müssen.
2. Maximale Deduplizierung auf dem Gerät. Innerhalb jeder Quelle werden die Messwerte eines Tages summiert; der Tageswert ist dann der Math.max()-Wert über alle Quellen hinweg (Kalorien, die nach source|deviceType verschlüsselt sind), sodass sich mehrere Messwerte von einem Gerät ansammeln, während eine Uhr und ein Telefon, die denselben Tag melden, sich nicht verdoppeln:
// Sum readings within each source, then take the max ACROSS sources dateSourceMap[date][source] += entry.count; const totalSteps = Math.ceil(Math.max(...Object.values(dateSourceMap[date]))); |
3. Die richtige Lesestrategie pro Plattform. Health Connect-Lesezugriffe sind mit einer pageToken-Schleife (1.000 Datensätze/Seite) paginiert; HealthKits tägliche Aggregationen werden Tag für Tag durchlaufen. Jede Besonderheit ist in ihrem eigenen Reader enthalten, unsichtbar für den Rest der App.
4. Eine Synchronisierungskadenz, die den Akku schont. Eine vollständige 30-Tage-Nachfüllung läuft nur einmal (geschützt durch ein First-Call-Flag); danach ruft jede Synchronisierung nur noch einen Tag ab – schnell über Mobilfunk, energiesparend.
5. Zeitzonenkorrekte tägliche Gruppierung. Samples werden in YYYY-MM-DD-Schlüssel in der Zeitzone des Benutzers gruppiert, sodass ein Training um 23 Uhr am richtigen Tag landet, egal wo der Server steht.
6. Idempotente Backend-Schreibvorgänge. Jedes Rollup führt ein Upsert auf (userId, deviceId, source, date) durch – das erneute Senden desselben Tages ist ein No-Op, wodurch Wiederholungen und überlappende Synchronisierungen sicher sind.
7. Ehrliche Degradation. Fehlende Berechtigungen, leere Lesezugriffe und Health Connect-Rate-Limit-Fehler werden abgefangen und sauber angezeigt – ein leerer Tag bringt die Synchronisierung nie zum Absturz, und der Benutzer erhält eine klare Aufforderung anstelle eines stillen Fehlers.
Ergebnisse
- Ein einziges, konsistentes Gesundheitsprofil über iOS und Android hinweg – der Plattformunterschied ist für den Rest der App unsichtbar.
- Überlappende Geräte-/App-Quellen werden dedupliziert, sodass Schritte und Kalorien die Realität widerspiegeln und nicht aufgeblähte Summen.
- Leichte inkrementelle Synchronisierungen halten die Daten aktuell, ohne den Akku zu entleeren oder mobiles Datenvolumen zu verbrauchen.
Zeitzonenkorrekte, idempotente Schreibvorgänge bedeuten, dass jede Metrik am richtigen Tag landet und Wiederholungen den Datensatz niemals beschädigen.
Technologie-Stack
React Native (Expo) · react-native-health (HealthKit) · react-native-health-connect · NestJS · MongoDB · MongoDB Change Streams · moment-timezone

