Inicie Location Manager en iOS 7 desde la tarea de fondo

Parece que en iOS 7 una aplicación ya no puede iniciar el Administrador de ubicaciones (llamando a startUpdatingLocation) desde la tarea en segundo plano.

En iOS 6 utilicé el enfoque descrito aquí: https://stackoverflow.com/a/6465280 para ejecutar la actualización de la ubicación de fondo cada n minutos. La idea era ejecutar tareas en segundo plano con un temporizador e iniciar el Administrador de ubicaciones cuando el temporizador lo activa. Después de eso, apague el Administrador de ubicaciones e inicie otra tarea en segundo plano.

Después de actualizar a iOS 7, este enfoque ya no funciona. Después de iniciar Location Manager, una aplicación no recibe ningún locationManager: didUpdateLocations. ¿Algunas ideas?

Encontré el problema / solución. Cuando llega el momento de iniciar el servicio de ubicación y detener la tarea en segundo plano, la tarea en segundo plano debe detenerse con un retraso (utilicé 1 segundo). De lo contrario, el servicio de ubicación no comenzará. Además, el servicio de localización debe dejarse ENCENDIDO durante un par de segundos (en mi ejemplo, son 3 segundos).

Otro aviso importante, el tiempo de fondo máximo en iOS 7 ahora es de 3 minutos en lugar de 10 minutos.

Actualizado el 29 de octubre de16

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.

Actualizado el 27 de mayo de 14

Ejemplo de Objective-C:

1) En el archivo “.plist” establece UIBackgroundModes en “ubicación”.

2) Cree una instancia de ScheduledLocationManager cualquier lugar que desee.

 @property (strong, nonatomic) ScheduledLocationManager *slm; 

3) Configurarlo

 self.slm = [[ScheduledLocationManager alloc]init]; self.slm.delegate = self; [self.slm getUserLocationWithInterval:60]; // replace this value with what you want, but it can not be higher than kMaxBGTime 

