Deep Dive into Core Location in iOS: Geofencing (Region Monitoring)

Explore Geofencing to Detect User Entry and Exit from Specific Locations.

Dwi Randy Herdinanto
8 min readApr 30, 2023

This is part of article series about Deep Dive Into Core Location

Outline

  • Introduction
  • Request Location Permission
  • Setting Up Geofencing
  • Handling Geofencing Error
  • Debugging In Simulator
  • Conclusion

Introduction

Geofencing (region monitoring) is a virtual boundary around a real-world geographical area. Geofencing is a way to notify our application when the device enters or leaves configured geographical regions. For example, it lets us make an app that can send a notification telling a parent that his child has left or entered a certain area, or an app that can enable or disable features when entering or leaving a region.

While Geofencing is a powerful feature that can add significant value to an iOS app, there are also some potential disadvantages to consider:

  • Geofencing requires the approval of the user. If the user rejects the location services, geofencing will not work
  • Geofencing requires continuous location monitoring, which can lead to significant battery drain
  • Geofencing rely on specific hardware capabilities
  • Geofencing requires access to the user’s location data, which can raise privacy concerns if handled improperly.

Request Location Permission

Since the geofencing feature needs to access the user's location, we need to ask for user location permission to the user so that we can receive location updates when a user is in the background and the foreground. If we want to receive geofencing works when the application is in the background, we need to ask location permission to Always Allow

Step 1: Setup Info.plist for Location Privacy

To get information about user location, we need to add some string in the info.plist file. This string message will be shown when the application asks for permission

<dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app need your location to provide best feature based on location</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app need your location to provide best feature based on location</string>
</dict>

Step 2: Add Background Mode Capabilities

Since we want to support Geofencing to retrieve location information in the background state, we need to ensure that the Location updates the option is selected in the Background Modes tab.

Step 3: Request Always Allow Permission

We can use requestAlwaysAuthorization() to get permission for allowing our application to receive location updates when our application is in the background, but keep in mind that to obtain Always authorization, our app must first request When In Use permission followed by requestAlwaysAuthorization()

import UIKit
import CoreLocation

class ViewController: UIViewController {

var locationManager: CLLocationManager?

override func viewDidLoad() {
super.viewDidLoad()
setupLocationManager()
}

private func setupLocationManager() {
locationManager = CLLocationManager()
locationManager?.delegate = self

// Request When In Use user permission
locationManager?.requestWhenInUseAuthorization()

}

}

extension ViewController: CLLocationManagerDelegate {

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch manager.authorizationStatus {
case .notDetermined:
print("When user did not yet determined")
case .restricted:
print("Restricted by parental control")
case .denied:
print("When user select option Dont't Allow")
case .authorizedAlways:
print("Geofencing feature has user permission")
case .authorizedWhenInUse:
// Request Always Allow permission
// after we obtain When In Use permission
locationManager?.requestAlwaysAuthorization()
default:
print("default")
}
}

}

Setting Up Geofencing

Step1: Create the Region We Want to Monitor

To work with the Geofencing feature in Core Location first of all we need to create a region that will be monitored by our application. We can center our region on the coordinates we want to monitor, and specify how big it will be by giving it a radius.

From the docs said:
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.

We need to define a region using a center coordinate and radius. Then, we can use Core Location’s geofencing APIs to monitor the region. When the user enters or exits the region, your app receives a notification.

When the user enters or exits the region, your app receives a notification. The system waits for the user to cross the boundary and remain on the other side for at least 20 seconds before sending the notification to avoid unnecessary notifications.

class ViewController: UIViewController {

private func setupGeofencing() {
guard CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) else {
showAlert(message: "Geofencing is not supported on this device")
return
}

guard locationManager?.authorizationStatus == .authorizedAlways else {
showAlert(message: "App does not have correct location authorization")
return
}

startMonitoring()
}

private func startMonitoring() {
let regionCoordinate: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 37.3346438, longitude: -122.008972)
let geofenceRegion: CLCircularRegion = CLCircularRegion(
center: regionCoordinate,
radius: 100, // Radius in Meter
identifier: "apple_park" // unique identifier
)


geofenceRegion.notifyOnEntry = true
geofenceRegion.notifyOnExit = true

// Start monitoring
locationManager?.startMonitoring(for: geofenceRegion)
}

private func showAlert(message: String) {
let alertController = UIAlertController(title: "Information", message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .cancel))
self.present(alertController, animated: true, completion: nil)
}
}

Here is an overview of the method above

  • setupGeofencing() is a method to check if the device supports for geofencing feature, and then we have to make sure that we get Always Allow permission from the user
  • startMonitoring() is a method to define a region that we want to monitor, we declare the coordinate inside regionCoordinate we are also able to adjust the radius of a region that we want to monitor
  • Apple only allows monitoring 20 regions, by using identifier property in CLCircularRegion we can distinguish one region from another

