ios, startMonitoringSignificantLocationChanges - sometimes breaks monitoring
cllocationmanager, ios
Solution
I can't say for sure if this is all of you problem but I would make the following changes:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Moved to didFinishLaunching... you should not be recreating every time app
// becomes active. You can check for locations key in the options Dictionary if
// it is necessary to alter your setup based on background launch
// Start location services
locationManager = [[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// Only report to location manager if the user has traveled 1000 meters
locationManager.distanceFilter = 1000.0f;
locationManager.delegate = self;
locationManager.activityType = CLActivityTypeAutomotiveNavigation;
// Start monitoring significant locations here as default, will switch to
// update locations on enter foreground
[locationManager startMonitoringSignificantLocationChanges];
// Hold in property to maintain reference
self.locationManager = locationManager;
}
Following your lead, this would be all that remains in your `didBecomeActive` method (I would use `willEnterForeground` instead):
- (void)willEnterForeground:(UIApplication *)application
{
[self.locationManager stopMonitoringSignificantLocationChanges];
[self.locationManager startUpdatingLocation];
}
Entering into background, go back to significant location changes only:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(@"Went to Background");
// Need to stop regular updates first
[self.locationManager stopUpdatingLocation];
// Only monitor significant changes
[self.locationManager startMonitoringSignificantLocationChanges];
}
In your delegate method, I recommend taking out all the conditional background testing. I am wrapping in background task identifier in case the app is in the background or goes into the background before finish. I am also responding on global thread so we don't block UI (optional):
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
UIApplication *app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier locationUpdateTaskID = [app beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (locationUpdateTaskID != UIBackgroundTaskInvalid) {
[app endBackgroundTask:locationUpdateTaskID];
locationUpdateTaskID = UIBackgroundTaskInvalid;
}
});
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// You might consider what to do here if you do not ever get a location accurate enough to pass the test.
// Also consider comparing to previous report and verifying that it is indeed beyond your threshold distance and/or recency. You cannot count on the LM not to repeat.
if ([[locations lastObject] horizontalAccuracy] < 100.0f) {
[self sendDataToServer:[locations lastObject]];
}
// Close out task Identifier on main queue
dispatch_async(dispatch_get_main_queue(), ^{
if (locationUpdateTaskID != UIBackgroundTaskInvalid) {
[app endBackgroundTask:locationUpdateTaskID];
locationUpdateTaskID = UIBackgroundTaskInvalid;
}
});
});
}
See my project, TTLocationHandler
on Github for some more examples of using Core Location the way you wish to use it.
Problem
I have strange problem with updating user's location. Sometimes my app updates location but from time to time breaks updating. I do not know where is problem, my code (from AppDelegate.m) is below: ``` - (void)applicationDidEnterBackground:(UIApplication *)application { NSLog(@"Went to Background"); // Only monitor significant changes [locationManager startMonitoringSignificantLocationChanges]; } - (void)applicationDidBecomeActive:(UIApplication *)application { // Start location services locationManager = [[CLLocationManager alloc] init]; locationManager.desiredAccuracy = kCLLocationAccuracyBest; // Only report to location manager if the user has traveled 1000 meters locationManager.distanceFilter = 1000.0f; locationManager.delegate = self; locationManager.activityType = CLActivityTypeAutomotiveNavigation; [locationManager stopMonitoringSignificantLocationChanges]; [locationManager startUpdatingLocation]; } - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { // Check if running in background or not BOOL isInBackground = NO; if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { isInBackground = YES; } if (isInBackground) { // If we're running in the background, run sendBackgroundLocationToServer [self sendBackgroundLocationToServer:[locations lastObject]]; } else { // If we're not in the background wait till the GPS is accurate to send it to the server if ([[locations lastObject] horizontalAccuracy] < 100.0f) { [self sendDataToServer:[locations lastObject]]; } } } -(void) sendBackgroundLocationToServer:(CLLocation *)location { bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [[UIApplication sharedApplication] endBackgroundTask:bgTask]; }]; // Send the data [self sendDataToServer:location]; if (bgTask != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; } } -(void) sendDataToServer:(CLLocation *)newLocation { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *lat = [NSString stringWithFormat:@"%.8f", newLocation.coordinate.latitude]; NSString *lng = [NSString stringWithFormat:@"%.8f", newLocation.coordinate.longitude]; [APIConnection SaveMyPositionWitLat:lat withLng:lng]; }); } ``` I need to update location every hour or if user change location more then 1000m. I chose the second way in order to save battery but my solution do not work as I need. I would greatly appreciate any help on this, Thanks in advance!