Salta al contenuto principale

Perché le geofence non si attivano su iOS

Le geofence non si attivano su iOS nella tua app Capacitor? Scopri come funziona il monitoraggio delle regioni, i permessi, le regole su raggio e limiti, e come fare il debug di ogni caso.

Mappa con confini di posizione che illustra il geofencing in un'app iOS Capacitor

Quando i geofence non vengono attivati su iOS, la causa è quasi sempre una di una breve lista: l'app non ha l'autorizzazione Sempre, il dispositivo era già all'interno della regione quando l'hai aggiunta, il raggio è troppo piccolo perché iOS rilevi un attraversamento, l'app ha raggiunto il limite di 20 regioni o il test non ha aspettato abbastanza a lungo. Il geofencing su iOS funziona bene, ma segnala gli attraversamenti secondo il proprio schema. Questa guida spiega come il monitoraggio delle regioni decide quando attivare un evento e come fare il debug di un'app Capacitor usando @capgo/background-geolocation, che include il geofencing nativo su iOS e Android.

Come funziona il monitoraggio delle regioni su iOS

Su iOS, i geofence sono CLCircularRegion oggetti registrati con Core Location. Una volta registrati, è il sistema operativo a monitorarli, non la tua app. L'app può essere sospesa o non in esecuzione e iOS continua a tracciare le regioni, utilizzando una combinazione di celle, Wi-Fi e GPS per mantenere basso il consumo della batteria.

Quando iOS decide che il dispositivo ha attraversato un confine, consegna un evento di entrata o uscita all'app, avviandola in background se necessario e consentito. Alcune regole controllano questa decisione:

  • iOS non segnala un attraversamento nel momento in cui il dispositivo tocca il confine. Le linee guida di Apple indicano che il dispositivo deve attraversare, spostarsi di una distanza minima oltre il confine e rimanervi per almeno circa 20 secondi prima che l'evento venga consegnato. Questo evita attivazioni dovute al jitter del GPS.
  • La precisione della posizione per il monitoraggio delle aree dipende dalle radio disponibili. In una città con Wi-Fi denso, può essere rapida. Su una strada rurale, può richiedere minuti.
  • Gli eventi riguardano solo le transizioni. Essere all'interno di un'area quando inizia il monitoraggio non conta come ingresso.

Configurare il geofencing in un'app Capacitor

Installa il plugin e configura le chiavi iOS:

bun add @capgo/background-geolocation
bunx cap sync
<key>NSLocationWhenInUseUsageDescription</key>
<string>We use your location to detect when you arrive at a store.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We need Always access to notify you when you arrive, even when the app is closed.</string>

Poi registra le aree:

import { BackgroundGeolocation } from '@capgo/background-geolocation';

await BackgroundGeolocation.setupGeofencing({
  url: 'https://api.example.com/v1/geofence-events',
  headers: { Authorization: 'Bearer <token>' },
  notifyOnEntry: true,
  notifyOnExit: true,
  payload: { userId: 'u-42' },
});

await BackgroundGeolocation.addGeofence({
  identifier: 'store-paris-11',
  latitude: 48.8606,
  longitude: 2.3795,
  radius: 150,
  payload: { storeId: 'paris-11' },
});

await BackgroundGeolocation.addListener('geofenceTransition', (event) => {
  console.log(`${event.transition} ${event.identifier}`, event.payload);
});

await BackgroundGeolocation.addListener('geofenceError', (event) => {
  console.error('Geofence error', event.identifier, event.code, event.message);
});

setupGeofencing richiede il permesso necessario per il geofencing in modo predefinito (requestPermissions ha come default true). Su iOS significa Sempre.

Ora, i modi di fallimento.

1. Non hai l'autorizzazione 'Always'

Il monitoraggio delle aree su iOS richiede l'autorizzazione Sempre. Con Solo durante l'uso, le tue aree non invieranno eventi quando l'app è in background o non in esecuzione, che è l'intero scopo di un geofence.

Controlla prima di registrare le aree e spiega la richiesta nella tua UI prima:

const status = await BackgroundGeolocation.checkPermissions();

if (status.backgroundLocation !== 'always' && status.backgroundLocation !== 'granted') {
  // Show your own screen explaining the feature, then:
  const next = await BackgroundGeolocation.requestPermissions({
    permissions: ['backgroundLocation'],
  });
  if (next.backgroundLocation === 'when_in_use' || next.backgroundLocation === 'denied') {
    await BackgroundGeolocation.openSettings();
  }
}

