iosobjective-ccore-locationbackground-taskios-background-mode

Periodic iOS background location updates


I'm writing an application that requires background location updates with high accuracy and low frequency. The solution seems to be a background NSTimer task that starts the location manager's updates, which then immediately shuts down. This question has been asked before:

How do I get a background location update every n minutes in my iOS application?

Getting user location every n minutes after app goes to background

iOS Not the typical background location tracking timer issue

iOS long-running background timer with "location" background mode

iOS full-time background-service based on location tracking

but I have yet to get a minimum example working. After trying every permutation of the above accepted answers, I put together a starting point. Entering background:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"ending background task");
        [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    }];


    self.timer = [NSTimer scheduledTimerWithTimeInterval:60
                                                  target:self.locationManager
                                                selector:@selector(startUpdatingLocation)
                                                userInfo:nil
                                                 repeats:YES];
}

and the delegate method:

- (void)locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
           fromLocation:(CLLocation *)oldLocation {

    NSLog(@"%@", newLocation);

    NSLog(@"background time: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
    [self.locationManager stopUpdatingLocation];

}

The current behavior is that the backgroundTimeRemaining decrements from 180 seconds to zero (while logging location), and then the expiration handler executes and no further location updates are generated. How do I modify the above code in order to receive periodic location updates in the background indefinitely?

Update: I'm targeting iOS 7 and there appears to be some evidence that background tasks behave differently:

Start Location Manager in iOS 7 from background task


Solution

  • It seems that stopUpdatingLocation is what triggers the background watchdog timer, so I replaced it in didUpdateLocation with:

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

    which appears to effectively power down the GPS. The selector for the background NSTimer then becomes:

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

    All I'm doing is periodically toggling the accuracy to get a high-accuracy coordinate every few minutes and because the locationManager hasn't been stopped, backgroundTimeRemaining stays at its maximum value. This reduced battery consumption from ~10% per hour (with constant kCLLocationAccuracyBest in the background) to ~2% per hour on my device.