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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With