Tieni presente che iOS potrebbe non mostrare subito il prompt per passare a "Sempre". Gli utenti che hanno scelto "Consenti durante l'utilizzo dell'app" potrebbero vedere il secondo prompt solo più tardi. Chi ha scelto "Consenti una volta" riceve un'autorizzazione temporanea che si azzera. Se il geofencing è una funzionalità chiave, controlla lo stato a ogni avvio dell'app e mostra un banner chiaro quando non è su "Sempre".

2. Il dispositivo era già all'interno della regione

Se aggiungi un geofence mentre il telefono è già al suo interno, iOS non invia un evento di ingresso. Nessun attraversamento è avvenuto. I team ci sbattono continuamente la testa quando fanno test alla scrivania con una regione attorno all'ufficio.

Risolvilo nella tua app: quando registri una regione, confronta la posizione attuale con la regione e gestisci tu il caso "già all'interno".

import { Geolocation } from '@capacitor/geolocation';

function distanceMeters(lat1: number, lon1: number, lat2: number, lon2: number) {
  const R = 6371000;
  const toRad = (d: number) => (d * Math.PI) / 180;
  const dLat = toRad(lat2 - lat1);
  const dLon = toRad(lon2 - lon1);
  const a =
    Math.sin(dLat / 2) ** 2 +
    Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) ** 2;
  return 2 * R * Math.asin(Math.sqrt(a));
}

const region = { identifier: 'store-paris-11', latitude: 48.8606, longitude: 2.3795, radius: 150 };
await BackgroundGeolocation.addGeofence(region);

const pos = await Geolocation.getCurrentPosition({ enableHighAccuracy: true });
if (distanceMeters(pos.coords.latitude, pos.coords.longitude, region.latitude, region.longitude) < region.radius) {
  handleAlreadyInside(region.identifier);
}

declare function handleAlreadyInside(id: string): void;

Per testare un evento di ingresso, esci a piedi o in auto ben oltre il raggio, aspetta, poi torna indietro.

3. Il raggio è troppo piccolo

iOS accetta raggi piccoli, ma l'affidabilità cala rapidamente sotto i 100 metri circa, perché il monitoraggio delle regioni si basa sul posizionamento Wi-Fi e cellulare. Una regione di 30 metri attorno alla porta di un negozio può attivarsi in ritardo, attivarsi dopo che il cliente se n'è andato, o non attivarsi affatto.

Valori pratici:

Caso d'uso Raggio consigliato
Arrivo in negozio o ristorante 100-200 m
Ufficio, campus, magazzino 150-300 m
Quartiere o zona cittadina 500 m o più

Per una precisione a livello di porta, combina una geofence più ampia con la posizione in foreground o un beacon quando l'utente è all'interno. Capgo ha anche un plugin iBeacon per la prossimità indoor.

4. Hai raggiunto il limite di 20 regioni

Un'app iOS può monitorare al massimo 20 regioni contemporaneamente. Se ne aggiungi di più, la registrazione fallisce per quelle extra e ricevi un errore di monitoraggio.

Usa getMonitoredGeofences() per vedere cosa è realmente registrato e mantenere un set rotante delle regioni più vicine:

const { regions } = await BackgroundGeolocation.getMonitoredGeofences();
console.log(`Monitoring ${regions.length} regions`, regions);

Un pattern comune:

  1. Mantieni l'elenco completo dei luoghi sul server.
  2. All'avvio dell'app e quando l'utente si sposta molto (ad esempio uscendo da una grande regione di "refresh" attorno a lui), recupera i 19 luoghi più vicini.
  3. Chiama removeAllGeofences(), poi aggiungi i 19 più vicini e una grande regione centrata sull'utente. L'uscita da quella grande regione è il segnale per il refresh.

Android consente fino a 100 geofence per app, quindi code che funziona su Android può fallire su iOS solo per questo motivo.

5. Il test era troppo breve o troppo statico

iOS segnala le transizioni solo dopo che il dispositivo ha chiaramente attraversato e si è fermato dall'altra parte. Se attraversi una regione di 150 metri a 50 km/h, ci passi circa 20 secondi. Potrebbe non bastare.

Durante i test:

  • Resta fuori per un minuto, poi entra e resta almeno un paio di minuti.
  • Testa con il Wi-Fi acceso. Disattivarlo rende il monitoraggio delle regioni molto meno accurato, anche se non sei connesso a nessuna rete.
  • Non fare affidamento sul Simulator per i tempi. Usalo con una rotta GPX in Xcode per verificare i tuoi handler, poi testa su un dispositivo.

