Geofencing on iOS (Swift) for noobs

Or How to show a notification when user enters/leaves someplace

Hilton Pintor
Apple Developer Academy | UFPE

--

🗺 + 📱 = 📈

For the most part of 2017, I’ve been working on Palpitadus, an iOS app that gives context-aware suggestion of activities on my hometown’s b-side. And to make sure our suggestions were helpful, we asked our users for feedback once they had done that activity.

You probably have different specifications, but your goal is the same as mine, to send the user a (local) notification when he arrives/leaves a specific place.

In order to keep fellow developers from suffering from the lack of straight-to-the-point, easy-to-implement, swift-written tutorials that actually work, I’ve decided to write one myself, so here we go, I hope it helps 😉

Requirements

For this tutorial, I will assume that you already have the coordinates of the place you want to monitor, like for example, Apple’s headquarters (37.33233141, -122.0312186).

Also, make sure that you have permission to get user location even when they are not using the app. If you don’t know how to do that, take a look at this link, and then come back.

If you are all set, let’s get this show on the road. Follow these steps, and surely fame and glory await for you at the end:

1. Define the region to be monitored

The way region monitoring works in Swift requires that first you define a circular region.

We can center our region on the coordinates we want to monitor, and specify how big it will be by giving it a radius.

A CLCircularRegion also needs an identifier, so that they can be differentiated from other regions on your application.

📐 Metric system rules 📏

2. Specify which events will be monitored

The next step is to define when we want to be notified of events that happen on the geofenceRegion.

By setting both of those values to true, we are saying that the app should be launched, and our locationManager’s delegate notified when the user enters, but also when they exit the defined region.

3. Start monitoring

To end our geofencing setup, we need to tell our locationManager to start monitoring this region

A couple of things are worth noting from this step:

  • Your app can register up to 20 regions at a time;
  • If you register a region with the same identifier as another region which is already being monitored, the old region is replaced by the new one;
  • Once the monitoring starts, it is shared by all location managers on your code, which means, the delegate that will receive the enter/exit events notifications doesn’t have to be on the same class as the location manager that started the monitoring;

4. Register as Delegate

In order to be notified of region events (entry/exit), we need to implement two methods from the CLLocationManagerDelegate protocol.

For my use-cases, I needed to handle those events even if my app was terminated by the user, so I made my AppDelegate a CLLocationManagerDelegate as well, since that's the entry point between the iOS and my App.

The delegate protocol can be implemented by other classes if that’s what makes sense for your app.

no iOS developer has ever forgotten to assign a delegate 😎

5. Receive monitoring events

This is the part where we calm down the compiler that was screaming at us saying our class wasn’t a CLLocationManagerDelegate by actually implementing what we need from the protocol

Protocol conformance on an extension keeps the Swift gods pleased 🙌

The locationManager(_:didEnterRegion:) method will be called, as the name suggests, when the user enters a monitored region. Since the region parameter is of type CLRegion, I checked to see of it was in fact a CLCircularRegion, and if so, called a method to handle this event.

locationManager(_:didExitRegion:) follows the same principle, but for exits from the region.

You don’t need to implement both methods, only the ones you said you'd monitor on step 2 (remember notifyOnExit and notifyOnEntry?).

6. Schedule a local notification

On the previous step, the function handleEvent(forRegion:) was called, because that’s where our local notification will be scheduled. Let’s see how that works:

It may seem like a lot of code, but the steps are trivial:

  • Choose what our notification is going to look/sound like
  • Schedule when it will be triggered
  • Create a notification request
  • Add it to the UNUserNotificationCenter (see next step)

7. Get permission to notify

On the previous step, a notificationCenter object was used to add a notification request to the UNUserNotificationCenter.

A notification center is where all the notification-related magic occurs. It’s a shared object that’s going to help us get permission to notify our users, and manage our notifications.

The code below actually builds upon step 4:

Since the Notification Center is a shared object (singleton) in order to get it’s instance, we use the current() method.

Then we set our class as its delegate so we can handle notification-related events, like user interaction with a notification.

After that, like most cool things in iOS, we need to ask permission to the user in order to do them. The requestAuthorization(options:completionHandler:) takes care of that for us, but make sure that it’s being called before you actually need to send one.

It’s not a good practice to ask the user for permission as soon as he opens the app for the first time, so try to find an appropriate moment that will have minimum impact on the user experience.

8. Notify even if in foreground

If your app is in foreground, we need an extra step to make the notification appear the same way it does on the other cases:

On the previous step we set the AppDelegate to be the notificationCenter delegate, and one of the methods of that protocol that we need to implement is the userNotificationCenter(_:willPresent:withCompletionHandler:)

If no implementation of this method is found, no notification will be shown while the app is in foreground. This is to keep the user from receiving a notification for content that is already being shown to him on the app.

To simplify things, line 5 of the code above says that the notification should be shown as an .alert, the same way it is shown on the other cases (background and terminated).

9. Handle tap on notification

If everything goes according to plan, your notification was triggered, the user was amused by its message and tapped it. Now what?

Now we need another delegate method that is going to be called whenever the user interacts with the notification, opening your app (if it’s not open already)

Inside the userNotificationCenter(_:didReceive:withCompletionHandler:) we can access the identifier of the tapped notification, so we can react accordingly and direct the user to the corresponding view.

10. App Terminated (optional)

Sometimes users are bored and they decide to terminate apps that they have open for no reason, or maybe they have a reason, they are your users, I don’t know their lives 💁🏻‍♂️

At palpitadus, I was using the VIPER architecture, so my AppDelegate's application(_:didFinishLaunchingWithOptions:) had some navigation logic on it that needed to be different if the app was launched by a location event.

If you need to differentiate who or what caused the launch of your app, inspect the launchOptions dictionary, which is a parameter of the aforementioned delegate method.

In it you can check if there are any entries for the key UIApplicationLaunchOptionsKey.location. If you find one, it means that your app was launched to handle an incoming location event.

You’re still here!?

It may seem like a lot of steps, but after following them carefully you should have a super cool app that notifies users when they leave/enter specific places, but remember:

With geofencing and notification come great responsibilities

So don’t abuse your newly acquired powers, use them with wisdom, so as not to alienate your users and have them delete your app faster than you can say "geofencing".

I really hope this tutorial was helpful, and if it wasn’t let me know on the comments so I can send you an apology letter. Or maybe help you get things working, whichever is more helpful 😉

Fully working (at the time of writing) project can be found here.

📝 Read this story later in Journal.

🗞 Wake up every Sunday morning to the week’s most noteworthy Tech stories, opinions, and news waiting in your inbox: Get the noteworthy newsletter >

--

--