StartUpdateLocations in Background, didUpdatingToLocation only called 10-20 times
Asked Answered
S

3

9

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?

Shelia answered 25/11, 2013 at 8:23 Comment(0)
B
27

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.

Bogy answered 10/12, 2013 at 19:36 Comment(5)
Your trick with "start in foreground and set to threekilometres" works, but it take instead of 10% battery in 24hours 50% battery in 24hours. This is much more! Damn it, why can't i just start the locationupdates in background for a specific region ... :( If you have other ideas let me know.Shelia
At least on my iPhone 5s, the battery usage seems to be moderate enough to be usable. I think the iOS uses the new M7 chip to check if the device moves, and if it is lying on a table the whole night, the iOS knows that the location can not have changed and therefore doesn’t even try to get location updates to save energy. On devices which do not have a M7 chip, the situation is different though. - I’d suggest to send a bugreport/feature request to Apple, maybe if enough developers are doing this, they consider going back to the old behavior...Bogy
Hi im using geofencing with startMonitoringSignificantLocationChanges.When significant location change in background mode then it get the current location call server and get the new locations,and it set to the geofencing.I think its ok with new apple policy.@Bogy can u tell me is it okCherycherye
This answer just saved my life... I wish I could upvote you 100 times!!Finite
The update on your answer was hugely helpful. Starting significant location change updates allowed me start updating location in the background. Also fwiw, I tested setting location to kCLLocationAccuracyThreeKilometers and doing nothing but showing a notification with the address every 30 minutes, and the battery drain was 1% over 24 hours on my iPhone X and iPhone 7.Ribaudo
T
0

In background timer will not run, so here your timer object will not respond, You need to create background task handler, check comment of below link,

How to run the timer in background of the application?

In background if you want to continue location service than you need to set pausesLocationUpdatesAutomatically flag, flag info on

pausesLocationUpdatesAutomatically flag info

  if ([self.locationManager respondsToSelector:@selector(pausesLocationUpdatesAutomatically)]) {
  self.locationManager.pausesLocationUpdatesAutomatically = NO;
  }

check my comment on Location service going to "Inactive" state in iPhone 5

For location manager there below are the CLLocationManagerDelegate methods for location update,

   - (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
       fromLocation:(CLLocation *)oldLocation [Deprecated in __IPHONE_6_0]

  - (void)locationManager:(CLLocationManager *)manager
 didUpdateLocations:(NSArray *)locations __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_6_0);

Where you found

  - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation ??
Tripersonal answered 25/11, 2013 at 8:26 Comment(6)
Thanks for the hint with the Timer, but I set the pauseLocationUpdateAutomatically to NO (see my example code above.) And the didUpdatingToLocation Method is only called 10-20 times with this initiation.Shelia
Why you have used _timer - (void) scheduleMethod:(NSTimer*)timer to stopUpdatingLocation ?Tripersonal
Becouse i have to make a movement statistic for 5 minutes if a geofence is entered (distance, speed,and after 5 minutes I will stopUpdateingLocation.., you are right that this wont work in background, so i have to do this with a background task handler. But my question is why it stops calling didupdateToLocation (the timer dont work, that i have learned now), so stopUpdatetingLocation never called... but it just stop updatetingShelia
I found it: developer.apple.com/library/mac/documentation/CoreLocation/… above is a literal error,i just use the first delegate method. but you are right, it is deprecated, ill try the other delegate method! If it works i will mark as solved. Thanks so far!Shelia
Whole method is like - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation, there is not half method - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation on that page either.Tripersonal
this is only a literal error, i use it with fromLocation:. but test the other method now. Edited this above.Shelia
A
0

Use below code in your didEnterRegion Method to start updates again

[_locationManager startMonitoringSignificantLocationChanges];
if ([_locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
{
    _locationManager.allowsBackgroundLocationUpdates =YES;

}
[_locationManager startUpdatingLocation];
Armrest answered 15/4, 2016 at 7:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.