6. L'evento è scattato, ma il tuo JavaScript non è stato eseguito

Quando iOS riavvia la tua app in background per un evento di regione, il plugin nativo lo riceve, ma il WebView Capacitor potrebbe non essere caricato o potrebbe essere sospeso di nuovo rapidamente. Il geofenceTransition listener si attiva solo mentre il WebView è attivo.

Ecco perché setupGeofencing accetta un url. Il plugin invia la transizione da code nativo, incluso il tuo payload, in modo che il backend la riceva anche quando JavaScript non viene eseguito. Gli header vengono persistiti con la configurazione, quindi funzionano ancora dopo un riavvio, e puoi ruotarli con updateHeaders().

La consegna è best effort, senza coda di retry. Mantieni l'endpoint veloce e idempotente, e registra ogni richiesta durante i test.

7. I servizi di localizzazione o il Background App Refresh sono disattivati

Controlla questi elementi sul dispositivo di test:

  • Impostazioni, Privacy e sicurezza, Servizi di localizzazione è attivo.
  • La tua app è impostata su Sempre.
  • La posizione precisa è attiva per la tua app. Senza di essa, le piccole regioni sono inaffidabili.
  • Assicurati che la Risparmio Energetico sia disattivato durante i primi test. Riduce l'attività in background.

8. Riavvii e aggiornamenti dell'app

iOS mantiene le regioni monitorate tra le avvio dell'app. Dopo il riavvio del dispositivo, il monitoraggio riprende quando il sistema è attivo, ma la tua app dovrebbe comunque risincronizzare le regioni all'avvio: confronta getMonitoredGeofences() con ciò che ti aspetti e riaggiungi quelle mancanti. Fai lo stesso dopo un aggiornamento dell'app e ogni volta che il server modifica l'elenco dei luoghi.

Su Android, i geofence vengono rimossi dopo un riavvio e in altre situazioni (posizione disattivata, dati app cancellati), quindi la reregistrazione all'avvio è comunque necessaria. Farlo su entrambe le piattaforme mantiene la logica semplice.

9. Chiusura forzata

Se un utente chiude forzatamente l'app dal task switcher, iOS lo interpreta come un segnale forte che l'utente non vuole che l'app sia in esecuzione. Il comportamento di riavvio delle regioni dopo una chiusura forzata è cambiato tra le versioni di iOS. Non costruire una funzionalità che funzioni solo se l'app viene riavviata dopo una chiusura forzata, e testala sulle versioni di iOS utilizzate dai tuoi utenti.

Checklist di debug

Verifica Come
Il permesso è Sempre checkPermissions() restituisce always oppure granted per backgroundLocation
La regione è registrata getMonitoredGeofences() elenca l'identificatore
Meno di 20 regioni Conteggio da getMonitoredGeofences()
Raggio ≥ 100 m Controlla il tuo addGeofence chiama
Non già all'interno Confronta la posizione attuale alla registrazione
Gli errori vengono registrati Ascolta geofenceError
Il backend riceve gli eventi Imposta url nella setupGeofencing e registra le richieste
Test di movimento reale Esci completamente dalla regione, aspetta, torna e resta

Differenze su Android da tenere a mente

Se lo stesso code funziona su Android ma non su iOS, i motivi più comuni sono il limite di 20 regioni e il requisito Always. Se funziona su iOS ma non su Android, verifica di aver aggiunto ACCESS_BACKGROUND_LOCATION al tuo manifest e impostato backgroundLocation: true nella setupGeofencing. Il plugin non aggiunge questa autorizzazione al posto tuo, perché Google Play richiede una dichiarazione per la posizione in background.

Pubblicare modifiche

Le stringhe di autorizzazione e la configurazione nativa richiedono un nuovo build. Le liste di regioni, i valori del raggio e i testi UI possono essere modificati lato server o distribuiti tramite Live updates di Capgo. Per il tracciamento continuo anziché gli eventi di ingresso e uscita, consulta why iOS stops background location updates, e il plugin documentazione per il API completo.

Live updates per le app Capacitor

Quando un bug nel web-layer è live, rilascia la fix tramite Capgo invece di aspettare giorni l'approvazione degli store. Gli utenti ricevono l'update in background, mentre le modifiche native restano nel normale processo di review.

Ultimi articoli dal blog

Capgo ti offre gli insight migliori che ti servono per creare un'app mobile davvero professionale.