Guides and Sample Code

Developer

Energy Efficiency Guide for iOS Apps

On This Page

Reduce Location Accuracy and Duration

Using location-based information in your app is a great way to keep the user connected to the surrounding world. However, improper or unnecessary use of location can prevent the device from sleeping, keep location hardware powered up, drain the user’s battery, and create a poor user experience. Follow best practices to optimize use of location services for energy efficiency.

Request Quick Location Updates

If your app just needs a quick fix on the user’s location, it’s best to call the requestLocation method of the location manager object, as shown in Listing 14-1. Doing so automatically stops location services once the request has been fulfilled, letting location hardware power down if not being used elsewhere. Location updates requested in this manner are delivered by a callback to the locationManager:didUpdateLocations: delegate method, which you must implement in your app.

Listing 14-1Efficiently requesting a single location update

Objective-C

  1. -(void)viewDidLoad {
  2. // Create a location manager object
  3. self.locationManager = [[CLLocationManager alloc] init];
  4. // Set the delegate
  5. self.locationManager.delegate = self;
  6. }
  7. -(void)getQuickLocationUpdate {
  8. // Request location authorization
  9. [self.locationManager requestWhenInUseAuthorization];
  10. // Request a location update
  11. [self.locationManager requestLocation];
  12. // Note: requestLocation may timeout and produce an error if authorization has not yet been granted by the user
  13. }
  14. -(void)locationManager:(CLLocationManager *)manager
  15. didUpdateLocations:(NSArray *)locations {
  16. // Process the received location update
  17. }

Swift

  1. override func viewDidLoad() {
  2. // Create a location manager object
  3. self.locationManager = CLLocationManager()
  4. // Set the delegate
  5. self.locationManager.delegate = self
  6. }
  7. func getQuickLocationUpdate() {
  8. // Request location authorization
  9. self.locationManager.requestWhenInUseAuthorization()
  10. // Request a location update
  11. self.locationManager.requestLocation()
  12. // Note: requestLocation may timeout and produce an error if authorization has not yet been granted by the user
  13. }
  14. func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
  15. // Process the received location update
  16. }

Stop Location Services When You Aren’t Using Them

With the exception of navigation apps that offer turn-by-turn directions, most apps don’t need location services to be on all the time. Turn location services on only when they’re needed. Then, leave them on just long enough to get a location fix and turn them off again. Unless the user is in a moving vehicle, the current location shouldn’t change frequently enough to be an issue. You can always start location services again later if you need another update.

To stop standard location updates, call the stopUpdatingLocation method of the location manager object. See Listing 14-2.

Listing 14-2Stopping location updates when no longer needed

Objective-C

  1. -(void)getLocationUpdate {
  2. // Create a location manager object
  3. self.locationManager = [[CLLocationManager alloc] init];
  4. // Set the delegate
  5. self.locationManager.delegate = self;
  6. // Request location authorization
  7. [self.locationManager requestWhenInUseAuthorization];
  8. // Start location updates
  9. [self.locationManager startUpdatingLocation];
  10. }
  11. -(void)locationManager:(CLLocationManager *)manager
  12. didUpdateLocations:(NSArray *)locations {
  13. // Get a fix on the user's location
  14. ...
  15. // Stop location updates
  16. [self.locationManager stopUpdatingLocation];
  17. }

Swift

  1. func getLocationUpdate() {
  2. // Create a location manager object
  3. self.locationManager = CLLocationManager()
  4. // Set the delegate
  5. self.locationManager.delegate = self
  6. // Request location authorization
  7. self.locationManager.requestWhenInUseAuthorization()
  8. // Start location updates
  9. self.locationManager.startUpdatingLocation()
  10. }
  11. func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
  12. // Get a fix on the user's location
  13. ...
  14. // Stop location updates
  15. self.locationManager.stopUpdatingLocation()
  16. }

Reduce Accuracy of Standard Location Updates Whenever Possible

Standard location updates let you specify a degree of accuracy ranging from a few meters to a few kilometers by setting the desiredAccuracy property of the location manager object, as shown in Listing 14-3. Requesting higher accuracy than you need causes Core Location to power up additional hardware and waste power for unnecessary precision. Unless your app really needs to know the user’s position within a few meters, don’t set the accuracy level to best (kCLLocationAccuracyBest) or nearest ten meters (kCLLocationAccuracyNearestTenMeters). Also, be aware that Core Location typically provides more accurate data than you have requested. For example, when specifying an accuracy level of three kilometers (kCLLocationAccuracyThreeKilometers), you may receive accuracy within a hundred meters or so.

