pcoving pcoving - 1 year ago 140
iOS Question

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

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

Answer Source

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.