Subscribing to location changes

One way of subscribing to changes in a user's location has already been covered in the previous section of this chapter. When you call startUpdatingLocation() on a location manager, it will automatically subscribe to the user's location. This method of tracking a user's location is excellent if you need very detailed reporting on a user's location, but usually, you don't need this level of detail. More importantly, using this kind of location tracking for an extended period will drain the user's battery.

Luckily, there are better ways to listen to location changes. One way is to subscribe to locations that the user visits by calling startMonitoringVisits(). This method is used if you aren't interested in the user's detailed movement but only want to know whether the user spent an extended period in a particular area. This type of tracking of a user's location is perfect if you need a low-power way to track very rough location changes. This kind of tracking even works well if your app is running in the background, because your app will automatically be woken up or launched if a visit event occurs.

If your app is relaunched due to a location-related event, then UIApplication.LaunchOptionsKey.location will be present in the application's launch options dictionary. When it is, you are expected to create an instance of a location manager and assign it a delegate to receive the relevant location update.

If the visits monitoring is a bit too inaccurate for your purposes but you don't need continuous location tracking, you can use significant location change tracking. This type of tracking triggers when a user has moved a significant distance over time, providing your app with updates only when the user is truly moving. This is a lot more power efficient than making your app track the user's location even when their current location hasn't changed. Just like visits tracking, significant location changes will wake up your app or even relaunch it when they occur. When an event is delivered to the app like this, you should re-enable the significant location changes monitoring. Let's implement significant location changes in the LocationServices sample app to see exactly how they work .

If you look at SignificantChangesViewController, you'll note that the view controller is fully set up to begin monitoring significant location changes. The monitorSignificantChanges(_:) method, defined on the location helper, takes a callback that's called every time a significant location change occurs. Every time new location data is retrieved, the table view is reloaded to display the latest available data. Since significant location updates can wake the app up with a special key in the app's launch options, let's update AppDelegate so it can handle this scenario. Add the following code application(_:didFinishLaunchingWithOptions:) right before the return statement:

if launchOptions?[UIApplication.LaunchOptionsKey.location] != nil {
  locationHelper.monitorSignificantChanges { _ in
    // continue monitoring
  }
}

Since AppDelegate already has a reference to the location helper, all it needs to do is re-enable significant location changes monitoring. This small change to AppDelegate is quite powerful because it allows your app to respond to changes in the user's location even when the app is not running. Let's implement the appropriate code in the location helper next.

Add the following implementation for monitorSignificantLocationChanges(_:) to LocationHelper:

func monitorSignificantChanges(_ locationHandler: @escaping (CLLocation) -> Void) {
  guard CLLocationManager.significantLocationChangeMonitoringAvailable()
    else { return }

  significantChangeReceivedCallback = locationHandler
  locationManager.startMonitoringSignificantLocationChanges()
  isTrackingSignificantLocationChanges = true
}

This method is very similar to the location helper methods you have seen before. When a significant location change is detected, the location manager calls locationManager(_:didUpdateLocations:) on its delegate. Since this method is already implemented, you should update the implementation as follows:

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
  latestLocationObtainedCallback?(locations.last!)
  latestLocationObtainedCallback = nil

  if isTrackingSignificantLocationChanges == false {
    locationManager.stopUpdatingLocation()
  }

  significantChangeReceivedCallback?(locations.last!)

  trackedLocations += locations
}

Note that the location manager is only told to stop updating the user's location when significant location tracking is not active. When you call stopUpdatingLocation(), the location manager will cease to deliver any location updates to this delegate method. Also, note that significantChangeReceivedCallback is not removed after calling it. The reason for this is that the caller of monitorSignificantChanges(_:) is interested in continuous location updates, so any time this method is called, the SignificantChangesViewController view controller that initiated significant location tracking should always be called.

One last thing you need to do, so your app receives significant location changes while it's not in the foreground, is to set the allowsBackgroundLocationUpdates property to true. Add the following line of code to the location helper's init():

locationManager.allowsBackgroundLocationUpdates = true

In addition to subscribing to significant location changes or visits, you can also respond to the user entering or leaving a certain area with geofences.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset