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