I wrote an app the monitors a user's location. Location services are turned on when my view loads as such:
// Create the location manager if this object does not
// already have one.
if (nil == self.locationManager) {
self.locationManager = [[CLLocationManager alloc] init];
}
self.locationManager.delegate = self;
// Check for iOS 8. Without this guard the code will crash with "unknown selector" on iOS 7.
if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
[self.locationManager startMonitoringSignificantLocationChanges];
NSLog(@"Started monitoring significant location changes");
If I terminate the app while its active, the location services stop. Here is the code I wrote to stop the location services in the AppDelegate.m:
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also
applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
NSLog(@"entered terminate in delegate");
[myController.locationManager stopUpdatingLocation];
[myController.locationManager stopMonitoringSignificantLocationChanges];
myController.locationManager = nil;
[self saveContext];
}
I ran into a problem such that if my app is already in the background, the above method is not called and as such I could not turn off location services. To work around this, I found this solution which I tried:
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
UIApplication *app = [UIApplication sharedApplication];
if ([app respondsToSelector:@selector(beginBackgroundTaskWithExpirationHandler:)]) {
self.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 (self.bgTask != UIBackgroundTaskInvalid)
{
NSLog(@"Marking bgTask as Invalid when we entered background");
[app endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}
});
}];
}
}
So the above solution works if my app was in the background. However, I noticed that if leave my app running in the background for a long time, more than five minutes, the expiration handler kicks in. So then if I terminate the app without bringing it to the foreground. The location services icon still appears on the phone. I have to restart the app or bring it to the foreground first and then terminate it for the code that disables location services kicks in.
If I remove these two lines:
[app endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
Then stopping location services works find after five minutes while the debugger is attached. If I leave it running longer in the background then the terminate code never kicks in. Is it because I am not changing locations or does the app eventually die?
So my question is, is there another way to make sure that the app properly stops location service monitoring if its been in the background for a while?
Thank you...Amro
Edit, I did more experiments and here are my findings:
While attached to the debugger if I wait 11 minutes from time it enters background mode, the method willTerminate gets called:
2015-01-13 08:52:45.935 [441:37074] @@@AMRO--->applicationWillResignActive entered
2015-01-13 08:52:46.642 [441:37074] @@@AMRO--->Entered background mode
2015-01-13 08:55:42.697 [441:37074] @@@AMRO--->beginBackgroundTaskWithExpirationHandler called
2015-01-13 09:03:26.265 [441:37074] entered terminate in delegate
If I try this without debugger, and only wait four minutes, the terminate function does not get called, I don't have to wait the whole 11 minutes.