Ciao a tutti! 👋 Se hai mai provato a sviluppare una funzione di salute, fitness o utilità in un’app React Native multipiattaforma, conosci il problema: non stai scrivendo un unico insieme di funzionalità, stai lottando con due ecosistemi nativi completamente separati e pesanti.
1. Punto di partenza
Su iOS c’è la matura API Apple HealthKit. Su Android c’è Health Connect, il framework API unificato di Google che ha sostituito il vecchio SDK di Google Fit. Quando sviluppavo l’architettura di monitoraggio dell’idratazione per la mia nuova app, WaterPaw, volevo una soluzione completamente modulare.
Non volevo controlli condizionali di piattaforma (Platform.OS === 'ios') a disturbare il livello dell’interfaccia. Volevo un unico hook React personalizzato e astratto che gestisse disponibilità, permessi e recupero dei dati in un solo punto. Ecco il design pattern pulito che ho usato per unificarli, sfruttando gli Expo Config Plugin per gestire automaticamente le configurazioni native.
Per prima cosa ho definito un modello concreto (interfaccia) in modo che il frontend non si preoccupi della piattaforma nativa su cui gira:
export type HydrationData = {
value: number; // in milliliters
date: string;
};
export interface HealthBridge {
isAvailable(): Promise<boolean>;
requestPermissions(): Promise<boolean>;
getHydrationData(date: Date): Promise<HydrationData[]>;
}2. Bridge concreti
Poi ho separato i dettagli esatti dell’implementazione in wrapper nativi isolati.
- Wrapper iOS: Basato su
react-native-healthper verificare lo stato di inizializzazione e passare le matrici di permessi standard di HealthKit. - Wrapper Android: Basato su
react-native-health-connectcon il flusso di esecuzione in due passaggi richiesto (verifica dell’inizializzazione seguita da un contratto separato per l’intent dei permessi).
3. L’hook: esportazione dinamica per piattaforma
La magia avviene esportando il bridge in modo dinamico in base alla piattaforma, così i componenti possono richiamare un unico hook personalizzato:
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. Il grande tranello di Expo 💡
Poiché entrambi i pacchetti nativi usano codice nativo molto personalizzato (Objective-C e Kotlin), non si compilano all’interno del normale Expo Go. Per aggirare il problema in modo pulito, senza armeggiare manualmente in Xcode o Android Studio, devi dichiarare i config plugin nel tuo app.json. Nota che Health Connect richiede esplicitamente un SDK minimo 26:
{
"expo": {
"plugins": [
["react-native-health", { "NSHealthShareUsageDescription": "Sync your hydration records." }],
["expo-health-connect"],
[
"expo-build-properties",
{ "android": { "minSdkVersion": 26 } }
]
]
}
}Dopo aver aggiornato il file, eseguire npx expo prebuild --clean gestisce automaticamente la generazione nativa.
Perché prendersi tutto questo disturbo?
Quando si sviluppano utility minimaliste, prestazioni e zero attriti sono tutto. Con questa architettura l’interfaccia resta fluida a 60 FPS, gli aggiornamenti sono istantanei e, essendo local-first, l’app rispetta pienamente la privacy dei dati dell’utente. Nessun server di terze parti, nessuna registrazione obbligatoria.
Ho usato questa logica strutturale per alimentare i promemoria intelligenti e i sistemi di progressione della mascotte all’interno di WaterPaw, che ho appena lanciato!
