9

I am working on an iOS tracer. It must run and receive locations even if the app is not in foreground, i.e. I use the background mode "Location updates". However, it would be fine to safe battery if possible. Especially, there is no need to receive locations if the device does not move.

  • I have tried to set the distanceFilter on CLLocationManager instance but it does not save power, it just reduces the number of location updates.
  • I cannot stop and start the location manager manually as the app would be suspended if in background.
  • I have tried to use location manager with pausesLocationUpdatesAutomatically set to YES (it is turned on by deafult) but if the app is in background and the location updates are paused, the app is suspended and does not wake up even if the device starts to move again.

Is there a way to save battery when I need to get location in background? The flag pausesLocationUpdatesAutomatically is very close to what I am looking for but suspending the app in background is a show stopper for me.

Cimlman
  • 3,404
  • 5
  • 25
  • 35

2 Answers2

17

Using "deferred location updates" as mentioned by Laky is probably the only possible way how to save power for an application that must run in the background and cannot be suspended. I just want to summarize what I have learnded when experimenting with this feature:

  • Pausing the app can be disallowed, i.e. the property pausesLocationUpdatesAutomatically can be set to NO on instance of CLLocationManager and deferred location updates will work anyway.

  • When calling allowDeferredLocationUpdatesUntilTraveled: timeout: some distance and some timeout must be specified as parameters. If you provide too low values, the feature will not take effect. Timeout of 80 seconds is too low, 90 seconds is OK. Distance of 90 meters is too low, 100 meters is OK. (tested on iPhone 5, iOS 8.4.1)

  • I am not sure if this feature takes effect if the app is just in background. I have not observe it so far. However, it can take effect if the screen is locked.

  • The feature does not take effect immediately after the screen is locked. You must wait a while. It was 30 - 150 seconds in my observations.

  • The system sometimes does not deliver location updates in batches anaway. My test app was running 13 hours in background with locked screen and 38% of this time it received locations one by one. Only the remaining 62% of the run time it received batches of locations (collection with at least two elements). The device did not move at all.

  • The feature will not take effect if the device is connected by docking cable to Mac and the app runs in debugger.

  • The feature is not supported by all devices, e.g. it is not supported by iPhone 4 or iPad 2.

  • I have made some measurements to test how much power is saved. I run an app on iPhone 5, iOS 8.4.1 in background with screen locked. The app just starts a CLLocationManager and saves statistics about received location data (using NSUSerDefaults) so that thy can be viewed on next app start. Wifi connection was disabled, cellular data enabled. Battery was fully chraged, no other app was running. The device did not move and it was laid on a place with GPS signal. With deferred location updates (with min. distance 900 meters and timeout 90 seconds), the battery was fully drained in 15.25 hours. Without deferred location updates, it took 13 hours.

Cimlman
  • 3,404
  • 5
  • 25
  • 35
  • great summary! Did you test it with all other apps killed and not using any background activity including location? – zakishaheen Aug 11 '16 at 23:11
6

What you looking for is this

- allowDeferredLocationUpdatesUntilTraveled:timeout:

If your app is in the background and the system is able to optimize its power usage, the location manager tells the GPS hardware to store new locations internally until the specified distance or timeout conditions are met. When one or both criteria are met, the location manager ends deferred locations by calling the locationManager:didFinishDeferredUpdatesWithError: method of its delegate and delivers the cached locations to the locationManager:didUpdateLocations: method.

ex;

[locationManager allowDeferredLocationUpdatesUntilTraveled:100.0f timeout:CLTimeIntervalMax];

So basically it will save some processing power by sending the location updates as an collection of location after specific time rather than firing the location update callback every time device register a movement.

And you can recieve the location update via the follwing callback method;

-(void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error
Laky
  • 632
  • 3
  • 10
  • It seems to be the way to go but I can't get it working. I call `allowDeferredLocationUpdatesUntilTraveled:timeout:` after receiving the first location but `locationManager:didFinishDeferredUpdatesWithError:` is never called. I also append formatted [NSDate date] to a label on the screen whenever `locationManager:didUpdateLocations:` is called. It is called each second regardless of anything. `[CLLocationManager deferredLocationUpdatesAvailable]` returns `YES` (iPhone 5). I run the app from Xcode (to install it to device), then stop it and run again from the home screen. What's wrong? – Cimlman Aug 24 '15 at 16:39
  • I am trying to get the idea of how deferring location updates works. 1. Should `pausesLocationUpdatesAutomatically` be set to `NO` or to `YES` on `CLLocationManager` instance? (neither worked in my app so far) 2. Does "deferring location updates" mean that the app will be suspended for most of the time instead of running continuously in background? 3. Does this feature take effect immediately when the app goes to background or do I have to wait e.g. couple of minutes with the app in background to observe some difference? Thanks. – Cimlman Aug 24 '15 at 19:28
  • @Cimlman sry for the late reply. for this to work, i forgot to mention this before... you also have to set [locationManager setPausesLocationUpdatesAutomatically:YES], locationManager.setDesiredAccuracy = kCLLocationAccuracyNearestTenMeters, and locationManager.distanceFilter = kCLDistanceFilterNone so the location manager will fire the -(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations delegate time to time as with collection of locations. Here's some help https://gist.github.com/eito/7275281 – Laky Aug 25 '15 at 04:01
  • 1
    Also, according to the Apple dev forum, this only works on iPhone 5 https://devforums.apple.com/message/751974#751974 – Laky Aug 25 '15 at 04:03
  • I must be doing some stupid mistake. I created an empty project and copied the source code from the link above (http://gist.github.com/eito/7275281). It does not work either, no multi-loction updates. I just created an App ID an Development Provisionong Profile to be able to launch the app on a real device (is there something special to be set here?), added "Location Updates" background mode in project capabilities and added authorization to the source code. What type of authorization should I use? Is `[self.locationManager requestAlwaysAuthorization]` OK? – Cimlman Aug 25 '15 at 11:22
  • Finally, it works. I made several mistakes. 1. I was too impatient. The app must be about 1 minute in background. Until that time, no deferred updates take effect. 2. I am not sure if the feature takes effect if the app is just sent to background by Home button. I observed deferred updates only if I locked the screen by HW button. 3. Timeout cannot be too low value. It works with timeout 100 seconds but it has no effect with timeout 15 seconds. This is btw. unacceptable for the app I am working on as it needs to send data to server several times per minute. – Cimlman Aug 25 '15 at 12:19
  • @Cimlman nice work! I didn't know about that 100 sec timeout issue, normally i setting it to the maximum. Also, now sure about why only it's working for HW button lock. Thanks for the update :) – Laky Aug 26 '15 at 04:56