Listing 14-3Specifying accuracy for location updates

Objective-C

  1. -(void)getLocationUpdate {
  2. // Create a location manager object
  3. self.locationManager = [[CLLocationManager alloc] init];
  4. // Set the delegate
  5. self.locationManager.delegate = self;
  6. // Request location authorization
  7. [self.locationManager requestWhenInUseAuthorization];
  8. // Set an accuracy level. The higher, the better for energy.
  9. self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
  10. // Start location updates
  11. [self.locationManager startUpdatingLocation];
  12. }

Swift

  1. func getLocationUpdate() {
  2. // Create a location manager object
  3. self.locationManager = CLLocationManager()
  4. // Set the delegate
  5. self.locationManager.delegate = self
  6. // Request location authorization
  7. self.locationManager.requestWhenInUseAuthorization()
  8. // Set an accuracy level. The higher, the better for energy.
  9. self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
  10. // Start location updates
  11. self.locationManager.startUpdatingLocation()
  12. }

Stop Location Updates if Accuracy Doesn’t Match Expectations

If your app isn’t receiving updates with the expected level of accuracy, your app should examine the updates it is receiving and determine whether accuracy is improving or staying about the same over time. If accuracy isn’t improving, it’s possible that the desired level of accuracy simply isn’t available at the moment. In this case, stop location updates and try again later so your app doesn’t continuously cause location hardware to draw power.

Auto-Pause and Specify an Activity Type When Receiving Location Updates in the Background

If your iOS app must continue monitoring location while it’s in the background, enable background mode in the Xcode Project > Capabilities pane. Select the checkbox for Location updates, as shown in Figure 14-1.

Figure 14-1Enabling background location updates in an app image: ../Art/xcode_project_capabilities_backgroundmodes_locationupdates_2x.png

In iOS 9 and later, regardless of deployment target, you must also set the allowsBackgroundLocationUpdates property of the location manager object to YEStrue in order to receive background location updates. By default, this property is NOfalse, and it should remain this way until a time when your app actively requires background location updates.

Make sure the location manager object’s pausesLocationUpdatesAutomatically property is set to YEStrue to help conserve power.

Set the activityType property to let Core Location know what type of location activity your app is performing at a given time—for example, if your app is performing fitness tracking or automotive navigation. See CLActivityType for a list of activity types.

Specifying these settings helps the location manager determine the most appropriate time to perform location updates. For example, background location updates may be auto-paused if the system determines that the user isn’t moving.

See Listing 14-4.

Listing 14-4Enabling auto-pause and classifying activity type for background location updates

Objective-C

  1. -(void)startBackgroundLocationUpdates {
  2. // Create a location manager object
  3. self.locationManager = [[CLLocationManager alloc] init];
  4. // Set the delegate
  5. self.locationManager.delegate = self;
  6. // Request location authorization
  7. [self.locationManager requestWhenInUseAuthorization];
  8. // Set an accuracy level. The higher, the better for energy.
  9. self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
  10. // Enable automatic pausing
  11. self.locationManager.pausesLocationUpdatesAutomatically = YES;
  12. // Specify the type of activity your app is currently performing
  13. self.locationManager.activityType = CLActivityTypeFitness;
  14. // Enable background location updates
  15. self.locationManager.allowsBackgroundLocationUpdates = YES;
  16. // Start location updates
  17. [self.locationManager startUpdatingLocation];
  18. }
  19. -(void)locationManager:(CLLocationManager *)manager
  20. didUpdateLocations:(NSArray *)locations {
  21. // Perform location-based activity
  22. ...
  23. // Stop location updates when they aren't needed anymore
  24. [self.locationManager stopUpdatingLocation];
  25. // Disable background location updates when they aren't needed anymore
  26. self.locationManager.allowsBackgroundLocationUpdates = NO;
  27. }

