ver los las guardan donde descargas descargados control como centro carpetas archivos app aparece ios objective-c core-location background-process

los - ¿Cómo obtengo una actualización de ubicación de fondo cada n minutos en mi aplicación iOS?



itunes (15)

Ahora que iOS6 está disponible, la mejor manera de tener un servicio de ubicación para siempre es ...

- (void)applicationWillResignActive:(UIApplication *)application { /* Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. */ NSLog(@"to background"); app.isInBackground = TRUE; UIApplication *app = [UIApplication sharedApplication]; // Request permission to run in the background. Provide an // expiration handler in case the task runs long. NSAssert(bgTask == UIBackgroundTaskInvalid, nil); bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ // Synchronize the cleanup call on the main thread in case // the task actually finishes at around the same time. dispatch_async(dispatch_get_main_queue(), ^{ if (bgTask != UIBackgroundTaskInvalid) { [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. locationManager.distanceFilter = 100; locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; [locationManager startMonitoringSignificantLocationChanges]; [locationManager startUpdatingLocation]; NSLog(@"App staus: applicationDidEnterBackground"); // Synchronize the cleanup call on the main thread in case // the expiration handler is fired at the same time. dispatch_async(dispatch_get_main_queue(), ^{ if (bgTask != UIBackgroundTaskInvalid) { [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; } }); }); NSLog(@"backgroundTimeRemaining: %.0f", [[UIApplication sharedApplication] backgroundTimeRemaining]); }

Sólo lo probé así:

Comencé la aplicación, salgo al fondo y me muevo en el auto unos minutos. Luego me voy a casa por 1 hora y empiezo a moverme nuevamente (sin abrir la aplicación nuevamente). Las ubicaciones comenzaron de nuevo. Luego se detuvo por dos horas y comenzó de nuevo. Todo bien otra vez ...

NO OLVIDE USAR los nuevos servicios de ubicación en iOS6

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { CLLocation *loc = [locations lastObject]; // Lat/Lon float latitudeMe = loc.coordinate.latitude; float longitudeMe = loc.coordinate.longitude; }

Estoy buscando una forma de obtener una actualización de la ubicación en segundo plano cada n minutos en mi aplicación iOS. Estoy usando iOS 4.3 y la solución debería funcionar para iPhones sin jailbreak.

Probé / consideré las siguientes opciones:

  • CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges : esto funciona en segundo plano como se esperaba, según las propiedades configuradas, pero parece que no es posible forzarlo a actualizar la ubicación cada n minutos
  • NSTimer : funciona cuando la aplicación se ejecuta en primer plano pero no parece estar diseñada para tareas en segundo plano
  • Notificaciones locales: las notificaciones locales se pueden programar cada n minutos, pero no es posible ejecutar algún código para obtener la ubicación actual (sin que el usuario tenga que iniciar la aplicación a través de la notificación). Este enfoque tampoco parece ser un enfoque limpio, ya que no es para lo que se deben usar las notificaciones.
  • UIApplication:beginBackgroundTaskWithExpirationHandler : Según tengo entendido, esto debería usarse para finalizar algunos trabajos en segundo plano (también limitados en el tiempo) cuando una aplicación se traslada al fondo en lugar de implementar procesos de fondo de "larga ejecución".

¿Cómo puedo implementar estas actualizaciones de ubicación de fondo regulares?


Desafortunadamente, todas sus suposiciones parecen correctas, y no creo que haya una manera de hacer esto. Para ahorrar batería, los servicios de ubicación del iPhone se basan en el movimiento. Si el teléfono se encuentra en un lugar, es invisible para los servicios de ubicación.

El CLLocationManager solo llamará a locationManager:didUpdateToLocation:fromLocation: cuando el teléfono recibe una actualización de ubicación, lo que solo sucede si uno de los tres servicios de ubicación (cell tower, gps, wifi) percibe un cambio.

Algunas otras cosas que podrían ayudar a informar otras soluciones:

  • Al iniciar y detener los servicios, se llama al método delegado didUpdateToLocation , pero newLocation puede tener una marca de tiempo antigua.

  • El monitoreo de la región podría ayudar

  • Cuando se ejecute en segundo plano, tenga en cuenta que puede ser difícil obtener la compatibilidad con los Servicios de Localización "completos" aprobados por Apple. Por lo que he visto, han diseñado específicamente startMonitoringSignificantLocationChanges como una alternativa de bajo consumo para las aplicaciones que necesitan soporte de ubicación en segundo plano, y animan encarecidamente a los desarrolladores a usar esto a menos que la aplicación lo necesite por completo.

¡Buena suerte!

ACTUALIZACIÓN: Estos pensamientos pueden estar fuera de fecha por ahora. Parece que las personas están teniendo éxito con la respuesta de @wjans, arriba.


En iOS 8/9/10 para hacer una actualización de la ubicación de fondo cada 5 minutos, haga lo siguiente:

  1. Vaya a Proyecto -> Capacidades -> Modos de fondo -> seleccione Actualizaciones de ubicación

  2. Vaya a Proyecto -> Información -> agregue una clave NSLocationAlwaysUsageDescription con valor vacío (u opcionalmente cualquier texto)

  3. Para hacer que la ubicación funcione cuando su aplicación está en segundo plano y enviar coordenadas al servicio web o hacer algo con ellos cada 5 minutos, impleméntela como en el código a continuación.

No estoy usando ninguna tarea de fondo o temporizadores. He probado este código con mi dispositivo con iOS 8.1 que estaba en mi escritorio durante algunas horas con mi aplicación ejecutándose en segundo plano. El dispositivo estaba bloqueado y el código se ejecutaba correctamente todo el tiempo.

@interface LocationManager () <CLLocationManagerDelegate> @property (strong, nonatomic) CLLocationManager *locationManager; @property (strong, nonatomic) NSDate *lastTimestamp; @end @implementation LocationManager + (instancetype)sharedInstance { static id sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[self alloc] init]; LocationManager *instance = sharedInstance; instance.locationManager = [CLLocationManager new]; instance.locationManager.delegate = instance; instance.locationManager.desiredAccuracy = kCLLocationAccuracyBest; // you can use kCLLocationAccuracyHundredMeters to get better battery life instance.locationManager.pausesLocationUpdatesAutomatically = NO; // this is important }); return sharedInstance; } - (void)startUpdatingLocation { CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; if (status == kCLAuthorizationStatusDenied) { NSLog(@"Location services are disabled in settings."); } else { // for iOS 8 if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) { [self.locationManager requestAlwaysAuthorization]; } // for iOS 9 if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) { [self.locationManager setAllowsBackgroundLocationUpdates:YES]; } [self.locationManager startUpdatingLocation]; } } - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { CLLocation *mostRecentLocation = locations.lastObject; NSLog(@"Current location: %@ %@", @(mostRecentLocation.coordinate.latitude), @(mostRecentLocation.coordinate.longitude)); NSDate *now = [NSDate date]; NSTimeInterval interval = self.lastTimestamp ? [now timeIntervalSinceDate:self.lastTimestamp] : 0; if (!self.lastTimestamp || interval >= 5 * 60) { self.lastTimestamp = now; NSLog(@"Sending current location to web service."); } } @end


Encontré una solución para implementar esto con la ayuda de los foros de desarrolladores de Apple:

  • Especificar location background mode
  • Cree un NSTimer en segundo plano con UIApplication:beginBackgroundTaskWithExpirationHandler:
  • Cuando n es más pequeño que UIApplication:backgroundTimeRemaining funcionará bien. Cuando n es mayor , el location manager debe habilitarse (y deshabilitarse) nuevamente antes de que no quede más tiempo para evitar que se elimine la tarea de fondo.

Esto funciona porque la ubicación es uno de los tres tipos permitidos de ejecución en segundo plano .

Nota: Perdí algo de tiempo probando esto en el simulador donde no funciona. Sin embargo, funciona bien en mi teléfono.


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

Simplemente use el allowDeferredLocationUpdatesUntilTraveled:timeout " allowDeferredLocationUpdatesUntilTraveled:timeout ", siguiendo el documento de Apple.

Lo que hice son:

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

1. Crea LocationManger y startUpdatingLocation , con accuracy y filteredDistance como lo que quieras:

-(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 que la aplicación se ejecute para siempre utilizando allowDeferredLocationUpdatesUntilTraveled:timeout método de allowDeferredLocationUpdatesUntilTraveled:timeout en segundo plano, debe reiniciar la allowDeferredLocationUpdatesUntilTraveled:timeout updatingLocation con un nuevo parámetro cuando la aplicación pase a segundo plano, como esto:

- (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 obtiene las ubicaciones actualizadas normalmente con locationManager:didUpdateLocations: devolución de llamada:

-(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 entonces locationManager:didFinishDeferredUpdatesWithError: devolución de llamada para su propósito

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

5. NOTA: Creo que deberíamos restablecer los parámetros de LocationManager cada vez que la aplicación cambia entre el modo de fondo / forground.


Esto es lo que yo uso:

import Foundation import CoreLocation import UIKit class BackgroundLocationManager :NSObject, CLLocationManagerDelegate { static let instance = BackgroundLocationManager() static let BACKGROUND_TIMER = 150.0 // restart location manager every 150 seconds static let UPDATE_SERVER_INTERVAL = 60 * 60 // 1 hour - once every 1 hour send location to server let locationManager = CLLocationManager() var timer:NSTimer? var currentBgTaskId : UIBackgroundTaskIdentifier? var lastLocationDate : NSDate = NSDate() private override init(){ super.init() locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyKilometer locationManager.activityType = .Other; locationManager.distanceFilter = kCLDistanceFilterNone; if #available(iOS 9, *){ locationManager.allowsBackgroundLocationUpdates = true } NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.applicationEnterBackground), name: UIApplicationDidEnterBackgroundNotification, object: nil) } func applicationEnterBackground(){ FileLogger.log("applicationEnterBackground") start() } func start(){ if(CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedAlways){ if #available(iOS 9, *){ locationManager.requestLocation() } else { locationManager.startUpdatingLocation() } } else { locationManager.requestAlwaysAuthorization() } } func restart (){ timer?.invalidate() timer = nil start() } func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) { switch status { case CLAuthorizationStatus.Restricted: //log("Restricted Access to location") case CLAuthorizationStatus.Denied: //log("User denied access to location") case CLAuthorizationStatus.NotDetermined: //log("Status not determined") default: //log("startUpdatintLocation") if #available(iOS 9, *){ locationManager.requestLocation() } else { locationManager.startUpdatingLocation() } } } func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { if(timer==nil){ // The locations array is sorted in chronologically ascending order, so the // last element is the most recent guard let location = locations.last else {return} beginNewBackgroundTask() locationManager.stopUpdatingLocation() let now = NSDate() if(isItTime(now)){ //TODO: Every n minutes do whatever you want with the new location. Like for example sendLocationToServer(location, now:now) } } } func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { CrashReporter.recordError(error) beginNewBackgroundTask() locationManager.stopUpdatingLocation() } func isItTime(now:NSDate) -> Bool { let timePast = now.timeIntervalSinceDate(lastLocationDate) let intervalExceeded = Int(timePast) > BackgroundLocationManager.UPDATE_SERVER_INTERVAL return intervalExceeded; } func sendLocationToServer(location:CLLocation, now:NSDate){ //TODO } func beginNewBackgroundTask(){ var previousTaskId = currentBgTaskId; currentBgTaskId = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({ FileLogger.log("task expired: ") }) if let taskId = previousTaskId{ UIApplication.sharedApplication().endBackgroundTask(taskId) previousTaskId = UIBackgroundTaskInvalid } timer = NSTimer.scheduledTimerWithTimeInterval(BackgroundLocationManager.BACKGROUND_TIMER, target: self, selector: #selector(self.restart),userInfo: nil, repeats: false) } }

Comienzo el seguimiento en AppDelegate así:

BackgroundLocationManager.instance.start()


Hice esto en una aplicación que estoy desarrollando. Los temporizadores no funcionan cuando la aplicación está en segundo plano, pero la aplicación recibe constantemente las actualizaciones de ubicación. Leí en algún lugar de la documentación (parece que no puedo encontrarlo ahora, publicaré una actualización cuando lo haga) que se puede llamar a un método solo en un ciclo de ejecución activo cuando la aplicación está en segundo plano. El delegado de la aplicación tiene un bucle de ejecución activo incluso en el bg, por lo que no necesita crear el suyo propio para que esto funcione. [No estoy seguro de si esta es la explicación correcta pero así es como entendí por lo que leí]

En primer lugar, agregue el objeto de location para la clave UIBackgroundModes en la aplicación info.plist. Ahora, lo que debe hacer es iniciar las actualizaciones de ubicación en cualquier lugar de su aplicación:

CLLocationManager locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self;//or whatever class you have for managing location [locationManager startUpdatingLocation];

A continuación, escriba un método para manejar las actualizaciones de ubicación, digamos -(void)didUpdateToLocation:(CLLocation*)location , en el delegado de la aplicación. Luego, implemente el método locationManager:didUpdateLocation:fromLocation de CLLocationManagerDelegate en la clase en la que inició el administrador de ubicación (ya que configuramos el delegado del administrador de ubicación en ''self''). Dentro de este método, debe verificar si ha transcurrido el intervalo de tiempo después del cual tiene que manejar las actualizaciones de ubicación. Puedes hacer esto guardando la hora actual cada vez. Si ese tiempo ha transcurrido, llame al método UpdateLocation desde el delegado de su aplicación:

NSDate *newLocationTimestamp = newLocation.timestamp; NSDate *lastLocationUpdateTiemstamp; int locationUpdateInterval = 300;//5 mins NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; if (userDefaults) { lastLocationUpdateTiemstamp = [userDefaults objectForKey:kLastLocationUpdateTimestamp]; if (!([newLocationTimestamp timeIntervalSinceDate:lastLocationUpdateTiemstamp] < locationUpdateInterval)) { //NSLog(@"New Location: %@", newLocation); [(AppDelegate*)[UIApplication sharedApplication].delegate didUpdateToLocation:newLocation]; [userDefaults setObject:newLocationTimestamp forKey:kLastLocationUpdateTimestamp]; } } }

Esto llamará a tu método cada 5 minutos, incluso cuando tu aplicación esté en segundo plano. Imp: Esta implementación consume la batería; si la precisión de los datos de su ubicación no es crítica, debe usar [locationManager startMonitoringSignificantLocationChanges]

Antes de agregar esto a su aplicación, lea la Guía de programación de Reconocimiento de ubicación


Para alguien más que tiene pesadilla averiguar esto. Tengo una solución simple.

  1. vea este ejemplo en raywenderlich.com -> tenga código de muestra, esto funciona perfectamente, pero desafortunadamente no hay temporizador en la ubicación de fondo. Esto se ejecutará indefinidamente.
  2. Agregue el temporizador usando:

    -(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.


Usé el método de xs2bush de obtener un intervalo (usando timeIntervalSinceDate ) y lo timeIntervalSinceDate un poco. Quería asegurarme de que estaba obteniendo la precisión requerida que necesitaba y también que no estaba agotando la batería al mantener la radio gps más de lo necesario.

Mantengo la ubicación corriendo continuamente con la siguiente configuración:

locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers; locationManager.distanceFilter = 5;

Este es un drenaje relativamente bajo en la batería. Cuando estoy listo para obtener mi próxima lectura periódica de ubicación, primero verifico si la ubicación está dentro de la precisión deseada, si es así, luego uso la ubicación. Si no lo es, entonces incremento la precisión con esto:

locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters; locationManager.distanceFilter = 0;

obtener mi ubicación y luego, una vez que tenga la ubicación, vuelvo a reducir la precisión para minimizar el desgaste de la batería. He escrito una muestra completa de esto y también he escrito la fuente del código del lado del servidor para recopilar los datos de ubicación, almacenarlos en una base de datos y permitir a los usuarios ver datos gps en tiempo real o recuperar y ver rutas almacenadas previamente. Tengo clientes para iOS, Android, Windows Phone y Java me. Todos los clientes están escritos de forma nativa y todos funcionan correctamente en segundo plano. El proyecto tiene licencia MIT.

El proyecto iOS está dirigido a iOS 6 utilizando un SDK básico de iOS 7. Puede obtener el código here .

Por favor, presenta un problema en github si ves algún problema con él. Gracias.


Código de trabajo (Código escalonado completo)

Paso 1

  • Vaya al 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 ()<CLLocationManagerDelegate> @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 }


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

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

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

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

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


Se adjunta una solución Swift basada en:

Definir App registers for location updatesen el info.plist

Mantener el locationManager funcionando todo el tiempo.

Alterne kCLLocationAccuracyentre BestForNavigation(durante 5 segundos para obtener la ubicación) y ThreeKilometersdurante el resto del período de espera para evitar el drenaje de la batería

Este ejemplo actualiza la ubicación cada 1 minuto en primer plano y cada 15 minutos en segundo plano.

El ejemplo funciona bien con Xcode 6 Beta 6, que se ejecuta en un dispositivo iOS 7.

En el delegado de la aplicación (mapView es un controlador opcional que apunta al controlador mapView)

func applicationDidBecomeActive(application: UIApplication!) { if appLaunched! == false { // Reference to mapView used to limit one location update per timer cycle appLaunched = true var appDelegate = UIApplication.sharedApplication().delegate as AppDelegate var window = appDelegate.window var tabBar = window?.rootViewController as UITabBarController var navCon = tabBar.viewControllers[0] as UINavigationController mapView = navCon.topViewController as? MapViewController } self.startInitialPeriodWithTimeInterval(60.0) } func applicationDidEnterBackground(application: UIApplication!) { self.startInitialPeriodWithTimeInterval(15 * 60.0) } func startInitialPeriodWithTimeInterval(timeInterval: NSTimeInterval) { timer?.invalidate() // reset timer locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector("getFirstLocationUpdate:"), userInfo: timeInterval, repeats: false) } func getFirstLocationUpdate(sender: NSTimer) { let timeInterval = sender.userInfo as Double timer?.invalidate() mapView?.canReportLocation = true timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: Selector("waitForTimer:"), userInfo: timeInterval, repeats: true) } func waitForTimer(sender: NSTimer) { let time = sender.userInfo as Double locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation finalTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector("getLocationUpdate"), userInfo: nil, repeats: false) } func getLocationUpdate() { finalTimer?.invalidate() mapView?.canReportLocation = true }

En el mapView (locationManager apunta al objeto en el AppDelegate)

override func viewDidLoad() { super.viewDidLoad() var appDelegate = UIApplication.sharedApplication().delegate! as AppDelegate locationManager = appDelegate.locationManager! locationManager.delegate = self canReportLocation = true } func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) { if canReportLocation! { canReportLocation = false locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers } else { //println("Ignore location update") } }


En iOS 9 y watchOS 2.0 hay un nuevo método en CLLocationManager que le permite solicitar la ubicación actual: CLLocationManager: requestLocation (). Esto se completa inmediatamente y luego devuelve la ubicación al delegado de CLLocationManager.

Puede usar un NSTimer para solicitar una ubicación cada minuto con este método ahora y no tiene que trabajar con los métodos startUpdatingLocation y stopUpdatingLocation.

Sin embargo, si desea capturar ubicaciones según un cambio de X metros desde la última ubicación, simplemente configure la propiedad distanceFilter de CLLocationManger y X llame a startUpdatingLocation ().


Existe un APScheduledLocationManager de APScheduledLocationManager que permite obtener actualizaciones de ubicación en segundo plano cada n segundos con la precisión de ubicación deseada.

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

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


if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) { [self.locationManager setAllowsBackgroundLocationUpdates:YES]; }

Esto es necesario para el seguimiento de la ubicación en segundo plano desde iOS 9.