Testing Device: iPhone 5 (iOS 7)
I have an app that uses RegionMonitoring
and updateLocation
. If a region is entered, didEnterRegion
is called as expected. Then I call startUpdatingLocation
. But the method didUpdateToLocation
is only called 10-20 times while it should update the location until the timer fires.
The relevant code:
CLLocationManager *_locationManager;
NSTimer *_timer;
-(void)initLocationManager
{
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
[_locationManager setActivityType:CLActivityTypeOther];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[_locationManager setPausesLocationUpdatesAutomatically:NO];
[_locationManager setDistanceFilter:kCLDistanceFilterNone];
}
//Did Enter Region, called as expected:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
[_locationManager startUpdatingLocation];
_timer = [NSTimer scheduledTimerWithTimeInterval:300.0f target:self selector:@selector(scheduleMethod:) userInfo:nil repeats:NO];
}
//Timer Fire Method:
- (void) scheduleMethod:(NSTimer*)timer
{
[Utils writeToLog:@"Timer-Stop"];
[_locationManager stopUpdatingLocation];
}
//This Method only called 10-20 Times (in the first 10-20 Seconds) and not the complete 5 Minutes:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
[Utils writeToLog:@"LocationUpdate!"];
}
So far I tried:
Restarting the Updates in the locationManagerDidPauseLocationUpdates
method, but it seems that this is never be called:
-(void)locationManagerDidPauseLocationUpdates:(CLLocationManager *)manager
{
[WRUtils writeToLog:@"LocationUpdate paused, restarted"];
[_locationManager startUpdatingLocation];
}
Check for errors in the didFailWithError
method, but this is not called either.
And played a bit with the properties:
[_locationManager setActivityType:CLActivityTypeOther];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBestForNavigation];
[_locationManager setPausesLocationUpdatesAutomatically:NO];
Plist settings are right I guess: Required background modes YES Required background Modes Item 0 App registers for location updates
How can I solve this?
Apple has introduced a new policy in iOS 7. iOS 7 does no longer deliver location updates in the background if you call „startUpdatingLocation“ while the App is in the background. You only get update when the App is brought to the foreground in this case.
When you’re using the geofencing feature, your App just gets a few seconds each time you get a RegionEntered/Exited notification to process this notification. And in these few seconds you may also get location updates. After these few seconds are over iOS 7 just suspends your App again.
You can use background task to get more than just a few seconds, but in iOS 7 Apple has also reduced the time Apps can run in the background from 10 minutes (iOS 6 and older) to only 3 minutes under iOS 7. It looks like that these 3 minutes are the total amount for the whole time the App is in the background. Which means you can not ask iOS 7 10 times to get 1 minute background time, you’ll only get 3 minutes in total, so after the 3rd time you’ve asked for a minute, your App won't get any background time anymore at all.
Your only chance in getting location updates in the background is to call „startUpdatingLocation“ while the App is in the foreground. This is sad especially when you only need location updates in response to Region(Enter/Exit messages, because you need to let the location updates running all the time. But you can at least reduce the battery usage by setting the accuracy value to kCLLocationAccuracyThreeKilometers when you do not need location updates and set the accuracy to kCLLocationAccuracyBest only when you really need the geo coordinates. The iOS won’t power up GPS for the kCLLocationAccuracyThreeKilometers value, so the battery usage would be moderate in this case.
Also the value kCLLocationAccuracyBestForNavigation for the accuracy seems to cause problems under IOS 7. I do not get any location updates with this value if the device is not connected to a an external power supply.
All in all the new iOS 7 policy for location updates is making it much harder to develop certain kinds of Apps. Instead of registering for location updates only when needed, you are forced to register for these for the while lifetime of your App. Which of course drains the battery a little bit faster, though Apple’s intension for this new policy was probably the opposite.
UPDATE:
After some more testing I’ve found some kind of solution for the problem. The docs from Apple mention that when using the Significant Location Change API, Apps can receive location updates in the background even if „startUpdatingLocation“ is started in the background.
My first tests didn’t work well. I was registering my App for significant location updates within the region monitoring delegate methods just before calling startUpdatingLocation (so this location service is only enabled when needed), but this still does not deliver location updates in the background as the docs would suggest.
But if you start listening for significant location changes directly after your App is launched (and never switch this off), you can call start „startUpdatingLocation“ while the App is in the background and also receive location updates in the background. The battery usage of having the "significant location change“ feature on all the time seems to be very low, so this will be probably the solution for most Apps.
You have to check if the „significant location change“ feature is available on the device, but it seems that all current devices do support this. Even the 5th generation of iPod Touch does support it (The iPod Touch can not use cell towers for location updates, which is the base method of this feature according to the docs from Apple, so I guess we can assume that all current devices running iOS 7 can use the „significant location update“ API. Though it’s probably a good idea to check if this feature is really available, maybe there are certain circumstances where the feature is not available).
Using the "significant location change“ API might have one disadvantage: The App can be launched in the background (if it was terminated in the background by the iOS to reuse its memory for other Apps) repeatedly and unnecessarily whenever the device has moved „significantly“ (according to the docs: when the cell tower has changed, but no more than once per 5 min). So Apps which only need to be activated, when a certain region is exited or entered, will be launched and informed about location changed all the time, not only at those regions. But I assume this should be still much better than having standard location updates active all the time.
My iPhone 5s drains the battery only 1% over night with the significant location changes active, instead of 12% with having the standard location updates active with the accuracy set to 3km.
Hope this helps all developers who are struggling with the new iOS 7 behavior.
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