Background Location Services not working in iOS 7
Here is the solution that I used to get continuous location from iOS 7 devices no matter it is in foreground or background.
You may find the full solution and explanation from blog and also github:-
Methods and Explanation:-
I restart the location manager every 1 minute in function didUpdateLocations
I allow the location manager to get the locations from the device for 10 seconds before shut it down (to save battery).
Partial Code Below:-
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{for(int i=0;i<locations.count;i++){ CLLocation * newLocation = [locations objectAtIndex:i]; CLLocationCoordinate2D theLocation = newLocation.coordinate; CLLocationAccuracy theAccuracy = newLocation.horizontalAccuracy; NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow]; if (locationAge > 30.0) continue; //Select only valid location and also location with good accuracy if(newLocation!=nil&&theAccuracy>0 &&theAccuracy<2000 &&(!(theLocation.latitude==0.0&&theLocation.longitude==0.0))){ self.myLastLocation = theLocation; self.myLastLocationAccuracy= theAccuracy; NSMutableDictionary * dict = [[NSMutableDictionary alloc]init]; [dict setObject:[NSNumber numberWithFloat:theLocation.latitude] forKey:@"latitude"]; [dict setObject:[NSNumber numberWithFloat:theLocation.longitude] forKey:@"longitude"]; [dict setObject:[NSNumber numberWithFloat:theAccuracy] forKey:@"theAccuracy"]; //Add the vallid location with good accuracy into an array //Every 1 minute, I will select the best location based on accuracy and send to server [self.shareModel.myLocationArray addObject:dict]; }}//If the timer still valid, return it (Will not run the code below)if (self.shareModel.timer) return;self.shareModel.bgTask = [BackgroundTaskManager sharedBackgroundTaskManager];[self.shareModel.bgTask beginNewBackgroundTask];//Restart the locationMaanger after 1 minuteself.shareModel.timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(restartLocationUpdates) userInfo:nil repeats:NO];//Will only stop the locationManager after 10 seconds, so that we can get some accurate locations//The location manager will only operate for 10 seconds to save batteryNSTimer * delay10Seconds;delay10Seconds = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(stopLocationDelayBy10Seconds) userInfo:nil repeats:NO]; }
Update on May 2014: I got a few requests for adding sample codes on sending the location to server for a certain time interval. I have added the sample codes and also combined a fix for BackgroundTaskManager to solve a glitch for background running over an extended period of time. If you have any questions, you are welcomed to join us for a discussion here: Background Location Update Programming for iOS 7 with Location Update to Server Discussion
Update on January 2015: If you want to get the location update even when the app is suspended, please see: Get Location Updates for iOS App Even when Suspended
If you look at WWDC 2013 Session video #204 - What's new with multitasking pdf, page number 15 clearly mentions that apps wont launch in the background if user kills it from the app switcher. Please see the image,
I think they made an optimization (probably using motion sensors), to detect "relatively" stationary positioning of the phone and they stop the location updates. This is only a speculation, but my tests currently show:
- Starting location updates; (tested with accuracy of 10 and 100 meters, 3 times each)
- Turn device's screen off to put the app in the background;
- Leave the device stationary (e.g. on a desk) for 30 min.
The data I log shows the geo-updates stop coming after ~ 15m and 30s. With that all other background processing you do is also terminated.
My device is iPhone 5 with iOS 7.
I am 95% sure, this wasn't the case on iOS 6/6.1. Where getting geo updates with 100m accuracy used to give you pretty much continuous running in background.
Update
If you restart the location manager every 8 minutes, it should run continuously.
Update #2
I haven't tested this in latest, but this is how I restarted it when I wrote the post. I hope this is helpful.
- (void)tryRestartLocationManager{ NSTimeInterval now = [[NSDate date] timeIntervalSince1970]; int seconds = round(floor(now - locationManagerStartTimestamp)); if ( seconds > (60 * 8) ) { [locationManager stopUpdatingLocation]; [locationManager startUpdatingLocation]; locationManagerStartTimestamp = now; }}