Saltar al contenido principal
iOS Capacitor Tutorial

Por qué las geocercas no se activan en iOS

¿Las geofences no se activan en iOS en tu app de Capacitor? Aprende cómo funciona el monitoreo de regiones, las reglas de permisos, radio y límites, y cómo depurar cada caso.

Mapa con límites de ubicación que ilustra el geofencing en una app iOS de Capacitor

Cuando los geofences no se activan en iOS, la causa casi siempre es una de una lista corta: la app carece de autorización Always, el dispositivo ya estaba dentro de la región cuando se añadió, el radio es demasiado pequeño para que iOS detecte un cruce, la app alcanzó el límite de 20 regiones, o la prueba no esperó lo suficiente. El geofencing en iOS funciona bien, pero informa los cruces según su propio calendario. Esta guía explica cómo el monitoreo de regiones decide cuándo dispararse y cómo depurar una app de Capacitor usando @capgo/background-geolocation, que incluye geofencing nativo en iOS y Android.

Cómo funciona el monitoreo de regiones en iOS

En iOS, los geofences son CLCircularRegion objetos registrados con Core Location. Una vez registrados, el sistema operativo los monitorea, no tu app. Tu app puede estar suspendida o no estar en ejecución y iOS sigue rastreando las regiones, usando una mezcla de torres celulares, Wi-Fi y GPS para mantener el uso de batería bajo.

Cuando iOS decide que el dispositivo cruzó un límite, entrega un evento de entrada o salida a la app, lanzándola en segundo plano si es necesario y está permitido. Algunas reglas controlan esa decisión:

  • iOS no informa el cruce en el momento en que el dispositivo toca el límite. La guía de Apple indica que el dispositivo debe cruzar, moverse una distancia mínima más allá del límite y permanecer allí durante al menos unos 20 segundos antes de que se entregue el evento. Esto evita activaciones por jitter de GPS.
  • La precisión de la ubicación para el monitoreo de regiones depende de qué radios estén cerca. En una ciudad con Wi-Fi denso, puede ser rápido. En una carretera rural, puede tardar minutos.
  • Los eventos son solo para transiciones. Estar dentro de una región cuando comienza el monitoreo no cuenta como una entrada.

Configura geofencing en una app Capacitor

Instala el plugin y configura las claves de 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>

Luego registra las regiones:

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 solicita el permiso que el geofencing necesita por defecto (requestPermissions por defecto es true). En iOS, eso significa Siempre.

Ahora, los modos de fallo.

1. No tienes autorización Siempre

El monitoreo de regiones en iOS requiere autorización Siempre. Con solo Mientras se usa la app, tus regiones no entregarán eventos cuando la app esté en segundo plano o no esté en ejecución, que es el punto clave de un geofence.

Comprueba esto antes de registrar regiones y explica la solicitud primero en tu propia interfaz:

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();
  }
}

Ten en cuenta que puede que iOS no muestre el prompt para cambiar a Always de inmediato. Los usuarios que eligieron “Allow While Using App” puede que solo vean el segundo prompt más tarde. Quienes eligieron “Allow Once” reciben un permiso temporal que se reinicia. Si el geofencing es una función clave, revisa el estado en cada inicio de la app y muestra un banner claro cuando no esté en Always.

2. El dispositivo ya estaba dentro de la región

Si añades un geofence mientras el teléfono ya está dentro, iOS no envía un evento de entrada. No hubo ningún cruce. Los equipos se topan con esto constantemente al probar desde su escritorio con una región alrededor de la oficina.

Resuélvelo en tu app: al registrar una región, compara la posición actual con la región y gestiona tú mismo el caso de “ya dentro”.

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;

Para probar un evento de entrada, sal caminando o conduciendo bastante más allá del radio, espera y vuelve.

3. El radio es demasiado pequeño

iOS acepta radios pequeños, pero la fiabilidad cae rápido por debajo de unos 100 metros, porque el monitoreo de regiones depende del Wi-Fi y del posicionamiento celular. Una región de 30 metros alrededor de la puerta de una tienda puede activarse tarde, activarse después de que el cliente se haya ido o no activarse nunca.

Valores prácticos:

Caso de uso Radio sugerido
Llegada a tienda o restaurante 100 a 200 m
Oficina, campus, almacén 150 a 300 m
Barrio o zona de la ciudad 500 m o más

Si necesitas precisión a nivel de puerta, combina una geocerca más grande con la ubicación en primer plano o un beacon una vez que el usuario esté dentro. Capgo también tiene un plugin de iBeacon para proximidad en interiores.

4. Alcanzaste el límite de 20 regiones

Una app de iOS puede monitorear un máximo de 20 regiones a la vez. Si agregas más, el registro fallará para las adicionales y recibirás un error de monitoreo.

Usa getMonitoredGeofences() para ver qué está realmente registrado, y mantén un conjunto rotativo de las regiones más cercanas:

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

Un patrón común:

  1. Mantén tu lista completa de lugares en el servidor.
  2. Cuando la app arranca y cuando el usuario se mueve mucho (por ejemplo, al salir de una región de “refresco” grande alrededor del usuario), obtén los 19 lugares más cercanos.
  3. Llama a removeAllGeofences(), luego añade los 19 más cercanos más una región grande centrada en el usuario. Salir de esa región grande es tu señal para refrescar.

