Tall Trucks, Low Bridges, and iOS Geofences

As my colleague Jeanette and I were walking home from the office a few months back, we came upon an unpleasant-looking accident approaching the bridge west of 1st Street on Washington. This unfortunate truck was too tall for this bridge’s 10’6″ clearance, and as such incurred serious damage.

An unfortunate truck

The experience made me wonder: could I create a mobile application that could warn me (in the unlikely event I was driving a truck taller than ten feet) that I was approaching this bridge, giving me time to stop and seek an alternate route? It turned out that iOS’ geofence support was just the ticket.

What Is Geofencing?

A geofence is an invisible physical region on the surface of the Earth. Notifications are sent when a location-capable device physically enters or exits that region.

Geofences in iOS are circular regions with a latitude and longitude at the center and a radius. To register our intent to be notified when an iOS device enters or exits one or more geofences, we use the CoreLocation framework, asking it to start monitoring for the geofence regions we’ve provided.

Geofence

We use geofences (instead of simply asking the device to deliver constant location updates) for two reasons:

  • Efficiency – It doesn’t take nearly as much power to let iOS do its own geofence calculations.
  • Privacy – We don’t need to know everywhere the user goes just so that we can figure out that they crossed over into our interesting area.

Asking Permission, Not Forgiveness

Before we can use any kind of location service on iOS, we need permission from the user, of course. Before iOS 8, apps could just try to use location services, and iOS would prompt the user on the app’s behalf for wide-ranging location permission. Apple has improved the permissions scheme since, splitting location permission into “always” and “when in use” variations—and now requires us to explicitly ask for one or the other.

For geofences, we need the “always” type of location permission. This doesn’t mean that we’ll be tracking the user’s precise location everywhere, though we will be always notified when they enter or leave geofences.

To get this permission, we need to first explain to the user what we’re going to do with the permission by setting a custom iOS target property. Select your project in Xcode, then the app target, Info, and add a property for NSLocationAlwaysUsageDescription:

Screen Shot 2015-06-02 at 5.52.33 PM

The other thing we should do before we ask for permission is check what our existing location authorization status is, by calling authorizationStatus on CLLocationManager. If it’s undetermined or (somehow) the “when in use” variety, we can go ahead and ask for the “always” permission.

If we already have our “always” permission, our request will just be a no-op; but if location authorization has been restricted or denied, it won’t do us any good to ask. The most we can do is advise the user that we’re not going to be able to provide location services unless they turn them back on in the Settings app.


switch ([CLLocationManager authorizationStatus]) {
    case kCLAuthorizationStatusNotDetermined:
    case kCLAuthorizationStatusAuthorizedWhenInUse:
        [self askForPermission];
        return;
 
    case kCLAuthorizationStatusAuthorizedAlways:
        [self setUpGeofences];
        return;
 
    case kCLAuthorizationStatusDenied:
    case kCLAuthorizationStatusRestricted:
        [self showSorryAlert];
        return;
}

Now, we can finally ask for permission. To do this, we’ll need to instantiate (and keep a strong reference to) a CLLocationManager and use requestAlwaysAuthorization, wrapped in a respondsToSelector check so we don’t crash pre-iOS-8 devices doing this. If we’re on a pre-iOS-8 device, we can go ahead and move on to setting up geofences—these versions of iOS will ask permission for us when we try.


self.locationManager = [CLLocationManager new];
if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
    [self.locationManager requestAlwaysAuthorization];
} else {
    [self setUpGeofences];
}

The response to the permission grant is asynchronous, so you’ll need to implement the CLLocationManagerDelegate protocol and implement didChangeAuthorizationStatus to detect if permission was granted, or the user denied you outright. The new status is of the same CLAuthorizationStatus type that authorizationStatus gave before. Once you have the “always” permission, you’re ready to move on.

Erecting the Geofences

Our next step is to register the geofences we’re interested in with our CLLocationManager. Geofences are defined as instances of CLRegion, though the only ones that actually work are CLCircularRegion. To create one, you’ll need a latitude, longitude, and radius in meters, as well as a string identifier for your region:


CLLocationCoordinate2D center = CLLocationCoordinate2DMake(42.280597,
                                                          -83.751891);
CLRegion *bridge = [[CLCircularRegion alloc]initWithCenter:center
                                                    radius:100.0
                                                identifier:@"Bridge"];

I’ve picked 100 meters for this geofence for a few reasons. It’s roughly the size of a block, for starters, giving you time to stop and not go under the bridge (though 200 meters might be better, giving you a chance to route around the hazard!) It’s also, in my testing with several iOS devices, proven to be a good, reliable, minimum geofence size.

Once you’ve created your region, you can ask iOS to start monitoring for it with startMonitoringForRegion. It’s safe to call this at any time—if you call it with a region whose identifier already exists, the old region will be replaced.


[self.locationManager startMonitoringForRegion:bridge];

