Article

Monitoring the User's Proximity to Geographic Regions

Use region monitoring to determine when the user enters or leaves a geographic region.

Overview

Region monitoring (also known as geofencing) is a way for your app to be alerted when the user enters or exits a geographical region. You might use region monitoring to perform location-related tasks. For example, the Reminders app uses them to trigger reminders when the user arrives at or leaves a specified location, as shown in Figure 1.

Figure 1

Notifying the user upon leaving a geographic region

A notification that can appear when the user leaves a geographic region

In iOS, regions are monitored by the system, which wakes up your app as needed when the user crosses a defined region boundary. In macOS, region monitoring works only while the app is running (either in the foreground or background) and the user’s system is awake. The system does not launch Mac apps to deliver region-related notifications.

Define and Monitor a Geographic Region

A region is a circular area centered on a geographic coordinate, and you define one using a CLCircularRegion object. The radius of the region object defines its boundary. You define the regions you want to monitor and register them with the system by calling the startMonitoring(for:) method of your CLLocationManager object. The system monitors your regions until you explicitly ask it to stop or until the device reboots.

Listing 1 shows how to configure and register a region centered around a point provided by the caller of the method. The method uses the largest allowed radius to define the boundaries of the region and asks that the system deliver notifications only when the user enters the region.

Listing 1

Monitoring a region around the specified coordinate

func monitorRegionAtLocation(center: CLLocationCoordinate2D, identifier: String ) {
    // Make sure the app is authorized.
    if CLLocationManager.authorizationStatus() == .authorizedAlways {
        // Make sure region monitoring is supported.
        if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
            // Register the region.
            let maxDistance = locationManager.maximumRegionMonitoringDistance
            let region = CLCircularRegion(center: center, 
                 radius: maxDistance, identifier: identifier)
            region.notifyOnEntry = true
            region.notifyOnExit = false
   
            locationManager.startMonitoring(for: region)
        }
    }
}

Handle a Region-Related Notification

Whenever the user crosses the boundary of one of your app's registered regions, the system notifies your app. If an iOS app is not running when the boundary crossing occurs, the system tries to launch it. An iOS app that supports region monitoring must enable the Location updates background mode so that it can be launched in the background.

Boundary crossing notifications are delivered to your location manager's delegate object. Specifically, the location manager calls the locationManager(_:didEnterRegion:) or locationManager(_:didExitRegion:) methods of its delegate. If your app was launched, you must configure a CLLocationManager object and delegate object right away so that you can receive these notifications. To determine whether your app was launched for a location event, look for the UIApplicationLaunchOptionsKey in the launch options dictionary.

When determining whether a boundary crossing happened, the system waits to be sure before sending the notification. Specifically, the user must travel a minimum distance over the boundary and remain on the same side of the boundary for at least 20 seconds. These conditions help eliminate spurious calls to your delegate object’s methods.

Listing 2 shows a delegate method that is called when the user enters a registered region. Regions have an associated identifier, which this method uses to look up information related to the region and perform the associated action.

Listing 2

Handling a region-entered notification

func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
    if let region = region as? CLCircularRegion {
        let identifier = region.identifier   
        triggerTaskAssociatedWithRegionIdentifier(regionID: identifier)
    }
}

Termination of your app (by the user or the system) does not prevent it from being relaunched to handle region boundary crossings. However, when Background App Refresh is disabled, either for your app or for all apps, the user must explicitly launch your app to resume the delivery of all location-related events.

See Also

Region Monitoring

class CLCircularRegion

A circular geographic region, specified as a center point and radius.

class CLRegion

An area that can be monitored.