4) Implementar métodos delegates

 -(void)scheduledLocationManageDidFailWithError:(NSError *)error { NSLog(@"Error %@",error); } -(void)scheduledLocationManageDidUpdateLocations:(NSArray *)locations { // You will receive location updates every 60 seconds (value what you set with getUserLocationWithInterval) // and you will continue to receive location updates for 3 seconds (value of kTimeToGetLocations). // You can gather and pick most accurate location NSLog(@"Locations %@",locations); } 

Aquí está la implementación de ScheduledLocationManager:

ScheduledLocationManager.h

 #import  #import  @protocol ScheduledLocationManagerDelegate  -(void)scheduledLocationManageDidFailWithError:(NSError*)error; -(void)scheduledLocationManageDidUpdateLocations:(NSArray*)locations; @end @interface ScheduledLocationManager : NSObject  -(void)getUserLocationWithInterval:(int)interval; @end 

ScheduledLocationManager.m

 #import "ScheduledLocationManager.h" int const kMaxBGTime = 170; // 3 min - 10 seconds (as bg task is killed faster) int const kTimeToGetLocations = 3; // time to wait for locations @implementation ScheduledLocationManager { UIBackgroundTaskIdentifier bgTask; CLLocationManager *locationManager; NSTimer *checkLocationTimer; int checkLocationInterval; NSTimer *waitForLocationUpdatesTimer; } - (id)init { self = [super init]; if (self) { locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; locationManager.desiredAccuracy = kCLLocationAccuracyBest; locationManager.distanceFilter = kCLDistanceFilterNone; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; } return self; } -(void)getUserLocationWithInterval:(int)interval { checkLocationInterval = (interval > kMaxBGTime)? kMaxBGTime : interval; [locationManager startUpdatingLocation]; } - (void)timerEvent:(NSTimer*)theTimer { [self stopCheckLocationTimer]; [locationManager startUpdatingLocation]; // in iOS 7 we need to stop background task with delay, otherwise location service won't start [self performSelector:@selector(stopBackgroundTask) withObject:nil afterDelay:1]; } -(void)startCheckLocationTimer { [self stopCheckLocationTimer]; checkLocationTimer = [NSTimer scheduledTimerWithTimeInterval:checkLocationInterval target:self selector:@selector(timerEvent:) userInfo:NULL repeats:NO]; } -(void)stopCheckLocationTimer { if(checkLocationTimer){ [checkLocationTimer invalidate]; checkLocationTimer=nil; } } -(void)startBackgroundTask { [self stopBackgroundTask]; bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ //in case bg task is killed faster than expected, try to start Location Service [self timerEvent:checkLocationTimer]; }]; } -(void)stopBackgroundTask { if(bgTask!=UIBackgroundTaskInvalid){ [[UIApplication sharedApplication] endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; } } -(void)stopWaitForLocationUpdatesTimer { if(waitForLocationUpdatesTimer){ [waitForLocationUpdatesTimer invalidate]; waitForLocationUpdatesTimer =nil; } } -(void)startWaitForLocationUpdatesTimer { [self stopWaitForLocationUpdatesTimer]; waitForLocationUpdatesTimer = [NSTimer scheduledTimerWithTimeInterval:kTimeToGetLocations target:self selector:@selector(waitForLoactions:) userInfo:NULL repeats:NO]; } - (void)waitForLoactions:(NSTimer*)theTimer { [self stopWaitForLocationUpdatesTimer]; if(([[UIApplication sharedApplication ]applicationState]==UIApplicationStateBackground || [[UIApplication sharedApplication ]applicationState]==UIApplicationStateInactive) && bgTask==UIBackgroundTaskInvalid){ [self startBackgroundTask]; } [self startCheckLocationTimer]; [locationManager stopUpdatingLocation]; } #pragma mark - CLLocationManagerDelegate methods - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { if(checkLocationTimer){ //sometimes it happens that location manager does not stop even after stopUpdationLocations return; } if (self.delegate && [self.delegate respondsToSelector:@selector(scheduledLocationManageDidUpdateLocations:)]) { [self.delegate scheduledLocationManageDidUpdateLocations:locations]; } if(waitForLocationUpdatesTimer==nil){ [self startWaitForLocationUpdatesTimer]; } } - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { if (self.delegate && [self.delegate respondsToSelector:@selector(scheduledLocationManageDidFailWithError:)]) { [self.delegate scheduledLocationManageDidFailWithError:error]; } } #pragma mark - UIAplicatin notifications - (void)applicationDidEnterBackground:(NSNotification *) notification { if([self isLocationServiceAvailable]==YES){ [self startBackgroundTask]; } } - (void)applicationDidBecomeActive:(NSNotification *) notification { [self stopBackgroundTask]; if([self isLocationServiceAvailable]==NO){ NSError *error = [NSError errorWithDomain:@"your.domain" code:1 userInfo:[NSDictionary dictionaryWithObject:@"Authorization status denied" forKey:NSLocalizedDescriptionKey]]; if (self.delegate && [self.delegate respondsToSelector:@selector(scheduledLocationManageDidFailWithError:)]) { [self.delegate scheduledLocationManageDidFailWithError:error]; } } } #pragma mark - Helpers -(BOOL)isLocationServiceAvailable { if([CLLocationManager locationServicesEnabled]==NO || [CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied || [CLLocationManager authorizationStatus]==kCLAuthorizationStatusRestricted){ return NO; }else{ return YES; } } @end 

Probé tu método pero no funcionó de mi lado. ¿Me puedes mostrar tu código?

De hecho, encontré una solución para resolver el problema del servicio de ubicación en iOS 7.

En iOS 7, no puede iniciar el servicio de ubicación en segundo plano. Si desea que el servicio de ubicación continúe ejecutándose en segundo plano, debe iniciarlo en primer plano y continuará ejecutándose en segundo plano.

Si fueras como yo, detén el servicio de ubicación y usa el temporizador para reiniciarlo en segundo plano, NO funcionará en iOS 7.

Para obtener información más detallada, puede ver los primeros 8 minutos del video 307 de la WWDC 2013: https://developer.apple.com/wwdc/videos/

Actualización: el servicio de ubicación también puede funcionar en segundo plano . Compruebe los servicios de ubicación en segundo plano que no funcionan en iOS 7 para la publicación actualizada con la solución completa publicada en Github y una publicación en el blog que explica los detalles.

Los pasos para implementar esto son los siguientes:

  1. Agregue “Registros de aplicación para actualizaciones de ubicación” en el elemento 0 en “Modos de fondo requeridos” en info.plist de su proyecto.

  2. Escriba el código siguiente en la aplicación si terminó de lanzarse.

     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(startFetchingLocationsContinously) name:START_FETCH_LOCATION object:nil]; 
  3. Escriba a continuación el código desde el que desea iniciar el seguimiento

     [[NSNotificationCenter defaultCenter] postNotificationName:START_FETCH_LOCATION object:nil]; AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate]; [appDelegate startUpdatingDataBase]; 
  4. Pegue el siguiente código en AppDelegate.m

     #pragma mark - Location Update -(void)startFetchingLocationsContinously{ NSLog(@"start Fetching Locations"); self.locationUtil = [[LocationUtil alloc] init]; [self.locationUtil setDelegate:self]; [self.locationUtil startLocationManager]; } -(void)locationRecievedSuccesfullyWithNewLocation:(CLLocation*)newLocation oldLocation:(CLLocation*)oldLocation{ NSLog(@"location received successfullly in app delegate for Laitude: %f and Longitude:%f, and Altitude:%f, and Vertical Accuracy: %f",newLocation.coordinate.latitude,newLocation.coordinate.longitude,newLocation.altitude,newLocation.verticalAccuracy); } -(void)startUpdatingDataBase{ UIApplication* app = [UIApplication sharedApplication]; bgTask = UIBackgroundTaskInvalid; bgTask = [app beginBackgroundTaskWithExpirationHandler:^(void){ [app endBackgroundTask:bgTask]; }]; SAVE_LOCATION_TIMER = [NSTimer scheduledTimerWithTimeInterval:300 target:self selector:@selector(startFetchingLocationsContinously) userInfo:nil repeats:YES]; } 
  5. Agregue una clase por nombre “LocationUtil” y pegue el siguiente código en el archivo de encabezado:

     #import  #import  @protocol LocationRecievedSuccessfully  @optional -(void)locationRecievedSuccesfullyWithNewLocation:(CLLocation*)newLocation oldLocation:(CLLocation*)oldLocation; -(void)addressParsedSuccessfully:(id)address; @end @interface LocationUtil : NSObject  { } //Properties @property (nonatomic,strong) id delegate; -(void)startLocationManager; 

    Y pegue el siguiente código en LocationUtil.m

     -(void)startLocationManager{ locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; [locationManager setPausesLocationUpdatesAutomatically:YES]; //Utkarsh 20sep2013 //[locationManager setActivityType:CLActivityTypeFitness]; locationManager.distanceFilter = kCLDistanceFilterNone; locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation; [locationManager startUpdatingLocation]; //Reverse Geocoding. geoCoder=[[CLGeocoder alloc] init]; //set default values for reverse geo coding. } //for iOS<6 - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { //call delegate Method [delegate locationRecievedSuccesfullyWithNewLocation:newLocation oldLocation:oldLocation]; NSLog(@"did Update Location"); } //for iOS>=6. - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { CLLocation *newLocation = [locations objectAtIndex:0]; CLLocation *oldLocation = [locations objectAtIndex:0]; [delegate locationRecievedSuccesfullyWithNewLocation:newLocation oldLocation:oldLocation]; NSLog(@"did Update Locationsssssss"); }