Bonjour à tous ! 👋 Si vous avez déjà essayé de créer une fonctionnalité de santé, de fitness ou d’utilitaire dans une app React Native multiplateforme, vous connaissez le problème : il ne s’agit pas de développer une seule fonctionnalité, mais de composer avec deux écosystèmes natifs lourds et entièrement distincts.
1. Pour commencer
Sur iOS, on dispose de l’API éprouvée Apple HealthKit. Sur Android, Health Connect est le framework unifié de Google qui a remplacé l’ancien SDK Google Fit. Pour créer le suivi de l’hydratation de ma nouvelle app WaterPaw, je voulais une solution entièrement modulaire.
Je ne voulais pas que des vérifications de plateforme comme Platform.OS === 'ios'encombrent l’interface. Je voulais un hook React personnalisé et abstrait qui gère disponibilité, autorisations et récupération des données au même endroit. Voici le modèle utilisé pour unifier les deux plateformes, avec les Expo Config Plugins pour automatiser la configuration native.
J’ai d’abord défini un contrat concret (une interface) afin que le frontend ignore la plateforme native utilisée :
export type HydrationData = {
value: number; // in milliliters
date: string;
};
export interface HealthBridge {
isAvailable(): Promise<boolean>;
requestPermissions(): Promise<boolean>;
getHydrationData(date: Date): Promise<HydrationData[]>;
}2. Bridges concrets
J’ai ensuite isolé les détails propres à chaque implémentation dans des wrappers natifs distincts.
- Wrapper iOS : Basé sur
react-native-healthpour vérifier l’initialisation et transmettre les autorisations HealthKit habituelles. - Wrapper Android : Basé sur
react-native-health-connectavec le flux obligatoire en deux étapes : vérifier l’initialisation, puis lancer séparément la demande d’autorisation.
3. Le hook : export dynamique selon la plateforme
La solution consiste à exporter dynamiquement le bridge selon la plateforme afin que les composants appellent un seul hook personnalisé :
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. La particularité importante d’Expo 💡
Les deux packages natifs utilisent du code fortement personnalisé (Objective-C et Kotlin) et ne peuvent donc pas être compilés dans Expo Go standard. Pour éviter cela sans intervenir manuellement dans Xcode ou Android Studio, il faut déclarer des config plugins dans app.json. Health Connect exige une version minimale du SDK de 26 :
{
"expo": {
"plugins": [
["react-native-health", { "NSHealthShareUsageDescription": "Sync your hydration records." }],
["expo-health-connect"],
[
"expo-build-properties",
{ "android": { "minSdkVersion": 26 } }
]
]
}
}Après cette mise à jour, la commande npx expo prebuild --clean génère automatiquement le code natif.
Pourquoi se donner tout ce mal ?
Pour les utilitaires minimalistes, les performances et la simplicité d’utilisation sont essentielles. Avec cette architecture, l’interface reste fluide à 60 FPS, les mises à jour sont instantanées et l’approche local-first protège entièrement les données des utilisateurs. Aucun serveur tiers, aucune inscription obligatoire.
J’ai utilisé cette structure pour les rappels intelligents et la progression de la mascotte de WaterPaw, que je viens de lancer !
