Actualizaciones periódicas de ubicación de fondo de iOS

Estoy escribiendo una aplicación que requiere actualizaciones de ubicación de fondo con alta precisión y baja frecuencia . La solución parece ser una tarea NSTimer de fondo que inicia las actualizaciones del administrador de ubicación, que luego se apaga inmediatamente. Esta pregunta se ha hecho antes:

¿Cómo obtengo una actualización de ubicación en segundo plano cada n minutos en mi aplicación iOS?

Obtener la ubicación del usuario cada n minutos después de que la aplicación vaya al fondo

iOS No es el típico problema de temporizador de seguimiento de ubicación de fondo

Temporizador de fondo de larga duración de iOS con modo de fondo de “ubicación”

Servicio en segundo plano de iOS a tiempo completo basado en el seguimiento de la ubicación

pero todavía tengo que obtener un ejemplo mínimo de trabajo. Después de probar cada permutación de las respuestas aceptadas anteriormente, armé un punto de partida. Ingresando el fondo:

- (void)applicationDidEnterBackground:(UIApplication *)application { self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ NSLog(@"ending background task"); [[UIApplication sharedApplication] endBackgroundTask:self.bgTask]; self.bgTask = UIBackgroundTaskInvalid; }]; self.timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self.locationManager selector:@selector(startUpdatingLocation) userInfo:nil repeats:YES]; } 

y el método delegado:

 - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { NSLog(@"%@", newLocation); NSLog(@"background time: %f", [UIApplication sharedApplication].backgroundTimeRemaining); [self.locationManager stopUpdatingLocation]; } 

El comportamiento actual es que backgroundTimeRemaining disminuye de 180 segundos a cero (mientras se registra la ubicación), y luego se ejecuta el manejador de expiración y no se generan más actualizaciones de ubicación. ¿Cómo modifico el código anterior para recibir actualizaciones de ubicación periódicas en el fondo indefinidamente ?

Actualización : me estoy orientando a iOS 7 y parece que hay alguna evidencia de que las tareas en segundo plano se comportan de manera diferente:

Inicie Location Manager en iOS 7 desde la tarea de fondo

Parece que stopUpdatingLocation es lo que desencadena el temporizador de vigilancia de fondo, por lo que lo didUpdateLocation en didUpdateLocation con:

 [self.locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers]; [self.locationManager setDistanceFilter:99999]; 

que parece apagar eficazmente el GPS. El selector para el NSTimer fondo se convierte en:

 - (void) changeAccuracy { [self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest]; [self.locationManager setDistanceFilter:kCLDistanceFilterNone]; } 

Todo lo que hago es alternar periódicamente la precisión para obtener una coordenada de alta precisión cada pocos minutos y dado que el locationManager no se ha detenido, backgroundTimeRemaining mantiene en su valor máximo. Esto reduce el consumo de batería de ~ 10% por hora (con kCLLocationAccuracyBest constante en el fondo) a ~ 2% por hora en mi dispositivo.

Escribí una aplicación usando los servicios de ubicación, la aplicación debe enviar la ubicación cada 10 segundos. Y funcionó muy bien.

Simplemente use el método ” allowDeferredLocationUpdatesUntilTraveled: timeout “, siguiendo el documento de Apple.

Los pasos son los siguientes:

Requerido: registre el modo de fondo para actualizar la ubicación.

