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


Estoy buscando una manera de obtener una actualización de 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, en función de las propiedades configuradas, pero no parece posible forzarlo a actualizar la ubicación cada n minutos
  • NSTimer: Funciona cuando la aplicación se está ejecutando en primer plano, pero no parecen estar diseñados 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: Por lo que entiendo, esto debe usarse para terminar algún trabajo en segundo plano (también limitado en el tiempo) cuando una aplicación se mueve al segundo plano en lugar de implementar procesos en segundo plano de "larga duración".

¿Cómo puedo implementar estas actualizaciones regulares de ubicación en segundo plano?

Author: Ashwini, 2011-06-14

15 answers

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

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

Esto funciona porque la ubicación es una de las 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.

 109
Author: wjans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-06 13:53:44

En iOS 8/9/10 para actualizar la ubicación en segundo plano cada 5 minutos, haga lo siguiente:

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

  2. Ir a Proyecto - > Info - > añadir 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 cualquier cosa con ellos cada 5 minutos implementarlo como en el código de abajo.

No estoy usando ninguna tarea en segundo plano ni temporizadores. He probado este código con mi dispositivo con iOS 8.1 que estaba tumbado en mi escritorio durante unas 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
 52
Author: Leszek Szary,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-25 18:42:24

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 un método solo se puede llamar en un bucle 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 si este es el explicación correcta, pero así es como entendí de lo que leí]

En primer lugar, agregue el objeto location para la clave UIBackgroundModes en la información de su aplicación.plist. Ahora, lo que necesita 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, diga -(void)didUpdateToLocation:(CLLocation*)location, en el delegado de la aplicación. Luego implementa el método locationManager:didUpdateLocation:fromLocation de CLLocationManagerDelegate en la clase en la que iniciaste el administrador de ubicación (ya que configuramos el delegado del administrador de ubicación en 'self'). Dentro este método es necesario comprobar si el intervalo de tiempo después del cual usted tiene que manejar las actualizaciones de ubicación ha transcurrido. Puede hacer esto guardando la hora actual cada vez. Si ha transcurrido ese tiempo, llama al método UpdateLocation desde tu delegado de app:

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 agota la batería, si la precisión de sus datos de ubicación no es crítica, debe usar [locationManager startMonitoringSignificantLocationChanges]

Antes de añadir esto a su aplicación, por favor lea la Guía de Programación de Conocimiento de Ubicación

 35
Author: Bushra Shahid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-05 10:06:34

Ahora que iOS6 está fuera de la mejor manera de tener una siempre corriendo servicios de ubicación 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]);

}

Lo probé así:

Empecé la aplicación, ir en segundo plano y se mueven en el coche por algunos minutos. Luego me voy a casa durante 1 hora y empiezo a moverme de nuevo (sin abrir de nuevo la aplicación). Las ubicaciones comenzaron de nuevo. Luego se detuvo durante 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;
}
 22
Author: Alejandro Luengo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-08 18:05:38

A alguien más teniendo pesadilla averiguar este. Tengo una solución simple.

  1. mira este ejemplo de raywenderlich.com -> tener código de ejemplo, esto funciona perfectamente, pero desafortunadamente no hay temporizador durante la ubicación en segundo plano. 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 te olvides de agregar" Registros de aplicaciones para actualizaciones de ubicación " en info.plist.

 13
Author: HelmiB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-01 15:41:03

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 solo lugar, es invisible para los servicios de ubicación.

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

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

  • Iniciar y detener los servicios hace que se llame al método delegado didUpdateToLocation, pero el newLocation puede tener una marca de tiempo antigua.

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

  • Cuando se ejecuta en segundo plano, tenga en cuenta que puede ser difícil obtener soporte "completo" de LocationServices aprobado por Apple. Por lo que he visto, han diseñado específicamente startMonitoringSignificantLocationChanges como una alternativa de bajo consumo para aplicaciones que necesitan ubicación en segundo plano apoyar, y animar encarecidamente a los desarrolladores a utilizar esto a menos que la aplicación absolutamente lo necesita.

¡Buena Suerte!

ACTUALIZACIÓN: Estos pensamientos pueden estar desactualizados por ahora. Parece que la gente está teniendo éxito con la respuesta de @wjans, arriba.

 5
Author: Chazbot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-28 08:59:49

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

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

Lo que hice fue:{[14]]}

Requerido: Registrar 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 mantener la aplicación ejecutada para siempre usando el método allowDeferredLocationUpdatesUntilTraveled:timeout en segundo plano, debe reiniciar updatingLocation con un nuevo parámetro cuando la aplicación se mueva al segundo plano, de la siguiente manera:

- (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 ubicaciones actualizadas de forma normal 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 usted 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 debemos restablecer los parámetros de LocationManager cada vez que la aplicación cambia entre el modo de fondo/forground.

 5
Author: samthui7,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-12 07:26:57
if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
    [self.locationManager setAllowsBackgroundLocationUpdates:YES];
}

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

 5
Author: Nilesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-13 06:48:34

Esto es lo que 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()
 5
Author: hmitkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-06 13:36:42

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

Mantengo la ubicación funcionando continuamente con los siguientes ajustes:

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

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

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

Obtener mi ubicación y luego, una vez que tengo la ubicación, vuelvo a bajar la precisión para minimizar el drenaje de la batería. He escrito una muestra completa de trabajo de esto y también he escrito la fuente para el 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 los 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 funcionan correctamente en segundo plano. El proyecto tiene licencia MIT.

El proyecto iOS está dirigido a iOS 6 utilizando un SDK base de iOS 7. Puede obtener el código aquí.

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

 4
Author: nickfox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-09 14:52:51

Parece que stopUpdatingLocation es lo que activa el temporizador de vigilancia en segundo plano, así que lo reemplacé en didUpdateLocation con:

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

Que parece efectivamente apagar el GPS. El selector para el NSTimer en segundo plano se convierte entonces en:

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

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

 2
Author: Amit Shelgaonkar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-30 17:52:11

Hay un cocoapod 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.

 2
Author: sash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-29 14:40:56

Código de Trabajo (Todo el Código Paso a Paso)

Paso 1

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

Paso 2

Añade este código a AppDelegate.m

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

Paso 3 Añadir Este código en applicationDidEnterBackground método 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
}
 1
Author: Hari R Krishna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-06 07:42:25

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.

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

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

 0
Author: Malcolm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-17 17:31:36

Se adjunta una solución Rápida basada en:

Define App registers for location updates en la información.plist

Mantenga el LocationManager funcionando todo el tiempo

Cambie kCLLocationAccuracy entre BestForNavigation (durante 5 segundos para obtener la ubicación) y ThreeKilometers durante 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 una opción 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 MapView (LocationManager apunta al objeto en 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")
        }
    }
 -1
Author: eharo2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-25 22:59:44