iOS interrompe gli aggiornamenti della posizione in background perché sospende l'app, non perché Core Location fallisce. A meno che l'app non dichiari la modalità di posizione in background, abiliti gli aggiornamenti in background nel suo CLLocationManager, e li avvii mentre è ancora in primo piano, iOS congela il processo pochi secondi dopo che l'utente se ne va e mette in coda o scarta i rilevamenti. Questa guida elenca ogni causa che vediamo nelle app Capacitor, con la soluzione per ciascuna, e mostra come @capgo/background-geolocation li gestisce.
Come iOS gestisce un'app in background
Quando un utente torna alla home o blocca il telefono, iOS concede all'app una breve finestra temporale, poi la sospende. Un'app sospesa non riceve tempo di CPU. Per un'app Capacitor questo significa che sia la parte nativa che la WebView smettono di girare, quindi anche i callback JavaScript smettono di essere eseguiti.
Apple fa un'eccezione per le app che usano attivamente la posizione. Se l'app è configurata per la posizione in background e ha una sessione di localizzazione attiva avviata in foreground, iOS la mantiene in esecuzione e continua a inviare aggiornamenti. Tutto ciò che segue riguarda il rispetto di questo contratto e i casi in cui il sistema lo viola di proposito.
Causa 1: manca la background mode per la posizione
Senza location nella UIBackgroundModes, nessun code riuscirà a mantenere attivi gli aggiornamenti. Aggiungila a ios/App/App/Info.plist:
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
Puoi abilitarla anche in Xcode sotto Signing & Capabilities, Background Modes, Location updates. È una chiave plist, non un entitlement, quindi non serve modificare il provisioning profile.
La proprietà nativa che ci sta dietro è allowsBackgroundLocationUpdates. Impostandola a true senza la background mode nel plist fa crashare l'app. Il plugin Capgo la imposta per te quando passi backgroundMessage a start(), quindi la chiave plist deve esserci prima del rilascio.
L'App Review verifica che la modalità background sia giustificata. Spiega nelle note di review quale funzione la utilizza e assicurati che la funzione sia visibile al revisore.
Causa 2: gli aggiornamenti sono stati avviati dal background
Regola di Apple: gli aggiornamenti in background continuano se sono stati avviati mentre l'app era in foreground. Tentare di avviare una sessione da una silent push, un background fetch o un BGTaskScheduler task non funziona per gli aggiornamenti di posizione standard.
Avvia il tracciamento da un'azione dell'utente o alla ripresa dell'app:
import { App } from '@capacitor/app';
import { BackgroundGeolocation } from '@capgo/background-geolocation';
let tracking = false;
export async function startTracking() {
if (tracking) return;
tracking = true;
await BackgroundGeolocation.start(
{
backgroundMessage: 'Recording your route',
backgroundTitle: 'Route recording',
requestPermissions: true,
distanceFilter: 10,
url: 'https://api.example.com/v1/points',
},
(location, error) => {
if (error) return console.error(error);
if (location) console.log(location.latitude, location.longitude);
},
);
}
// Re-arm when the user comes back, if the session should be active
App.addListener('appStateChange', ({ isActive }) => {
if (isActive && shouldBeTracking()) startTracking();
});
declare function shouldBeTracking(): boolean;
Causa 3: backgroundMessage non è stato passato
Questo è specifico per il plugin Capgo ed è il bug report più comune. Se backgroundMessage è undefined, il plugin configura il location manager solo per l'uso in foreground: allowsBackgroundLocationUpdates rimane false e gli aggiornamenti si fermano quando l'app è sospesa. Passa un messaggio su entrambe le piattaforme. Su Android diventa anche il testo della notifica del servizio in foreground.
Causa 4: livello di autorizzazione errato, o Consenti una volta
Molti sviluppatori pensano di aver bisogno di Sempre per tracciare in background. Non è così. Apple documenta che un'app con autorizzazione In Uso continua a funzionare in background mentre ha servizi di localizzazione attivi, purché gli aggiornamenti in background siano abilitati.
Conta sempre per un’altra cosa: il riavvio di un’app terminata. Solo il monitoraggio di aree, i cambiamenti significativi di posizione e le visite possono riavviare un’app, e solo con Always.
Two user choices quietly break background tracking:
- Consenti una volta. Questo è un permesso In Uso temporaneo che scade quando l'app smette di essere utilizzata. La sessione successiva riparte da "non determinato".
- Durante l'uso, poi la richiesta Sempre non viene mai mostrata. Quando richiedi Sempre, iOS potrebbe mostrare la richiesta di aggiornamento più tardi, in un momento scelto da esso, spesso quando l'app non è a schermo.
Verifica lo stato reale prima di avviare una sessione:
import { BackgroundGeolocation } from '@capgo/background-geolocation';
const status = await BackgroundGeolocation.checkPermissions();
if (status.location !== 'granted') {
await BackgroundGeolocation.requestPermissions({ permissions: ['location'] });
}
if (status.backgroundLocation === 'when_in_use') {
// Background tracking still works for standard updates.
// Explain why Always helps if you also rely on geofences.
}
Su iOS, backgroundLocation restituisce when_in_use oppure granted / always, così da mostrare la spiegazione corretta invece di fare supposizioni.
Causa 5: Core Location mette in pausa gli aggiornamenti automaticamente
CLLocationManager.pausesLocationUpdatesAutomatically ha come default true. Quando iOS decide che l'utente non si sta muovendo, mette in pausa gli aggiornamenti per risparmiare batteria. Per un'app When In Use, una pausa termina l'accesso alla posizione finché l'app non viene riavviata, e Core Location non riprende da solo.
Il plugin Capgo imposta pausesLocationUpdatesAutomatically = false nel suo code nativo, quindi questo aspetto è già gestito. Se scrivi il tuo code nativo o usi un altro plugin, controlla prima questa proprietà.
Causa 6: il dispositivo è fermo e il filtro è ampio
Con un distanceFilter di 50 metri, un telefono fermo su una scrivania non genera aggiornamenti. Nei log sembra che il tracking si sia fermato, ma è il comportamento previsto.
- Se hai bisogno di heartbeat mentre sei fermo, usa un valore piccolo o zero
distanceFilter. - Usa
minIntervalMsper limitare la frequenza dei POST nativi, invece di un filtro di distanza ampio.
await BackgroundGeolocation.start(
{
backgroundMessage: 'Vehicle tracking active',
distanceFilter: 0,
minIntervalMs: 120_000, // at most one native POST every 2 minutes
url: 'https://api.example.com/v1/vehicle-points',
},
() => {},
);
Causa 7: Localizzazione precisa disattivata
Da iOS 14 gli utenti possono disattivare la Localizzazione precisa per la tua app. Ricevi comunque aggiornamenti, ma sono approssimativi (diversi chilometri) e arrivano molto meno spesso. Rilevalo nei tuoi dati (valori accuracy ampi) e spiega all'utente perché la localizzazione precisa è importante per la tua funzionalità. Se pubblichi un'app di delivery o fitness, mostralo durante l'onboarding, non dopo un viaggio andato storto.
Causa 8: l'utente ha chiuso l'app
Se l'utente fa scorrere via l'app nell'app switcher, iOS interrompe gli aggiornamenti di posizione standard. Nessun plugin può mantenerli attivi. È una sorpresa comune per i team che arrivano da Android, dove un foreground service può continuare a girare anche dopo la rimozione del task.
Opzioni:
- Avvisa gli utenti di non forzare la chiusura durante una sessione. La maggior parte delle app fleet lo fa in onboarding.
- Aggiungi geofence con autorizzazione Always attorno ai luoghi importanti, così iOS può riavviare l'app in background all'ingresso o all'uscita.
- Invia una push per ricordare all'utente di riaprire l'app quando il server smette di ricevere punti per una sessione.
Causa 9: JavaScript non è in esecuzione anche se il nativo sì
Un caso non scontato. Il nativo code può ricevere una posizione mentre la WebView è sospesa, o mentre è ancora attiva ma non elabora rapidamente timer o chiamate di rete. Se invii punti con fetch dal callback, possono arrivare in ritardo o non arrivare affatto.
Usa l'opzione url del plugin. Ogni punto viene inviato via POST dal code nativo con lo stesso JSON del Location tipo più "source": "native", indipendentemente dalla WebView. Usa il callback solo per gli aggiornamenti UI. La consegna è best effort e senza coda su disco: se non puoi perdere un punto, salvalo anche in locale quando scatta il callback e riconcilia dopo.
Per il lavoro in background oltre alla posizione, leggi come funzionano i task in background in Capacitor e consulta @capgo/capacitor-background-task.
Causa 10: Modalità Risparmio energia e stato termico
La Modalità Risparmio energia riduce l'attività in background in tutto il sistema. La posizione funziona ancora, ma le altre cose da cui dipende la tua sessione (retry di rete, refresh in background) rallentano. Testa con la Modalità Risparmio energia attiva prima di approvare un rilascio.
L'indicatore blu
Quando un'app con autorizzazione 'When In Use' usa la posizione in background, iOS mostra una pillola blu nella barra di stato. Il plugin Capgo imposta showsBackgroundLocationIndicator a true per le sessioni in background: è ciò che gli utenti si aspettano e che i reviewer controllano. Non provare a nasconderlo, gli utenti si fidano.
Un piano di test che intercetti questi bug
I test sul Simulator non bastano per la posizione in background. Usa un iPhone reale e:
- Crea una configurazione di release, non solo di debug. Le build di debug collegate a Xcode hanno un ciclo di vita differente.
- Avvia una sessione in foreground, blocca il telefono, cammina o guida per 20 minuti.
- Ripeti con “Allow Once”, con Precise Location disattivata e con il Risparmio energetico attivo.
- Controlla i log del tuo server per
source: "native"i punti, non solo il log dell'app. - Forza la chiusura durante una sessione e verifica che l'app spieghi cosa è successo al prossimo avvio.
Se ti serve l'UDID del dispositivo per le build ad hoc, usa l' Trova UDID iOS lo ottiene da Safari. Se non hai un Mac con Xcode 26 per i build, Build Capgo può generare build iOS firmati nel cloud.
Checklist per il troubleshooting
| Sintomo | Causa probabile | Soluzione |
|---|---|---|
| Si ferma pochi secondi dopo il blocco | No location modalità background o no backgroundMessage |
Aggiungi la chiave plist e passa backgroundMessage |
| Crash all'avvio | allowsBackgroundLocationUpdates senza modalità in background |
Aggiungi UIBackgroundModes → location |
| Funziona una volta, poi no | L'utente ha scelto Consenti una volta | Spiega e chiedi di nuovo, oppure invia a openSettings() |
| Si ferma quando è in sosta | Grande distanceFilter |
Riducilo, usa minIntervalMs |
| Punti distanti, bassa precisione | Posizione precisa disattivata | Chiedi all'utente di attivarla |
| Si interrompe dopo lo swipe | L'utente ha terminato l'app | Comportamento atteso su iOS, usa geofence per il riavvio |
| Il nativo riceve i punti, il server no | JavaScript fetch nel callback |
Usa il url opzione |
Pubblicazione delle correzioni
La maggior parte di queste correzioni riguarda Info.plist o configurazione nativa, quindi richiedono un nuovo build per l'App Store. Le parti in JavaScript, come le spiegazioni dei permessi, il copy dell'onboarding e la logica di retry, possono essere distribuite agli utenti esistenti con Live updates di Capgo una volta rilasciato il build nativo.
Se stai decidendo tra i plugin, il nostro confronto tra Transistorsoft e il plugin gratuito Capgo copre i compromessi.