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




7 Answers

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
ios objective-c core-location background-process

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?




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;
}



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.




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.




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.




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.




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




Related