iOS detiene las actualizaciones de ubicación en segundo plano porque suspende tu app, no porque Core Location falle. A menos que la app declare el modo de ubicación en segundo plano, habilite las actualizaciones en segundo plano en su CLLocationManager, y las inicie mientras aún está en primer plano, iOS congela el proceso unos segundos después de que el usuario sale y encola o descarta las posiciones. Esta guía enumera cada causa que vemos en apps de Capacitor, con la solución para cada una, y muestra cómo @capgo/background-geolocation las gestiona.
Cómo iOS gestiona las apps en segundo plano
Cuando el usuario desliza hacia la pantalla de inicio o bloquea el teléfono, iOS le da a la app una ventana breve y luego la suspende. Una app suspendida no recibe tiempo de CPU. Para una app de Capacitor, esto significa que tanto el lado nativo como el WebView dejan de ejecutarse, así que tu callback de JavaScript también deja de dispararse.
Apple hace una excepción con las apps que están usando la ubicación de forma activa. Si la app está configurada para ubicación en segundo plano y tiene una sesión de ubicación activa iniciada en primer plano, iOS la mantiene en ejecución y sigue entregando actualizaciones. Todo lo que sigue trata sobre cumplir ese contrato y sobre los casos en que el sistema lo rompe a propósito.
Causa 1: falta el modo de ubicación en segundo plano
Sin location en UIBackgroundModes, ninguna cantidad de code mantendrá el flujo de actualizaciones. Agrégala a ios/App/App/Info.plist:
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
También puedes habilitarlo en Xcode en Signing & Capabilities, Background Modes, Location updates. Es una clave de plist, no un entitlement, así que no hace falta cambiar el perfil de aprovisionamiento.
La propiedad nativa que hay detrás es allowsBackgroundLocationUpdates. Al configurarlo en true sin el background mode en el plist la app falla. El plugin Capgo lo configura por ti cuando pasas backgroundMessage a start(), así que la clave del plist debe estar antes de publicar.
App Review revisa que el background mode esté justificado. Explica en las notas de revisión qué funcionalidad lo usa y asegúrate de que el revisor pueda verla.
Causa 2: las actualizaciones se iniciaron en segundo plano
Regla de Apple: las actualizaciones en segundo plano continúan si se iniciaron mientras la app estaba en primer plano. Intentar iniciar una sesión desde un silent push, un background fetch o una BGTaskScheduler task no funciona para las actualizaciones de ubicación estándar.
Empieza el rastreo desde una acción del usuario o al reanudar la 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 no se pasó
Esto es específico del plugin Capgo y es el informe de error más común. Si backgroundMessage está indefinido, el plugin configura el gestor de ubicación solo para uso en primer plano: allowsBackgroundLocationUpdates permanece false y las actualizaciones se detienen cuando la app se suspende. Pasa un mensaje en ambas plataformas. En Android, también pasa a ser el texto de la notificación del foreground service.
Causa 4: nivel de autorización incorrecto o «Permitir una vez»
Muchos desarrolladores creen que necesitan «Siempre» para el rastreo en segundo plano. No es así. Apple documenta que una app con autorización «Al usar la app» sigue ejecutándose en segundo plano mientras tenga servicios de ubicación activos, siempre que las actualizaciones en segundo plano estén habilitadas.
«Siempre» es importante para otra cosa: relanzar una app terminada. Solo el monitoreo de regiones, los cambios significativos de ubicación y las visitas pueden relanzar una app, y solo con «Siempre».
Dos decisiones del usuario rompen el rastreo en segundo plano:
- Permitir una vez. Es un permiso temporal de tipo «Al usar la app» que expira cuando la app deja de usarse. La siguiente sesión vuelve a empezar desde «no determinado».
- El prompt «Al usar la app, luego Siempre» nunca aparece. Cuando solicitas «Siempre», iOS puede mostrar el prompt de actualización más tarde, en el momento que elija, a menudo cuando la app no está en pantalla.
Verifica el estado real antes de iniciar una sesión:
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.
}
En iOS, backgroundLocation devuelve when_in_use o granted / always, para que puedas mostrar la explicación correcta en lugar de adivinar.
Causa 5: Core Location pausó las actualizaciones automáticamente
CLLocationManager.pausesLocationUpdatesAutomatically por defecto es true. Cuando iOS decide que el usuario no se mueve, pausa las actualizaciones para ahorrar batería. En una app When In Use, la pausa finaliza el acceso a la ubicación hasta que se vuelve a abrir la app, y Core Location no se reanuda por sí solo.
El plugin Capgo establece pausesLocationUpdatesAutomatically = false en su code nativo, por lo que esto ya está gestionado. Si escribes tu propio code nativo o usas otro plugin, comprueba esta propiedad primero.
Causa 6: el dispositivo está inmóvil y el filtro es grande
Con un distanceFilter de 50 metros, un teléfono sobre un escritorio no genera actualizaciones. En tus logs parece que el “seguimiento se detuvo”, pero funciona como está diseñado.
- Si necesitas heartbeats mientras estás inmóvil, usa un valor pequeño o cero
distanceFilter. - Usa
minIntervalMspara limitar la frecuencia de las POST nativas, en lugar de usar un filtro de distancia grande.
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: la ubicación precisa está desactivada
Desde iOS 14, los usuarios pueden desactivar la Ubicación precisa para tu app. Sigues recibiendo actualizaciones, pero son aproximadas (varios kilómetros) y llegan con mucha menos frecuencia. Detecta esto en tus datos (valores grandes de accuracy ) y explica al usuario por qué la ubicación precisa es importante para tu funcionalidad. Si publicas una app de reparto o fitness, muéstralo en el onboarding, no después de un viaje fallido.
Causa 8: el usuario cerró la app
Si el usuario desliza la app para cerrarla en el conmutador de apps, iOS detiene las actualizaciones de ubicación estándar. Ningún plugin puede mantenerlas activas. Esta es una sorpresa común para equipos que vienen de Android, donde un servicio en primer plano puede seguir funcionando después de que se elimina la tarea.
Opciones:
- Pide que no fuercen el cierre durante la sesión. La mayoría de apps de flota lo hace en onboarding.
- Añade geocercas con autorización Siempre alrededor de lugares importantes para que iOS pueda relanzar la app en segundo plano al entrar o salir.
- Envía un push para recordar al usuario que reabra la app cuando tu servidor deje de recibir puntos de una sesión.
Causa 9: JavaScript no está ejecutándose aunque el código nativo sí
Un matiz sutil. Native code puede recibir una ubicación mientras el WebView está suspendido, o mientras sigue activo pero no procesa timers ni llamadas de red con rapidez. Si envías puntos con fetch desde el callback, pueden llegar tarde o no llegar nunca.
Usa el del plugin url opción. Cada punto se envía mediante POST desde el code nativo con el mismo JSON que el Location tipo más "source": "native", independientemente del WebView. Usa el callback solo para actualizaciones de la UI. La entrega es best effort, sin cola en disco; si no puedes perder ningún punto, guárdalo en local cuando se dispare el callback y reconcilia después.
Para tareas en segundo plano más allá de la ubicación, lee cómo funcionan las tareas en segundo plano en Capacitor y consulta @capgo/capacitor-background-task.
Causa 10: Modo de bajo consumo y estado térmico
El Modo de bajo consumo reduce la actividad en segundo plano en todo el sistema. La ubicación sigue funcionando, pero otras cosas de las que depende tu sesión (reintentos de red, refresco en segundo plano) se ralentizan. Prueba con el Modo de bajo consumo activado antes de aprobar un release.
El indicador azul
Cuando una app con autorización 'When In Use' usa la ubicación en segundo plano, iOS muestra una píldora azul en la barra de estado. El plugin de Capgo establece showsBackgroundLocationIndicator a true para sesiones en segundo plano, que es lo que esperan los usuarios y lo que revisan los evaluadores. No intentes ocultarlo, los usuarios confían en ello.
Un plan de pruebas que detecte estos errores
Probar en el simulador no basta para la ubicación en segundo plano. Usa un iPhone real y:
- Crea una configuración de release, no solo de debug. Las builds de debug conectadas a Xcode reciben un tratamiento de ciclo de vida diferente.
- Inicia una sesión en primer plano, bloquea el teléfono y camina o conduce durante 20 minutos.
- Repite con “Permitir una vez”, con Ubicación precisa desactivada y con Modo de bajo consumo activado.
- Revisa los logs de tu servidor para
source: "native"puntos, no solo en el log de tu app. - Finaliza la sesión forzando el cierre y confirma que tu app explica qué ocurrió en el siguiente inicio.
Si necesitas un UDID de dispositivo para builds ad hoc, el Buscador de UDID de iOS lo obtiene desde Safari. Si no tienes un Mac con Xcode 26 para builds, Build de Capgo puede generar builds de iOS firmados en la nube.
Lista de verificación para resolver problemas
| Síntoma | Causa probable | Solución |
|---|---|---|
| Se detiene segundos después de bloquear | No location sin modo en segundo plano o no backgroundMessage |
Añade la clave plist y pasa backgroundMessage |
| Crash al iniciar | allowsBackgroundLocationUpdates sin modo en segundo plano |
Añadir UIBackgroundModes → location |
| Funciona una vez, luego no | El usuario eligió Permitir una vez | Explicar y volver a preguntar, o enviar a openSettings() |
| Se detiene cuando está estacionado | Grande distanceFilter |
Bájalo, usa minIntervalMs |
| Puntos muy separados, baja precisión | Ubicación precisa desactivada | Pide al usuario que lo active |
| Se detiene al deslizar para cerrar | El usuario cerró la app | Comportamiento normal en iOS; usa geofences para relanzar |
| El código nativo recibe los puntos, el servidor no | JavaScript fetch en el callback |
Usa la url opción |
Desplegando correcciones
La mayoría de estos fixes afectan a Info.plist o la configuración nativa, por lo que requieren un nuevo build para la App Store. Las partes de JavaScript, como las explicaciones de permisos, el copy de onboarding y la lógica de reintentos, pueden enviarse a los usuarios actuales con actualizaciones Live Update de Capgo una vez que el build nativo está listo.
Si estás decidiendo entre plugins, nuestra comparación de Transistorsoft y el plugin gratuito Capgo cubre las ventajas y desventajas.