1. Cree LocationManger y startUpdatingLocation, con precisión y distancia filtrada como desee:

 -(void) initLocationManager { // Create the manager object self.locationManager = [[[CLLocationManager alloc] init] autorelease]; _locationManager.delegate = self; // This is the most important property to set for the manager. It ultimately determines how the manager will // attempt to acquire location and thus, the amount of power that will be consumed. _locationManager.desiredAccuracy = 45; _locationManager.distanceFilter = 100; // Once configured, the location manager must be "started". [_locationManager startUpdatingLocation]; } 

2. Para mantener la aplicación ejecutándose para siempre utilizando el método “allowDeferredLocationUpdatesUntilTraveled: timeout” en segundo plano, debe reiniciar la actualización de LocationLocation con un nuevo parámetro cuando la aplicación se mueva a un segundo plano, como este:

 - (void)applicationWillResignActive:(UIApplication *)application { _isBackgroundMode = YES; [_locationManager stopUpdatingLocation]; [_locationManager setDesiredAccuracy:kCLLocationAccuracyBest]; [_locationManager setDistanceFilter:kCLDistanceFilterNone]; _locationManager.pausesLocationUpdatesAutomatically = NO; _locationManager.activityType = CLActivityTypeAutomotiveNavigation; [_locationManager startUpdatingLocation]; } 

3. La aplicación se actualiza con las ubicaciones normales con “locationManager: didUpdateLocations:” callback:

 -(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { // store data CLLocation *newLocation = [locations lastObject]; self.userLocation = newLocation; //tell the centralManager that you want to deferred this updatedLocation if (_isBackgroundMode && !_deferringUpdates) { _deferringUpdates = YES; [self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10]; } } 

4. Pero debe manejar los datos en la callback “locationManager: didFinishDeferredUpdatesWithError:” para su propósito

 - (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error { _deferringUpdates = NO; //do something } 

5. NOTA: Creo que debemos restablecer los parámetros de LocationManager cada vez que la aplicación cambie entre el modo de fondo / falso.

  1. Si tiene los UIBackgroundModes en su plist con location clave de location entonces no necesita usar el método beginBackgroundTaskWithExpirationHandler . Eso es redundante. También lo estás usando incorrectamente (mira aquí ) pero eso es discutible ya que tu lista está establecida.

  2. Con la UIBackgroundModes location en la lista, la aplicación continuará ejecutándose en segundo plano indefinidamente solo mientras CLLocationManger esté ejecutando. Si llama a stopUpdatingLocation mientras está en segundo plano, la aplicación se detendrá y no comenzará de nuevo.

    Tal vez podrías llamar a beginBackgroundTaskWithExpirationHandler justo antes de llamar a stopUpdatingLocation y luego, después de llamar a startUpdatingLocation puedes llamar al endBackgroundTask para mantenerlo en segundo plano mientras el GPS está parado, pero nunca lo he intentado, es solo una idea.

    Otra opción (que no he probado) es mantener el administrador de ubicación en segundo plano, pero una vez que obtenga una ubicación precisa, cambie la propiedad de precisión desiredAccuracy a 1000 desiredAccuracy más para permitir que el chip GPS se apague (para ahorrar batería) . Luego, 10 minutos más tarde, cuando necesites otra actualización de ubicación, cambia la desiredAccuracy a 100 metros para encender el GPS hasta que obtengas una ubicación precisa, repite.

  3. Cuando llame a startUpdatingLocation en el administrador de ubicación, debe darle tiempo para obtener un puesto. No debe llamar inmediatamente a stopUpdatingLocation . Lo dejamos funcionar durante un máximo de 10 segundos o hasta que obtengamos una ubicación de alta precisión no almacenada en caché.

  4. Debe filtrar las ubicaciones en caché y verificar la precisión de la ubicación para asegurarse de que cumpla con la precisión mínima requerida (consulte aquí ). La primera actualización que reciba puede durar 10 minutos o 10 días. La primera precisión que obtienes puede ser 3000m.

  5. Considere utilizar las API de cambio de ubicación significativas en su lugar. Una vez que reciba la notificación de cambio significativo, puede iniciar CLLocationManager durante unos segundos para obtener una posición de alta precisión. No estoy seguro, nunca he usado los servicios de cambio de ubicación significativos.

Cuando llega el momento de iniciar el servicio de ubicación y detener la tarea de fondo, la tarea de fondo debe detenerse con un retraso (1 segundo debería ser suficiente). De lo contrario, el servicio de ubicación no comenzará. Además, el servicio de ubicación debe dejarse ENCENDIDO durante unos segundos (por ejemplo, 3 segundos).

Hay un cocoapod APScheduledLocationManager que permite obtener actualizaciones de ubicación de fondo cada n segundos con la precisión de ubicación deseada.

 let manager = APScheduledLocationManager(delegate: self) manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100) 

El repository también contiene una aplicación de ejemplo escrita en Swift 3.

¿Qué le parece intentarlo con startMonitoringSignificantLocationChanges: API? Definitivamente dispara con menos frecuencia y la precisión es razonablemente buena. Además, tiene muchas más ventajas que usar otras API de locationManager .

Mucho más con respecto a esta API ya se ha discutido en este enlace

Debe agregar el modo de actualización de ubicación en su aplicación agregando la siguiente clave en su info.plist.

 UIBackgroundModes  location  

didUpdateToLocation método didUpdateToLocation (incluso cuando su aplicación esté en segundo plano). Puede realizar cualquier cosa sobre la base de este método llamado

Este proyecto funcionará para administrar la ubicación en segundo plano.

UBICACIÓN ANTECEDENTES DE FONDO

se administrará automáticamente si la aplicación ingresa al fondo y necesita enviar una llamada de red para actualizar la ubicación.

Después de iOS 8, hay varios cambios en la recuperación y actualización de fondos relacionados con el marco CoreLocation realizado por Apple.

1) Apple introduce la solicitud AlwaysAuthorization para buscar la actualización de ubicación en la aplicación.

2) Apple presenta backgroundLocationupdates para buscar la ubicación desde el fondo en iOS.

Para recuperar la ubicación en segundo plano, debe habilitar la Actualización de ubicación en Capabilities of Xcode.

 // // ViewController.swift // DemoStackLocation // // Created by iOS Test User on 02/01/18. // Copyright © 2018 iOS Test User. All rights reserved. // import UIKit import CoreLocation class ViewController: UIViewController { var locationManager: CLLocationManager = CLLocationManager() override func viewDidLoad() { super.viewDidLoad() startLocationManager() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } internal func startLocationManager(){ locationManager.requestAlwaysAuthorization() locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.delegate = self locationManager.allowsBackgroundLocationUpdates = true locationManager.startUpdatingLocation() } } extension ViewController : CLLocationManagerDelegate { func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){ let location = locations[0] as CLLocation print(location.coordinate.latitude) // prints user's latitude print(location.coordinate.longitude) //will print user's longitude print(location.speed) //will print user's speed } }