Tip from apple docs

Regions are shared resources that rely on specific hardware capabilities. To ensure that all apps can participate in region monitoring, Core Location prevents any single app from monitoring more than 20 regions simultaneously. To work around this limitation, monitor only regions that are close to the user’s current location. As the user moves, update the list based on the user’s new location.

Step 2: Handle a Region Notification

Your app will receive a notification from the system whenever the user enters or exits one of the regions that your app has registered. Even if the app is not currently running when the user crosses the boundary, the system will attempt to launch the app to handle the notification.

From CLLocationManagerDelegate we can use locationManager(_:didEnterRegion:) to get notified when the user enters the region andlocationManager(_:didExitRegion:) when the user leaves the region.

extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
guard let region = region as? CLCircularRegion else { return }
showAlert(message: "User enter \(region.identifier)")
}

func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
guard let region = region as? CLCircularRegion else { return }
showAlert(message: "User leave \(region.identifier)")
}
}

Step 3: Start Monitoring

We need to adjust our code inside locationManagerDidChangeAuthorization(_:) so that we call setupGeofencing when the user allows the authorizedAlways permission

extension ViewController: CLLocationManagerDelegate {

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch manager.authorizationStatus {
case .notDetermined:
print("When user did not yet determined")
case .restricted:
print("Restricted by parental control")
case .denied:
print("When user select option Dont't Allow")
case .authorizedAlways:
// setup geofencing feature and start monitoring
setupGeofencing()
case .authorizedWhenInUse:
locationManager?.requestAlwaysAuthorization()
default:
print("default")
}
}
}

If we want to stop monitoring specific regions we can call stopMonitoring(for:) and pass region with a specific identifier

Handling Geofencing Error

In some cases error occurs while trying to monitor the region that we have set, region monitoring might fail because the region itself cannot be monitored or because there was a more general failure in configuring the region monitoring service.

We can get notified when geofencing error by implementing locationManager(_:monitoringDidFailFor:withError:) from CLLocationManagerDelegate

extension ViewController: CLLocationManagerDelegate {

func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
guard let region = region else {
print("The region could not be monitored, and the reason for the failure is not known.")
return
}

print("There was a failure in monitoring the region with a identifier: \(region.identifier)")
}
}

Debugging in Simulator

We already implement geofencing inside the application. To check if it works properly, we can simulate a movement into and out of that location using a gpx file that contains a list of coordinates that we have defined. This file helps us test if the feature is correctly detecting when someone enters and leaves the location.

Step 1: Create gpx File

To create a new gpx file

  • Select File menu > New > FilE.
  • Select GPX File under the Resource section, then click Next
  • Specify the file name and click Create. In this case, we can name our GPX file SimulateGeofencing

Step 2: Define Locations

To simulate location changes for testing purposes, we can define specific locations using a GPX file. This tutorial demonstrates how to define locations by providing their latitude, longitude, name, and time. For example, we can simulate a route that starts from Hilton Garden Inn Cupertino and moves to Apple Park, and after that moves on to N Tautau Ave.

<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode">
<wpt lat="37.3352915" lon="-122.0203281">
<name>Hilton Garden Inn Cupertino</name>
<time>2014-09-24T14:55:37Z</time>
</wpt>
<wpt lat="37.335056" lon="-122.008548">
<name>Apple Park</name>
<time>2014-09-24T14:55:45Z</time>
</wpt>

<wpt lat="37.336105" lon="-122.000029">
<name>N Tantau Ave</name>
<time>2014-09-24T14:55:50Z</time>
</wpt>
</gpx>

Xcode will interpolate movement at a rate of speed based on the time elapsed between each waypoint. If you do not provide a time element, then Xcode will use a fixed rate of speed.

Step 3: Run The Application

Now we can run our application and simulate location updates based on the previously created GPX file. To do this, we need to set a breakpoint in our code, and then select the GPX file we want to use to start the simulation.

Run SimulateGeofencing

After waiting for a few seconds, our application will display an alert that informs us when the user enters “apple_park” which indicates the user enter the region. After a few seconds, our application will display another alert when the user leaves “apple_park” that indicatesthe user exit the region.

Conclusion

Geofencing is a powerful feature provided by Core Location in iOS that allows an application to receive notifications when a user enters or leaves a configured geographical region. By implementing geofencing, we can develop location-based apps that can provide useful features, such as notifying a user when they enter or leave a certain area.

However, it is important to note that geofencing requires user approval, continuous location monitoring, specific hardware capabilities, and access to the user’s location data, which may raise privacy concerns.

In this article, we have provided a step-by-step guide to set up and use geofencing in an iOS app, including requesting location permission, setting up geofencing, handling geofencing errors, and debugging in the simulator. With this guide, we can easily implement geofencing in our iOS apps and take advantage of its powerful capabilities.

--

--