Swift

  1. func startBackgroundLocationUpdates() {
  2. // Create a location manager object
  3. self.locationManager = CLLocationManager()
  4. // Set the delegate
  5. self.locationManager.delegate = self
  6. // Request location authorization
  7. self.locationManager.requestWhenInUseAuthorization()
  8. // Set an accuracy level. The higher, the better for energy.
  9. self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
  10. // Enable automatic pausing
  11. self.locationManager.pausesLocationUpdatesAutomatically = true
  12. // Specify the type of activity your app is currently performing
  13. self.locationManager.activityType = CLActivityTypeFitness
  14. // Enable background location updates
  15. self.locationManager.allowsBackgroundLocationUpdates = true
  16. // Start location updates
  17. self.locationManager.startUpdatingLocation()
  18. }
  19. func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
  20. // Perform location-based activity
  21. ...
  22. // Stop location updates when they aren't needed anymore
  23. self.locationManager.stopUpdatingLocation()
  24. // Disable background location updates when they aren't needed anymore
  25. self.locationManager.allowsBackgroundLocationUpdates = false
  26. }

Defer Location Updates When Running in the Background

On supported devices with GPS hardware, you can let the location manager defer the delivery of location updates when your app is in the background. For example, a fitness app that tracks the user’s location on a hiking trail can defer updates until the user has moved a certain distance or a certain period of time has elapsed. Then, it can process the updates all at once. You can use deferredLocationUpdatesAvailable to determine if a device supports deferred location updates.

To defer updates, call the location manager object’s allowDeferredLocationUpdatesUntilTraveled:timeout: method and pass it a distance and time that may elapse before the next location update is received. This method is typically called in the locationManager:didUpdateLocations: delegate method in order to defer again, if appropriate, when a deferred location update is received.

When a deferred location update is received, the locationManager:didFinishDeferredUpdatesWithError: delegate method is also called, and your app can use this as an opportunity to adjust behavior accordingly—such as increasing or decreasing the deferral distance and time—for the next update. See Listing 14-5.

Listing 14-5Deferring background location updates based on distance and time

Objective-C

  1. -(void)startHikeLocationUpdates {
  2. // Create a location manager object
  3. self.locationManager = [[CLLocationManager alloc] init];
  4. // Set the delegate
  5. self.locationManager.delegate = self;
  6. // Request location authorization
  7. [self.locationManager requestWhenInUseAuthorization];
  8. // Specify the type of activity your app is currently performing
  9. self.locationManager.activityType = CLActivityTypeFitness;
  10. // Start location updates
  11. [self.locationManager startUpdatingLocation];
  12. }
  13. -(void)locationManager:(CLLocationManager *)manager
  14. didUpdateLocations:(NSArray *)locations {
  15. // Add the new locations to the hike
  16. [self.hike addLocations:locations];
  17. // Defer updates until the user hikes a certain distance or a period of time has passed
  18. if (!self.deferringUpdates) {
  19. CLLocationDistance distance = self.hike.goal - self.hike.distance;
  20. NSTimeInterval time = [self.nextUpdate timeIntervalSinceNow];
  21. [self.locationManager allowDeferredLocationUpdatesUntilTraveled:distance timeout:time];
  22. self.deferringUpdates = YES;
  23. } }
  24. -(void)locationManager:(CLLocationManager *)manager
  25. didFinishDeferredUpdatesWithError:(NSError *)error {
  26. // Stop deferring updates
  27. self.deferringUpdates = NO;
  28. // Adjust for the next goal
  29. }

Swift

  1. func startHikeLocationUpdates() {
  2. // Create a location manager object
  3. self.locationManager = CLLocationManager()
  4. // Set the delegate
  5. self.locationManager.delegate = self
  6. // Request location authorization
  7. self.locationManager.requestWhenInUseAuthorization()
  8. // Specify the type of activity your app is currently performing
  9. self.locationManager.activityType = CLActivityTypeFitness
  10. // Start location updates
  11. self.locationManager.startUpdatingLocation()
  12. }
  13. func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
  14. // Add the new locations to the hike
  15. self.hike.addLocations(locations)
  16. // Defer updates until the user hikes a certain distance or a period of time has passed
  17. if (!deferringUpdates) {
  18. distance: CLLocationDistance = hike.goal - hike.distance
  19. time: NSTimeInterval = nextUpdate.timeIntervalSinceNow()
  20. locationManager.allowDeferredLocationUpdatesUntilTraveled(distance, timeout:time)
  21. deferringUpdates = true;
  22. } }
  23. func locationManager(manager: CLLocationManager, didFinishDeferredUpdatesWithError error: NSError!) {
  24. // Stop deferring updates
  25. self.deferringUpdates = false
  26. // Adjust for the next goal
  27. }

Restrict Location Updates to Specific Regions or Locations

