Artikel

Eine Local-First-Brücke zwischen HealthKit und Health Connect mit Expo

Ein Entwurfsmuster, das Apple HealthKit und Google Health Connect über einen einzigen React-Hook zusammenführt – ohne plattformspezifische Bedingungen in der UI.

Eine Local-First-Brücke zwischen HealthKit und Health Connect mit Expo

Hallo zusammen! 👋 Wer schon einmal eine Gesundheits-, Fitness- oder Utility-Funktion in einer plattformübergreifenden React-Native-App entwickelt hat, kennt das Problem: Man entwickelt nicht nur eine Funktion, sondern muss sich mit zwei völlig unterschiedlichen, umfangreichen nativen Ökosystemen auseinandersetzen.

1. Der Einstieg

Unter iOS gibt es die ausgereifte Apple-HealthKit-API. Auf Android gibt es Health Connect, Googles einheitliches API-Framework als Ersatz für das ältere Google-Fit-SDK. Für die Trinkmengen-Erfassung meiner neuen App WaterPaw wollte ich eine vollständig modulare Lösung.

Plattformprüfungen wie Platform.OS === 'ios'sollten meine UI-Schicht nicht verkomplizieren. Ich wollte einen einzigen abstrakten React-Hook, der Verfügbarkeit, Berechtigungen und Datenabruf zentral behandelt. Dieses Entwurfsmuster verbindet beide Plattformen; Expo Config Plugins übernehmen dabei automatisch die native Konfiguration.

Zuerst definierte ich einen konkreten Bauplan (ein Interface), damit das Frontend unabhängig von der nativen Plattform bleibt:

export type HydrationData = {
  value: number; // in milliliters
  date: string;
};

export interface HealthBridge {
  isAvailable(): Promise<boolean>;
  requestPermissions(): Promise<boolean>;
  getHydrationData(date: Date): Promise<HydrationData[]>;
}

2. Konkrete Bridges

Anschließend kapselte ich die jeweiligen Implementierungsdetails in getrennten nativen Wrappern.

  • iOS-Wrapper: Verwendetreact-native-health , um den Initialisierungsstatus zu prüfen und die üblichen HealthKit-Berechtigungen anzufordern.
  • Android-Wrapper: Verwendetreact-native-health-connect mit dem erforderlichen zweistufigen Ablauf: erst die Initialisierung prüfen, dann separat die Berechtigungsanfrage starten.

3. Der Hook: dynamischer Plattform-Export

Der entscheidende Schritt ist, abhängig von der Plattform dynamisch die Bridge zu exportieren, sodass Komponenten einen einzigen Hook aufrufen können:

import { Platform } from 'react-native';
import { iOSHealthBridge } from './iOSHealthBridge';
import { AndroidHealthBridge } from './AndroidHealthBridge';

const HealthAPI = Platform.OS === 'ios' ? iOSHealthBridge : AndroidHealthBridge;

export function useHydrationSync() {
  const syncData = async () => {
    const available = await HealthAPI.isAvailable();
    if (!available) return;

    const hasPermission = await HealthAPI.requestPermissions();
    if (hasPermission) {
      const records = await HealthAPI.getHydrationData(new Date());
      // Commit directly to a local-first store (e.g. AsyncStorage or Zustand)
    }
  };

  return { syncData };
}

4. Die wichtige Expo-Besonderheit 💡

Da beide nativen Pakete stark angepassten nativen Code (Objective-C und Kotlin) verwenden, lassen sie sich nicht im normalen Expo Go kompilieren. Um das sauber zu umgehen, ohne manuell in Xcode oder Android Studio einzugreifen, müssen Config Plugins in der app.json deklariert werden. Health Connect erfordert mindestens SDK-Version 26:

{
  "expo": {
    "plugins": [
      ["react-native-health", { "NSHealthShareUsageDescription": "Sync your hydration records." }],
      ["expo-health-connect"],
      [
        "expo-build-properties",
        { "android": { "minSdkVersion": 26 } }
      ]
    ]
  }
}

Nach dieser Änderung erzeugt der Befehl npx expo prebuild --clean den nativen Code automatisch.

Warum der ganze Aufwand?

Bei minimalistischen Utility-Apps zählen Performance und eine reibungslose Bedienung. Mit dieser Architektur bleibt die UI bei 60 FPS flüssig, Aktualisierungen erfolgen sofort und Local First schützt die Daten der Nutzer vollständig. Keine Drittanbieter-Server, keine verpflichtenden Konten.

Mit dieser Struktur habe ich die intelligenten Erinnerungen und das Maskottchen-Fortschrittssystem von WaterPaw umgesetzt – meiner gerade veröffentlichten App!