Geofencing with Core Location: Getting Started

In this geofencing tutorial, you’ll learn how to create and use geofences in iOS with Swift using the Core Location framework. By Michael Katz.

4.4 (5) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Reacting to Geofence Events

When the user enters or leaves a geofence region, your app could be running in the foreground, running in the background, or not running at all! You have to deal with all of these possibilities.

Handling Location Errors

You'll start by implementing some of the delegate methods to facilitate error handling. These are important to add in case anything goes wrong.

In GeotificationsViewController.swift, add the following methods to CLLocationManagerDelegate:

func locationManager(
  _ manager: CLLocationManager,
  monitoringDidFailFor region: CLRegion?,
  withError error: Error
) {
  guard let region = region else {
    print("Monitoring failed for unknown region")
    return
  }
  print("Monitoring failed for region with identifier: \(region.identifier)")
}

func locationManager(
  _ manager: CLLocationManager, 
  didFailWithError error: Error
) {
  print("Location Manager failed with the following error: \(error)")
}

These delegate methods log any errors the location manager encounters to facilitate your debugging.

Note: You'll definitely want to handle these errors more robustly in your production apps. For example, instead of failing silently, you could inform the user what went wrong.

Handling Location Events

Next, you'll add the code to listen for and react to geofence entry and exit events.

Open SceneDelegate.swift and add the following line at the top of the file to import the CoreLocation framework:

import CoreLocation

Add a new property below var window: UIWindow?:

let locationManager = CLLocationManager()

Then add the following method. It's called when the scene is activated:

func scene(
  _ scene: UIScene,
  willConnectTo session: UISceneSession,
  options connectionOptions: UIScene.ConnectionOptions
) {
  locationManager.delegate = self
  locationManager.requestAlwaysAuthorization()
}

You've set up your SceneDelegate to receive geofence-related events. Ignore the error Xcode will show here; you'll fix it shortly. But you might wonder, "Why did I designate SceneDelegate to do this instead of the view controller?"

iOS monitors the geofences registered by an app at all times, including when the app isn't running. If the device triggers a geofence event while the app isn't running, iOS relaunches the app in the background. This makes SceneDelegate an ideal entry point for handling the event, as the view controller may not be loaded or ready.

Now you might also wonder, "How will a newly created CLLocationManager instance know about the monitored geofences?"

It turns out that all geofences registered for monitoring by your app are conveniently accessible by all location managers in your app, so it doesn't matter where you initialize the location managers. Pretty nifty, right? :]

Comings and Goings

Now all that's left is to implement the relevant delegate methods to react to the geofence events. To receive those events, add the following extension to SceneDelegate.swift:

// MARK: - Location Manager Delegate
extension SceneDelegate: CLLocationManagerDelegate {
  func locationManager(
    _ manager: CLLocationManager,
    didEnterRegion region: CLRegion
  ) {
    if region is CLCircularRegion {
      handleEvent(for: region)
    }
  }

  func locationManager(
    _ manager: CLLocationManager,
    didExitRegion region: CLRegion
  ) {
    if region is CLCircularRegion {
      handleEvent(for: region)
    }
  }

  func handleEvent(for region: CLRegion) {
    print("Geofence triggered!")
  }
}

As the method names aptly suggest, you receive locationManager(_:didEnterRegion:) when the device enters a CLRegion and locationManager(_:didExitRegion:) when the device exits a CLRegion.

Both methods receive the CLRegion in question. You must ensure it's a CLCircularRegion, since it could be a CLBeaconRegion if your app happens to be monitoring iBeacons too. If the region is indeed a CLCircularRegion, call handleEvent(for:).

At this point, handleEvent(_:) takes in a CLRegion and logs a statement. Not to worry — you'll implement the event handling later.

Simulating Location Events

Now that your app is able to receive geofence events, you're ready to give it a (maybe literal) test run. If that doesn't excite you, it ought to, because for the first time in this tutorial, you're going to see some results. :]

The most accurate way to test your app is to deploy it on your device, add some geotifications and take the app for a walk or a drive. However, it wouldn't be wise to do so right now, as you wouldn't be able to verify the print logs emitted by the geofence events with the device unplugged. Besides, it'd be nice to get assurance that the app works before you commit to taking it for a spin.

Fortunately, there's an easy way do this without leaving the comfort of your home. Xcode lets you include a hard-coded waypoint GPX file in your project that you can use to simulate test locations. The starter project includes one for your convenience. :]

Open SimulatedLocations.gpx, which you can find in the Supporting Files group, and inspect its contents.

You'll see the following:

<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode">
  <wpt lat="37.3349285" lon="-122.011033">
    <name>Apple</name>
    <time>2014-09-24T14:00:00Z</time>
  </wpt>
  <wpt lat="37.422" lon="-122.084058">
    <name>Google</name>
    <time>2014-09-24T14:00:05Z</time>
  </wpt>
</gpx>

The GPX file is essentially an XML file that contains two waypoints: Google's Googleplex in Mountain View, and Apple Park in Cupertino. You'll notice there are time nodes on each waypoint. They're spaced at five seconds apart, so when you simulate locations with this file, it'll take five seconds to go between Apple and Google. There are also two more GPX files: Google.gpx and Apple.gpx. These are fixed locations, and you may use them for convenience when creating geofences.

Going on an Imaginary Drive

To begin simulating the locations in the GPX file, build and run the project. When the app launches the main view controller, go back to Xcode, select the Location icon in the Debug bar, and choose SimulatedLocations:

Simulate location button in debugger

Back in the app, use the zoom button in the top-left of the navigation bar to zoom to the current location. Once you get close to the area, you'll see the location marker moving from the Googleplex to Apple Park and back.

Test the app by adding a few geotifications along the path defined by the two waypoints. If you added any geotifications earlier in the tutorial before you enabled geofence registration, those geotifications won't work, so you might want to clear them out and start anew.

For the test locations, it's a good idea to place a geotification roughly at each waypoint. Here's a possible test scenario:

  • Google: Radius: 1000m, Message: "Say Bye to Google!", Notify on Exit
  • Apple: Radius: 1000m, Message: "Say Hi to Apple!", Notify on Entry
Note: Use the additional test locations provided to make it easy to add the locations.

Showing two geotifications

Once you've added your geotifications, you'll see a log in the console each time the location marker enters or leaves a geofence. If you activate the home button or lock the screen to send the app to the background, you'll also see the logs each time the device crosses a geofence, though you won't be able to verify that behavior.

Geofence triggered