Location Monitoring in iOS
Location awareness is a powerful feature we often leverage when developing custom mobile Apps. Whether for delivering location-specific data, tagging user created content, or providing current position for mapping Apps, location awareness provides context and a personalized user experience.
Frequent requests for location data and a high degree of position accuracy will both result in the device location services running more continuously and drawing significant battery power. If the App tracks location in the background, this can cause rapid and, from the user’s perspective, unexpected battery drain. In a recent project, we had a requirement for continuous location monitoring, even when the App was in the background, so it was crucial to tailor the location monitoring strategy to conserve power while still maintaining an accurate enough position to achieve our desired user experience, which was to have the App respond to movements between geographic regions.
The iOS Core Location Framework provides several services we could choose from to meet our requirements:
- The significant-change location service, which provides a low-power way to get the current location and be notified when significant changes occur.
- The standard location service, which offers a highly configurable way to get the current location and track changes.
- Region monitoring that lets you monitor boundary crossings for defined geographical regions and Bluetooth low-energy beacon regions.
While region monitoring sounds very promising for our scenario (geographic regions!), unfortunately Core Location currently supports only circular regions (defined by a centre coordinate and a radius). In our scenario, the geographic regions were decidedly not circular, so we looked to the other options and arrived at a solution with a combination of significant-change location tracking while the App is in the background, and higher accuracy standard location tracking when the App is running in the foreground. This allowed the App to sufficiently “keep-up” with location monitoring when in the background, then have a position “correction” with high accuracy when the user began to interact with the App again.
To allow for location monitoring in the background, we specified the location value of the UIBackgroundModes key code in our App plist file (see update on this below). This can be set through Xcode by selecting the project in the Navigator pane, then checking the corresponding option under “Capabilities” as shown below:
JULY 1, 2014 UPDATE
As noted in Apple’s documentation for UIBackgroundModes, these keys should be used sparingly and only by apps providing the indicated services. Apple will enforce this during submission review. For location monitoring, the location background mode is only required to use the standard location service when the App is in the background. If you use only the signifiant location change interface to receive location events in the background, then the location background mode is not required.
In code, we used two CLLocationManager objects so we could start and stop the connection to the corresponding location services independent of one another:
CLLocationManager* foregroundLocationManager; CLLocationManager* backgroundLocationManager;
We instantiate these objects and set the necessary properties to control the location service behaviours:
backgroundLocationManager = [[CLLocationManager alloc] init]; backgroundLocationManager.delegate = self; backgroundLocationManager.activityType = CLActivityTypeAutomotiveNavigation; backgroundLocationManager.pausesLocationUpdatesAutomatically = YES; foregroundLocationManager = [[CLLocationManager alloc] init]; foregroundLocationManager.delegate = self;
For the background location monitoring, we set activityType to CLActivityTypeAutomotiveNavigation and pausesLocationUpdatesAutomatically to YES. These settings combine to let the location service monitoring pause when the device detects that it’s no longer moving as it would during vehicle travel. In our scenario we only needed an approximate location fix when the App was in the background, and if the device was not moving “fast” (i.e. being driven around), we could safely pause location updates. This turned out to save significantly on power consumption when the App is in the background, when users would not expect a large battery drain.
Next, we start the relevant type of location monitoring:
[backgroundLocationManager startMonitoringSignificantLocationChanges]; if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) { [self startForegroundMonitoring]; } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(startForegroundMonitoring) name:UIApplicationDidBecomeActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(stopForegroundMonitoring) name:UIApplicationDidEnterBackgroundNotification object:nil];
The call to startMonitoringSignificantLocationChanges starts our power-friendly significant location change monitoring that we’ll continue to run even when the App is in the background. We only start our higher-accuracy, and therefore higher power consumption, foreground monitoring if the App is in the active state (i.e. in the foreground). We also register to be notified when the App state changes, so we can turn foreground location monitoring on and off in our startForegroundMonitoring and stopForegroundMonitoring methods respectively:
- (void)startForegroundMonitoring { [foregroundLocationManager startUpdatingLocation]; }
- (void)stopForegroundMonitoring { [foregroundLocationManager stopUpdatingLocation]; }
When the App enters the foreground, the startUpdatingLocation method is called, starting the standard, high-accuracy location service monitoring. It takes a moment – usually a second or two – for the App to get a location fix, but this is OK since we’ve been monitoring in the background, and have an approximate position already. The foreground location fix ensured that if we were near a boundary transition, that the App would “correct” itself when the user began interacting with it. When the App returns to the background, the call to stopUpdatingLocation ensures we disconnect from the high-accuracy location service to avoid heavy battery utilization.