Tracking Highly Accurate Location in iOS — (Vol.3) Background tracking, location accuracy, battery consumption, and architecture

Architecture of location management code

In the previous post, I made LocationService class which handles all the location stuff. I created a instance in AppDelegate’s didFinishLaunchingWithOptions, so we were confident that there was only one instance within the app.
But it is always good to design this class as singleton. 
I changed LocationService as below.

Then I made AppDelegate call startUpdatingLocation() via sharedInstance of LocationService class. By doing this we can avoid making multiple instances of LocationService class.

Tracking Location in Background

Most of location tracking apps like Pockemon GO need location update even when the app is in the background.
To make your app keep getting location information in the background, first add “location” to “UIBackgroundModes” key in the info.plist file. In the plist editor, the key-value pair should look like below.

Then set allowsBackgroundLocationUpdates flag of LocationManager class to true as below
 locationManager.allowsBackgroundLocationUpdates = true

Battery Consumption vs Background mode

There is another flag in LocationManager class which Apple encourages to turn on as;

locationManager.pausesLocationUpdatesAutomatically = true

pausesLocationUpdatesAutomatically will enable iOS to detect the situation where an app doesn’t need location update.

The behavior of auto pausing location update is controlled by the flag named activityType in LocationManager class.

You set CLActivityType to this value as the code below.

locationManager.activityType = .fitness

There are four CLActivityType options

  • AutomotiveNavigation (For navigating car drivers)
  • CLActivityTypeFitness (For users walking, running, or cycling)
  • CLActivityTypeOtherNavigation (For users moving on other types of vehicles such as a boat, a train or an airplane)
  • Other (For unknown type of motion or transportation)

For each type, iOS changes the rule to judge whether a user is “not moving, thus no new location is necessary”. 
It is not well documented, but I guess iOS uses the accelerometer to detect the user’s motion without GPS.

iOS starts updating locations when it detects the user is moving so new location information is necessary for the app.

The combination of pausesLocationUpdatesAutomatically flag and activityType looks a good way to get the user’s location in the background while saving battery consumption. However there is a pitfall here.

pausesLocationUpdatesAutomatically being true resumes location tracking only when the app is in the foreground.

If your user put the app in the background while jogging, and stopped for a while to chat with friends, check emails or answer the phone, the iOS suspends getting location since the user has stopped for a while at the same location. After that, when the user starts running, the app will not be able to resume tracking since the app is in the background.

Therefore, if your app’s aim is to keep logging the user’s location in the background, you should set false to pausesLocationUpdatesAutomatically flag as the sample code. (You don’t have to set activityType if you set pausesLocationUpdatesAutomatically to false.)

For a running app such as Nike+, the app must keep tracking the user in the background, so turn off auto pause (i.e. setting the flag to false).
For apps like Uber, it might be enough if the app can resume location tracking when the user reopens the app, in this case, it is wise to set this flag to true and save battery consumption.

Location Accuracy and Battery Consumption

LocationManager class has variable named desiredAccuracy, this is where the app tells LocationManager about what level of accuracy the app needs. 
For location tracking app such as Nike+, Pokemon Go, and Uber, it is the best to ask for the best accuracy. kCLLocationAccuracyBestForNavigation is the value for the best accuracy.

locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation

If your app needs to provide the user’s location real time like Pokemon Go, this is OK. But in many cases, it is enough for the app to update the user’s location when the user has moved certain amount of distance, say, 5 meters. It might be enough for Nike+ to refresh the map every time the user run more than 5 meters. It might be ok for Uber to update the car’s location on the map every 10 meters the car moves.

In this case, you have a chance to save battery power. 
LocationManager has variable named distanceFilter
You can set desired distance between each pair of obtained locations in meters. LocationManager tries to provide the app with location information when the user has moved more than this distance.

distanceFilter is not very accurate, even if the distanceFilter was set to 5 meters, you may get a location information even when you have moved less than 5 meters, or you might not get a location information till you have moved more than 6 meters. So it is practical to set a slightly smaller value than your target. For example if you want to have one location at least once every 10 meters, set 6 to 8 meters to the distance filter.

The distance filter works on the upper layer in the iOS than the GPS driver, thus doesn’t reduces the frequency of the iOS’s access to the GPS chip. This means we cannot save battery power consumed by the GPS chip. If you do heavy graphic processing or any other CPU intensive work each time new location is obtained, these processing will be reduced by the distance filter, thus batter power consumed for that app-side processing will be reduced.

This is the final initialization code of LocationService class.

Now location is updated even when the app is in the background (w/o auto pause). Location accuracy is the best of what iOS can do. The distance filter is 5 meters, so the app doesn’t get too many location callbacks.

Run the app, and put the app in the background, you’ll still see the location information being printed out to the log console of the XCode.

You can get the snapshot till here on the commit with the SHA-hash of
76ad5aed71600b1f306176baecf54e7ec9ddb892 in the repository below.
https://gist.github.com/mizutori/cded26da76994b45ece31c350f34d455

Next post — Display locations on the map


If you need any dev help..

I’m running an app development studio in Tokyo called Goldrush Computing.
If you need any development force to empower your app with location tech magic, feel free to contact me any time.
mizutori@goldrushcomputing.com