Be sure you implement the CLLocationManagerDelegate methods for both success (didStartMonitoringForRegion) and failure (monitoringDidFailForRegion) so that your app is aware of the status of your monitoring request. There are several reasons geofence monitoring can fail, so it pays to listen to what iOS is telling you.

Entries and Exits

Once iOS is monitoring for your geofences, implement the appropriate CLLocationManager methods for geofence entry (didEnterRegion) and exit (didExitRegion) and iOS will call them when at the appropriate times—even launching your app in the background if it isn’t already running. For my app, I chose to deliver local notifications (which, conveniently, appear on my smartwatch as well.)

One caveat about monitoring for geofence entry: if you need to know whether the device is already in the geofence when you start monitoring, you’ll need to ask separately—you won’t receive a didEnterRegion call unless the device actually moves from outside a geofence to inside. I’ve tried several solutions for this, and the best one I’ve come up with is to call startUpdatingLocation, use CLCircularRegion‘s containsCoordinate to test whether the latitude and longitude are inside the region, and finally call stopUpdatingLocation.

Putting it all together, the answer seems to be yes—in the event I’m ever driving a truck over ten feet tall, I can use my phone to give me another shot at avoiding disaster.

Watch out for that bridge!

 
Conversation
  • Mark O. Hammontree says:

    This app. should be required to be built into the cabs of trucks, and other tall vehicles, and the app should have a way of requiring the trailer height or vehicle height to be entered into the app.

    Low bridge ahead warnings need to come about a mile before any detour, too, so trucks can use the detour.

  • Hemang says:

    Excellent post !

    I have few questions to make better understanding for this.

    I have few cases when using Geo Fencing in my app.

    1. App is running (Not in background)
    2. App is running (In background) 
    3. App is killed (By iOS)
    4. App is removed (By User – manually)

    Please consider that I have already register for geo fencing.

    My question, as per your post, and what I know about geo fencing, does it work for all of the four cases? If yes, then its fine. But in case No, for which case it will not work and why? Anyway to make it work?

    Thank you for this post.

    Regards

    • Manu says:

      Hi Hemang,

      Im also having same doubts what u have posted , you got any solution for the issues ,
      3. App is killed (By iOS)
      4. App is removed (By User – manually)

      Please share with me , if you have found.

      and is there anyway to auto restart the apps in ios when ” App is killed (By iOS)”
      ” App is removed (By User – manually) ”

      and if the app is running continuously in background, at what scenario the app gets killed by iOS.

  • Venkatesh says:

    3. App is killed (By iOS)
    4. App is removed (By User – manually)

    Those two scenerios will not work

    • Manu says:

      Hi Venkatesh,

      Is there anyway to auto restart the apps in ios when ” App is killed (By iOS)”
      ” App is removed (By User – manually) “

      and if the app is running continuously in background, at what scenario the app gets killed by iOS.

  • Andrey says:

    “if you need to know whether the device is already in the geofence when you start monitoring…”
    Why not just use `requestStateForRegion` to find out whether you are in particular region?

    • Matt Behrens says:

      Andrey,

      requestStateForRegion was the first thing I tried, but it didn’t work. As I recall, it returned the state of the geofence based on entries or exits, not whether the current position of the device was actually within the region—so if you started off inside the region, it wouldn’t know, because an “entry” hadn’t happened.

  • Afiq says:

    Hey matt, do you have sample code for this project. I really would like to test it out. Thank you in advance.

  • Viresh Madabhavi says:

    Here is the example for geofencing
    https://github.com/Be-With-Viresh/geofencing

  • Sulabh says:

    Does Geofencing have any floor dependency,suppose i need to work on a autocheckin feature to mark attendance and my company is in 10th floor,how accurate will be the region coverage?

    • Matt Behrens says:

      Hi Sulabh,

      Geofencing won’t help you there; it only handles latitude and longitude. You might look into iBeacon technology instead, which can trigger when an iOS device approaches the physical beacon you have installed.

      • Sulabh says:

        Thank You Matt Behrens for the quick reply.
        After some initial investigations i think that would be the correct approach,since creating geofences wont be effective to handle all the cases.

  • Sulabh says:

    One More Question.
    How energy efficient is the geofencing approach.Like i have created a geofence at my hometown where i visit typically 2,3 times a week and add those details to reminder app.Will it keep polling to check whether i am nearby the geofence every time?

  • David says:

    One question:
    When the geofencing app is running on background, geofencing is working?

  • David says:

    Hi.
    I want to use geofencing fuctions running in background.
    Could you help me?

  • richa says:

    Can you please share the sample application as i am facing issue where my delegate methods never gets called and i am out of ideas as what could be causing the issue

    Thanks & Regards
    Richa

  • Ravi says:

    Please send me multi latitude and longitudes geofences

  • Ravi says:

    Please send me multi latitude and longitudes geofences code

  • Comments are closed.