Location Notifications with UNLocationNotificationTrigger

Learn how to use UNLocationNotificationTrigger to set up location-triggered notifications for your iOS app. By Graham Connolly.

4.9 (7) · 1 Review

Download materials
Save for later
Share

Geofencing, often called region monitoring, alerts your app when it enters or exits a geographical region. An example of this is an app that alerts users of offers when entering their favorite coffee shop, or an app that alerts the user to check-in on arriving at the dentist. Geofencing in iOS is a powerful feature, and you can use it to drive location-triggered notifications, using UNLocationNotificationTrigger. The feature works when an app is active, in the background, suspended or terminated — all without requiring users to always provide access to their location and sacrifice their privacy.

In this tutorial, you’ll add region monitoring to Swifty TakeOut, which is a simple app that allows users to place an order for pickup.

You’ll enhance the app by alerting users when they arrive using UNLocationNotificationTrigger. Users can then notify the kitchen they are “here” to collect their order.

Along the way, you’ll not only learn what Core Location is, but also how to:

  • Set up Core Location.
  • Register a geofence.
  • Ask the user for permission to use location services.
  • Enable the location update background mode capability.
  • Notify users of a geofence entry event.
  • Register a UNLocationNotificationTrigger.
Note: This tutorial assumes you know the basics of SwiftUI. If you’re new to SwiftUI, check out the SwiftUI: Getting Started tutorial first.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.

In the starter project, you’ll find Swifty TakeOut, an app that displays a list of takeout items available for order. Users can later pick up their orders at the Swifty TakeOut location.

Build and run to check out the app:

The Swifty TakeOut menu with an overview of options for ordering

The app allows users to select a food item:

A food item with price and a Place Order button

It also lets them place an order:

An alert showing you placed an order

In Xcode, look at the main files you’ll be working with:

  • TakeOutStore.swift contains a struct representing the takeout store.
  • LocationManager.swift contains an ObservableObject, which is where the magic happens.
  • MenuItem.swift contains a struct representing a menu item and an array of menu items.
  • MenuListView.swift displays a list of items available for order.
  • MenuListRow.swift represents a row item for the menu.
  • DetailView.swift displays the details of the selected item.

In this tutorial, you’ll learn how to show an alert when a user arrives at the Swifty TakeOut restaurant. You’ll do this by registering a geofence and using UNLocationNotificationTrigger set to trigger on that geofence.

But first, you’ll learn a bit more about Core Location.

What Is Core Location?

Core Location is an Apple framework that provides many services to get geographical information from a user’s device. Using the API, you can determine the device’s location, altitude, heading and orientation. Additionally, if any iBeacons (Bluetooth advertisements) are in the vicinity, you can detect and locate them.

The geographic information comes from the device’s hardware components. Where available, that includes Wi-Fi, GPS, Bluetooth, magnetometer, barometer and cellular radio.

Everything revolves around CLLocationManager. You’ll use it to start, stop and configure the delivery of location events in Swifty TakeOut. You can also set its properties to different accuracies and use region monitoring to watch for a user entering or leaving distinct areas.

Note: To learn more about Core Location and its services, check out Apple’s Core Location documentation.

Setting Up Core Location

Before you can detect if a user enters a geographical region, you’ll need to set up Core Location. To do so, you’ll create an instance of CLLocationManager.

Open LocationManager.swift and add the following code under the location property, inside LocationManager:

// 1
lazy var locationManager = makeLocationManager() 
// 2
private func makeLocationManager() -> CLLocationManager {
  // 3
  let manager = CLLocationManager()
  // 4
  return manager
}

Here’s what this code does:

  1. Adds an instance property to store the location manager. A lazy property delays initialization until it’s first used.
  2. Declares a method that creates and configures CLLocationManager.
  3. Creates an instance of the location manager.
  4. Returns the configured CLLocationManager object.

With a location manager configured, you’ll now register your geographical point of interest.

Note: You might be tempted to assign CLLocationManager to the property rather than use the method, but some additional configuration of the CLLocationManager object is needed later.

Creating a Geofence

To create a geofence in iOS, you need to configure a CLCircularRegion. This object consists of a center point and a radius, in meters. An entry event will notify your app when the device moves from outside to inside the radius.

A map showing someone entering a region

An exit event gets triggered when going from inside to outside the radius.

A map showing someone exiting a region

Next, while still in LocationManager.swift, configure your region by adding the following code under makeLocationManager():

// 1
private func makeStoreRegion() -> CLCircularRegion {
  // 2
  let region = CLCircularRegion(
    center: location,
    radius: 2,
    identifier: UUID().uuidString)
  // 3
  region.notifyOnEntry = true
  // 4
  return region
}

Here’s what this code does:

  1. Declares a method that creates a CLCircularRegion.
  2. Creates a CLCircularRegion instance. The center point is location, which is the latitude and longitude of Swifty TakeOut. The radius is two meters, and a unique identifier is associated.
  3. Configures region to trigger a notification when an entry event occurs.
  4. Returns the configured region.

With that added, configure a property to use this returned value. Add the code below the location property:

lazy var storeRegion = makeStoreRegion()

Now that you have set up this utility methods, you need to get the user’s permission to use location information. You’ll do that next.

Placing an Order

The main functionality of Swifty TakeOut is ordering food. When users place a food order, they receive a confirmation alert. This confirmation alert also asks if they’d like to get notified on arrival. Some restaurants offer to bring food out to your car if you select curbside pickup when ordering. When you arrive, you have to tell the restaurant “I’m here!”, so they can bring the food out to your car. In the next few sections, you’ll add this functionality.

Monitoring User Location

For notifications when a region boundary is crossed, you need to start tracking the location of the device. To do this, you’ll need to request authorization from the user.

Back in LocationManager.swift, add the following method under makeStoreRegion():

// 1
func validateLocationAuthorizationStatus() {
  // 2
  switch locationManager.authorizationStatus {
  // 3
  case .notDetermined, .denied, .restricted:
    // 4
    print("Location Services Not Authorized") 
  // 5
  case .authorizedWhenInUse, .authorizedAlways:
    // 6
    print("Location Services Authorized")
  default:
    break
  }
}

Here, you’ve added code that:

  1. Creates a method named validateLocationAuthorizationStatus() that determines the app’s authorization status for using location services.
  2. Configures a switch statement on the current authorization status of the app.
  3. Validates the current authorization value against .notDetermined, .denied or .restricted. If there’s a match, then the code block for this case gets executed.
  4. Prints to the debugger if location services isn’t authorized.
  5. Checks the current authorization value matches .authorizedWhenInUse or .authorizedAlways.
  6. Prints to the debugger if authorized.

You will request to track the user’s location after they place an order. The user’s location is then used to determine if the geofence has been breached.

Next, open DetailView.swift. requestNotification() is called if the user agrees to get notified on arrival. Update this method to confirm the location services authorization status:

func requestNotification() {
  locationManager.validateLocationAuthorizationStatus()
}

With this code added, it’s time to test out what you’ve added.

Build and run:

  1. Select a food item.
  2. Place an order.
  3. Tap Yes to get notified on arrival.

Order Placed Alert

You should see the following printed to the debugger:

Location Services Not Authorized

Next, you’ll request the user’s permission to allow Swifty TakeOut to use location services.