Android permite hasta 100 geofences por app, así que code que funciona en Android puede fallar en iOS solo por esta razón.

5. La prueba fue demasiado corta o estática

iOS informa transiciones solo después de que el dispositivo ha cruzado claramente y se ha quedado al otro lado. Si conduces a través de una región de 150 metros a 50 km/h, pasas unos 20 segundos dentro. Eso puede no ser suficiente.

Al probar:

  • Quédate fuera un minuto, luego entra y quédate al menos un par de minutos.
  • Prueba con Wi-Fi activado. Desactivar el Wi-Fi hace que el monitoreo de regiones sea mucho menos preciso, incluso si no estás conectado a ninguna red.
  • No confíes en el Simulator para el timing. Úsalo con una ruta GPX en Xcode para verificar tus handlers, luego prueba en un dispositivo.

6. El evento se disparó, pero tu JavaScript no se ejecutó

Cuando iOS relanza tu app en segundo plano para un evento de región, el plugin nativo lo recibe, pero el WebView de Capacitor puede no estar cargado o puede suspenderse de nuevo rápidamente. El geofenceTransition listener solo se dispara mientras el WebView está activo.

Es por eso que setupGeofencing acepta un url. El plugin hace un POST de la transición desde el code nativo, incluyendo tu payload, para que tu backend lo reciba incluso si JavaScript nunca se ejecuta. Los headers se persisten con la configuración para que sigan funcionando tras un relanzamiento, y puedes rotarlos con updateHeaders().

La entrega es de mejor esfuerzo, sin cola de reintentos. Haz que el endpoint sea rápido e idempotente, y registra cada solicitud durante las pruebas.

7. Location Services o Background App Refresh están desactivados

Comprueba esto en el dispositivo de prueba:

  • Ajustes, Privacidad y seguridad, Servicios de ubicación está activado.
  • Tu app está configurada en Siempre.
  • La ubicación precisa está activada para tu app. Sin ella, las regiones pequeñas no son fiables.
  • El modo de bajo consumo está desactivado en tus primeras pruebas. Reduce la actividad en segundo plano.

8. Reinicios y actualizaciones de la app

iOS mantiene las regiones monitorizadas entre lanzamientos de la app. Tras un reinicio del dispositivo, la monitorización se reanuda cuando el sistema vuelve a estar operativo, pero tu app debería volver a sincronizar sus regiones al iniciar: compara getMonitoredGeofences() con lo que esperas y vuelve a añadir las que falten. Haz lo mismo tras una actualización de la app y siempre que el servidor cambie la lista de lugares.

En Android, las geocercas se eliminan tras un reinicio y en algunas otras situaciones (ubicación desactivada, datos de la app borrados), por lo que volver a registrarlas al iniciar es obligatorio allí de todos modos. Hacerlo en ambas plataformas mantiene la lógica simple.

9. Forzar cierre

Si un usuario fuerza el cierre de la app desde el conmutador de apps, iOS lo interpreta como una señal fuerte de que el usuario no quiere que la app se ejecute. El comportamiento de relanzamiento de regiones tras un cierre forzado ha cambiado entre versiones de iOS. No construyas una funcionalidad que solo funcione si la app se relanza tras un cierre forzado, y pruébala en las versiones de iOS que usan tus usuarios.

Checklist de depuración

Verificar Cómo
El permiso es Siempre checkPermissions() devuelve always o granted para backgroundLocation
La región está registrada getMonitoredGeofences() lista el identificador
Menos de 20 regiones Contar desde getMonitoredGeofences()
Radio ≥ 100 m Revisa tu addGeofence llama a
No estar ya dentro Comparar la posición actual al registrar
Los errores se registran Escuchar a geofenceError
El backend recibe eventos Configurar url en setupGeofencing y registra las solicitudes
Prueba de movimiento real Sal de la región por completo, espera, vuelve y quédate

Diferencias de Android a tener en cuenta

Si el mismo code funciona en Android pero no en iOS, el límite de 20 regiones y el requisito de Always suelen ser las causas. Si funciona en iOS pero no en Android, verifica que hayas añadido ACCESS_BACKGROUND_LOCATION a tu manifest y que hayas configurado backgroundLocation: true en setupGeofencing. El plugin no añade ese permiso por ti, ya que Google Play exige una declaración para la ubicación en segundo plano.

Despliegue de cambios

Las cadenas de permisos y la configuración nativa requieren un nuevo build. Las listas de regiones, los valores de radio y el texto de la interfaz se pueden cambiar en el servidor o enviarse mediante actualizaciones Live Update de Capgo. Para el seguimiento continuo en lugar de eventos de entrada y salida, consulta por qué iOS detiene la ubicación en segundo plano, y el plugin documentación para el API completo.

Live updates para apps Capacitor

Cuando un bug de la capa web está en producción, publica el fix a través de Capgo en lugar de esperar días a la aprobación de la app store. Los usuarios reciben la actualización en segundo plano mientras los cambios nativos siguen el flujo de revisión habitual.

Lo último de nuestro blog

Capgo te da la información clave que necesitas para crear una app móvil realmente profesional.