La maggior parte delle guide React Native si fermano quando “funziona sull’emulatore”. Il problema è che in produzione — su un vero telefono, con iOS che gestisce aggressivamente la memoria e Android che varia secondo il produttore — le tue attività in background si fermano. Silenziosamente. Senza log. Senza errore visibile.
Risultato: sincronizzazione dei dati che non avviene più, notifiche che non arrivano, fetch che fallisce dopo 3 giorni di utilizzo. E un utente che disinstalla.
Ecco cosa 15 anni di costruzione di applicazioni web e mobili ci hanno insegnato — e quello che la documentazione ufficiale non documenta abbastanza.
Perché le attività in background falliscono davvero (e non è il tuo codice)
Il primo errore che fanno la maggior parte degli sviluppatori: cercare il bug nel proprio codice. Spesso, il codice è corretto. È il sistema operativo che lavora contro di te.
iOS e Android hanno filosofie radicalmente opposte sulla gestione dei processi in background. iOS è draconiano: l’applicazione non ha alcuna garanzia di esecuzione al di fuori delle finestre esplicitamente concesse dal sistema. Android è teoricamente più permissivo, ma i layer dei produttori (Samsung, Xiaomi, Huawei) aggiungono i propri strati di ottimizzazione della batteria che uccidono i processi senza preavviso.
Quello che vediamo concretamente con i nostri clienti che sviluppano app React Native:
- Un task
BackgroundFetchche funziona in sviluppo, ma si ferma dopo 72 ore in produzione su iOS - Un servizio di geolocalizzazione in background che si ferma non appena un utente Xiaomi attiva la “modalità risparmio energetico”
- Worker che girano perfettamente su un Pixel (Android puro), ma muoiono su un Galaxy S24
La documentazione React Native menziona questi vincoli. Non ti dice come aggirarli in modo robusto.
I tre approcci — e perché due ti deluderanno
BackgroundFetch: utile, ma limitato
react-native-background-fetch è la soluzione più documentata. Funziona su iOS e Android, è semplice da configurare, e ti darà false speranze.
Su iOS, l’intervallo minimo è di 15 minuti — ed è un suggerimento, non una garanzia. Il sistema decide quando eseguire il task in base alla cronologia di utilizzo dell’app, al livello della batteria e alla connessione di rete. In pratica, i tuoi task vengono eseguiti tra 15 minuti e diverse ore dopo l’orario previsto.
Non è un bug. È il comportamento documentato di iOS.
Per sincronizzazioni non critiche (aggiornamento di contenuto, precaricamento di dati), è accettabile. Per tutto ciò che deve essere puntuale — pagamenti, avvisi, aggiornamento dello stock — è insufficiente.
Headless JS Tasks su Android: potenti, fragili
Android permette tramite HeadlessJS di eseguire JavaScript anche quando l’app è in background. È più affidabile di BackgroundFetch sui dispositivi Android “puri”. Il problema arriva con i layer dei produttori.
Ecco cosa non ti dicono: i telefoni Xiaomi, Oppo e Huawei rappresentano una quota significativa del mercato. E tutti questi produttori hanno sistemi di gestione della batteria che uccidono i processi in background, a meno che l’utente non vada manualmente nelle impostazioni a deselezionare “ottimizzazione batteria” per la tua app.
Quanti utenti lo fanno? Pochissimi.
Le silent push notification: la vera soluzione per il tempo reale
Se la tua necessità è scatenare un’azione precisa in un momento preciso, smetti di combattere contro le limitazioni delle attività in background. Usa le silent push notification.
Il principio: il tuo server invia una push notification senza contenuto visibile (silent push notification). L’app si sveglia, esegue il suo task, torna a dormire. iOS garantisce 30 secondi di esecuzione. Android è più generoso.
È l’architettura usata da WhatsApp, Slack e dalla maggior parte delle app critiche. Non perché sia elegante — perché funziona in produzione.
Lo stack che funziona davvero in produzione
Dopo aver testato diversi approcci su progetti reali, ecco cosa raccomandiamo in base al caso d’uso.
Per la sincronizzazione periodica non critica (aggiornamento di contenuto, statistiche, log): Usa react-native-background-fetch con una strategia di retry. Non assumere mai che il task sia stato eseguito. Verifica lato server, e ri-sincronizza all’apertura dell’app se necessario.
Per le azioni scatenate dal server (notifiche, aggiornamenti di dati critici): Silent push notification via Firebase Cloud Messaging (FCM) su Android, APNs su iOS. Testa imperativamente su dispositivi reali, non solo su simulatore.
Per la geolocalizzazione continua: react-native-background-geolocation di Transistor Software è il riferimento. È a pagamento (circa 400$/anno per un’app commerciale). È anche l’unica lib che gestisce correttamente i casi limite iOS/Android in produzione. Se la tua app dipende dalla geoloc, è un investimento, non un costo.
“L’affidabilità di un’app mobile si misura da ciò che accade quando nessuno guarda — quando lo schermo è spento, la batteria al 15%, e l’utente in zona di segnale debole.” — Principio che applichiamo sistematicamente in audit mobile
Gli errori di configurazione che costano caro
Ecco dove diventa interessante — e dove la maggior parte dei progetti deraglia.
Dimenticare le capabilities iOS
Su iOS, ogni tipo di attività in background deve essere dichiarata in Info.plist e abilitata nelle Capabilities di Xcode. Background Fetch, Remote Notifications, Background Processing — se dimentichi uno di questi flag, il task viene eseguito in sviluppo (dove le restrizioni sono alleggerite) e fallisce in produzione.
È una delle cause più frequenti di “funzionava prima della messa in produzione”.
Ignorare il Doze Mode e App Standby su Android
Android 6+ introduce il “Doze Mode”: quando il dispositivo è inattivo, i job in background vengono raggruppati e differiti. Android 8+ aggiunge restrizioni ancora più severe sui servizi in background.
La soluzione ufficiale: usare WorkManager tramite una lib nativa. react-native-background-fetch lo fa sotto il cofano dalla versione 4. Se usi una versione precedente o una lib non mantenuta, sei esposto.
Verifica le tue dipendenze. Un rapido npm outdated può rivelare sorprese.
Non gestire i timeout
iOS ti dà 30 secondi. Android è variabile. Se il tuo task supera questo limite — fetch di rete lento, elaborazione pesante, database SQLite lento — il sistema lo uccide. Senza eccezioni. Senza log pulito.
La regola: ogni task in background deve avere un timeout esplicito. Se non ha terminato in 25 secondi, deve terminare pulitamente da solo, salvare il suo stato, e riprendere al prossimo ciclo.
Testare in condizioni reali: il protocollo che usiamo
Gli emulatori non riproducono i comportamenti in background. Punto. Devi testare su dispositivi reali, con vincoli reali.
Il nostro protocollo minimo prima di rilasciare una funzionalità di background processing:
Dispositivo 1 — Android puro: Un Pixel o un dispositivo Android One. Valida che il tuo codice funzioni senza layer del produttore.
Dispositivo 2 — Samsung Galaxy: Testa il comportamento con il layer Samsung, il più diffuso.
Dispositivo 3 — iPhone con iOS recente: Valida le restrizioni Apple, che evolvono ad ogni versione principale.
Per ogni dispositivo, lo scenario di test:
- Avviare l’app, metterla in background
- Spegnere lo schermo, aspettare 30 minuti
- Verificare se il task è stato eseguito
- Ripetere con la batteria sotto il 20%
- Ripetere con la modalità risparmio energetico attivata
È noioso. È ciò che separa un’app che regge in produzione da un’app che genera ticket di supporto alle 3 di notte.
La strategia di resilienza: non assumere mai che il task sia stato eseguito
Ecco il cambiamento di mentalità più importante: progetta la tua app come se le attività in background potessero non eseguirsi mai.
Questo implica:
Una sincronizzazione in primo piano come fallback. Ad ogni apertura dell’app, verifica se ci sono dati da sincronizzare. Se il task in background è fallito, il primo piano recupera il ritardo.
Un timestamp dell’ultima esecuzione. Memorizza localmente quando il task è stato eseguito l’ultima volta. Se il ritardo è anomalo, scatena una risincronizzazione immediata.
Indicatori lato server. Non fidarti solo del client. Il tuo server deve sapere se un dispositivo è “in ritardo” e adattare le sue risposte di conseguenza.
Non è over-engineering. È quello che fanno tutte le app mobili critiche. Gmail, Notion, Slack — tutti hanno una logica di recupero in primo piano perché tutti sanno che il background processing non è affidabile al 100%.
“Un sistema robusto non è quello che non cade mai. È quello che si rialza da solo.”
Cosa cambia concretamente per il tuo progetto
Se stai sviluppando un’app React Native con necessità in background, ecco i tre punti azionabili da ricordare:
1. Scegli il tuo stack in base al tuo caso d’uso reale, non in base al primo tutorial di Medium che hai letto. BackgroundFetch per il non critico, silent push per il tempo reale, lib specializzate per la geoloc.
2. Testa su dispositivi reali, con vincoli reali. Emulatore = falsa sicurezza. Un Samsung in modalità risparmio energetico ti rivelerà più bug in 30 minuti che 10 ore su simulatore.
3. Progetta per il fallimento. Il tuo task in background fallirà. Non sempre, non su tutti i dispositivi, ma fallirà. La tua architettura deve anticiparlo.
Conclusione: l’affidabilità si costruisce, non si suppone
Il background processing in React Native non è un problema di codice. È un problema di architettura, configurazione e test in condizioni reali.
I tutorial base ti mostrano come eseguire un task. Non ti mostrano come eseguire un task in modo affidabile, su tutti i dispositivi, per 18 mesi di produzione. È lì che si fa la differenza tra un’app che gli utenti conservano e un’app che disinstallano.
In GDM-Pixel, quando interveniamo in un audit di un’app mobile che “non funziona più come prima”, le attività in background sono tra le prime tre cose che verifichiamo. Perché sappiamo che è lì che vivono i bug silenziosi.
La tua app React Native ha comportamenti erratici in produzione? Facciamo audit tecnici mobili — diagnosi onesta, raccomandazioni azionabili, senza venderti una riscrittura se non è necessaria. Contattaci per parlarne.