Some apps don’t need to receive continuous location updates, and just need to know when the user is within a certain distance of a location. For example, a grocery app may display new coupons whenever the user nears the store. There are several Core Location APIs that can keep your app informed.

Region and Beacon Monitoring

Core Location provides two methods for detecting a user’s entry to and exit from specific regions.

  • Geographical region monitoring provides entry and exit notifications for a specified location on the Earth.

  • Beacon monitoring provides entry and exit notifications when the user is within range of low-powered Bluetooth devices that are advertising iBeacon information.

For detailed information on using these techniques, see Region Monitoring and iBeacon in Location and Maps Programming Guide.

Visit Monitoring

Visit monitoring allows an app to receive entry and exit notifications for specific locations the user visits frequently or for long periods of time such as home, work, or a favorite coffee shop.

To begin monitoring for visits, assign a delegate to the location manager object and call its startMonitoringVisits method. Note that calling this method enables all visit updates in your app, not just ones for the current delegate. When enabled, visit events are delivered to the locationManager:didVisit: method of the delegate. If your app isn’t running when a visit event is delivered, your app is relaunched automatically. When visit location updates are no longer required, call stopMonitoringVisits, as shown in Listing 14-6.

Listing 14-6Using visit monitoring to receive updates tied to a specific location

Objective-C

  1. -(void)startVisitMonitoring {
  2. // Create a location manager object
  3. self.locationManager = [[CLLocationManager alloc] init];
  4. // Set the delegate
  5. self.locationManager.delegate = self;
  6. // Request location authorization
  7. [self.locationManager requestAlwaysAuthorization];
  8. // Start monitoring for visits
  9. [self.locationManager startMonitoringVisits];
  10. }
  11. -(void)stopVisitMonitoring {
  12. [self.locationManager stopMonitoringVisits];
  13. }
  14. -(void)locationManager:(CLLocationManager *)manager didVisit:(CLVisit *)visit {
  15. // Perform location-based activity
  16. ...
  17. }

Swift

  1. func startVisitMonitoring() {
  2. // Create a location manager object
  3. self.locationManager = CLLocationManager()
  4. // Set the delegate
  5. self.locationManager.delegate = self
  6. // Request location authorization
  7. self.locationManager.requestAlwaysAuthorization()
  8. // Start monitoring for visits
  9. self.locationManager.startMonitoringVisits()
  10. }
  11. func stopVisitMonitoring() {
  12. self.locationManager.stopMonitoringVisits()
  13. }
  14. func locationManager(manager: CLLocationManager, didVisit visit: CLVisit!) {
  15. // Perform location-based activity
  16. ...
  17. }

Register for Significant-Change Location Updates Only as a Last Resort

If GPS-level accuracy isn’t critical for your app, you don’t need continuous tracking, and region or visit monitoring isn’t more appropriate for your app, you can use the significant-change location service instead of the standard one.

To start significant-change location updates, call the startMonitoringSignificantLocationChanges method of the location manager object. When you’re done, call stopMonitoringSignificantLocationChanges, as shown in Listing 14-7.

Listing 14-7Requesting significant-change location updates

Objective-C

  1. -(void)startSignificantChangeLocationUpdates {
  2. // Create a location manager object
  3. self.locationManager = [[CLLocationManager alloc] init];
  4. // Set the delegate
  5. self.locationManager.delegate = self;
  6. // Request location authorization
  7. [self.locationManager requestAlwaysAuthorization];
  8. // Start significant-change location updates
  9. [self.locationManager startMonitoringSignificantLocationChanges];
  10. }
  11. -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
  12. // Perform location-based activity
  13. ...
  14. // Stop significant-change location updates when they aren't needed anymore
  15. [self.locationManager stopMonitoringSignificantLocationChanges];
  16. }

Swift

  1. func startSignificantChangeLocationUpdates() {
  2. // Create a location manager object
  3. self.locationManager = CLLocationManager()
  4. // Set the delegate
  5. self.locationManager.delegate = self
  6. // Request location authorization
  7. self.locationManager.requestAlwaysAuthorization()
  8. // Start significant-change location updates
  9. self.locationManager.startMonitoringSignificantLocationChanges()
  10. }
  11. func locationManager(manager: CLLocationManager, didFinishDeferredUpdatesWithError error: NSError!) {
  12. // Perform location-based activity
  13. ...
  14. // Stop significant-change location updates when they aren't needed anymore
  15. self.locationManager.stopMonitoringSignificantLocationChanges()
  16. }