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

Estoy tratando de implementar las sugerencias dadas en esta publicación .

Lamentablemente, los pasos no están claros para mí. Intenté implementar esas sugerencias, pero el backgroundTimeRemaining continúa disminuyendo incluso después de iniciar y detener LocationServices. Así es como lo desarrollé:

- (void)applicationDidEnterBackground:(UIApplication *)application { UIApplication* app = [UIApplication sharedApplication]; bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; // Start the long-running task and return immediately. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Do the work associated with the task. self.timer = nil; [self initTimer]; }); } 

initTimer:

 - (void)initTimer { // Create the location manager if this object does not // already have one. if (nil == self.locationManager) self.locationManager = [[CLLocationManager alloc] init]; self.locationManager.delegate = self; [self.locationManager startMonitoringSignificantLocationChanges]; if (self.timer == nil) { self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(checkUpdates:) userInfo:nil repeats:YES]; } } 

revisa actualizaciones:

 - (void)checkUpdates:(NSTimer *)timer{ UIApplication* app = [UIApplication sharedApplication]; double remaining = app.backgroundTimeRemaining; if(remaining < 580.0) { [self.locationManager startUpdatingLocation]; [self.locationManager stopUpdatingLocation]; [self.locationManager startMonitoringSignificantLocationChanges]; } DbgLog(@"Reminaing %f", app.backgroundTimeRemaining); } 

¿Alguien tiene alguna sugerencia sobre lo que podría estar mal en mi código? Se están llamando tanto a initTimer como a CheckUpdates, pero solo durante el tiempo de ejecución de fondo (+ – 10 Mins). Quiero que la aplicación actualice la ubicación cada n minutos “para siempre”.

El UIBackgroundModes de mi aplicación está configurado en la ubicación.

ACTUALIZAR:

Ahora estoy restableciendo el temporizador en didUpdateToLocation y didFailWithError. Pero aún así, el backgroundTimeRemaining sigue disminuyendo:

 - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { NSLog(@"Did Update Location = %f / %f", [newLocation coordinate].latitude, [newLocation coordinate].longitude); UIApplication* app = [UIApplication sharedApplication]; bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; // Start the long-running task and return immediately. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Do the work associated with the task. [self initTimer]; }); } - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { [self.locationManager stopUpdatingLocation]; UIApplication* app = [UIApplication sharedApplication]; bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; // Start the long-running task and return immediately. [self initTimer]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Do the work associated with the task. }); } 

También estoy invalidando el temporizador:

 - (void)checkUpdates:(NSTimer *)timer{ UIApplication* app = [UIApplication sharedApplication]; double remaining = app.backgroundTimeRemaining; if(remaining  570.0) { [self.timer invalidate]; self.timer = nil; [self.locationManager startUpdatingLocation]; [self.locationManager stopUpdatingLocation]; } DbgLog(@"*************************Checking for updates!!!!!!!!!!! Reminaing %f", app.backgroundTimeRemaining); } 

Para cualquier otra persona que tenga una pesadilla de tiempo tratando de resolver esto, tengo una solución simple.

  1. Estudia el ejemplo de raywenderlich.com . El código de muestra funciona perfectamente, pero desafortunadamente no hay temporizador durante la ubicación de fondo. Esto se ejecutará indefinidamente.
  2. Agregar temporizador usando este fragmento de código:

     -(void)applicationDidEnterBackground { [self.locationManager stopUpdatingLocation]; UIApplication* app = [UIApplication sharedApplication]; bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; self.timer = [NSTimer scheduledTimerWithTimeInterval:intervalBackgroundUpdate target:self.locationManager selector:@selector(startUpdatingLocation) userInfo:nil repeats:YES]; } 
  3. Simplemente no olvide agregar “registros de aplicación para actualizaciones de ubicación” en info.plist.

Después de algunos días probando todas las soluciones posibles, finalmente pude hacerlo funcionar. Aquí está lo que estaba mal en mi solución:

  • Necesito configurar 2 UIBackgroundTaskIdentifiers, uno para el temporizador y otro para el locationManager

En mi solución, solo agregue:

 UIApplication *app = [UIApplication sharedApplication]; bgTask2 = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTask2]; bgTask2 = UIBackgroundTaskInvalid; }]; self.locationManager.delegate = self; [self.locationManager startUpdatingLocation]; 

Una vez que estás en segundo plano, solo puedes tener 10 minutos de tiempo adicional. No hay forma de extender eso. En su lugar, debe usar los servicios en segundo plano de ubicación.

Código de trabajo (Código paso a paso completo) Código modificado de scurioni

Paso 1

  • Vaya a proyecto -> Capacidades -> Modos de fondo -> seleccione Actualizaciones de ubicación.
  • Vaya a Proyecto -> Información -> agregue una clave NSLocationAlwaysUsageDescription con una cadena opcional.

Paso 2

Agregue este código a AppDelegate.m

 @interface AppDelegate () @property (strong, nonatomic) CLLocationManager *locationManager; @property (strong, nonatomic) NSTimer *timer; @end 

Paso 3 Agregue este código al método applicationDidEnterBackground en AppDelegate.m

  - (void)applicationDidEnterBackground:(UIApplication *)application { UIApplication *app = [UIApplication sharedApplication]; __block UIBackgroundTaskIdentifier bgTaskId = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTaskId]; bgTaskId = UIBackgroundTaskInvalid; }]; dispatch_async( dispatch_get_main_queue(), ^{ self.timer = nil; [self initTimer]; [app endBackgroundTask:bgTaskId]; bgTaskId = UIBackgroundTaskInvalid; }); } - (void)initTimer { if (nil == self.locationManager) self.locationManager = [[CLLocationManager alloc] init]; self.locationManager.delegate = self; [self.locationManager requestAlwaysAuthorization]; [self.locationManager startMonitoringSignificantLocationChanges]; if (self.timer == nil) { self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(checkUpdates:) userInfo:nil repeats:YES]; } } - (void)checkUpdates:(NSTimer *)timer{ UIApplication *app = [UIApplication sharedApplication]; double remaining = app.backgroundTimeRemaining; if(remaining < 580.0) { [self.locationManager startUpdatingLocation]; [self.locationManager stopUpdatingLocation]; [self.locationManager startMonitoringSignificantLocationChanges]; } } - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { NSLog(@"Did Update Location = %f / %f", [newLocation coordinate].latitude, [newLocation coordinate].longitude); [self updateLocationWithLatitude:[newLocation coordinate].latitude andLongitude:[newLocation coordinate].longitude]; UIApplication* app = [UIApplication sharedApplication]; __block UIBackgroundTaskIdentifier bgTask = bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self initTimer]; }); } - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { [self.locationManager stopUpdatingLocation]; UIApplication *app = [UIApplication sharedApplication]; __block UIBackgroundTaskIdentifier bgTask = bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; [self initTimer]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Do the work associated with the task }); } -(void)updateLocationWithLatitude:(CLLocationDegrees)latitude andLongitude:(CLLocationDegrees)longitude{ //Here you can update your web service or back end with new latitude and longitude }