iOS Geofencing using Core Location

The ability to detect whether a user has entered a specific geographic location is a useful feature that was added to iOS in version 4. There are several aspects to creating a monitored region (geofence) that I’m going to go over in a series of tutorials. In this tut we create a core location region object to act as the geo fence, then we monitor that region to see if the user enters or exits, in the sample code there’s an example of how a notification is fired even if the application is not running.

Firstly we create a CLRegion Object, this is made up of three parts, location (long/lat), radius (size of the geofence), and identifier (NSString name). In the example project I’m storing the regions as JSON, this could be loaded dynamically from a server and download them at runtime, but for this tutorial we’ll just hardcode one. Make sure you’ve imported the , added it to the project and your class conforms to the CLLocationManagerDelegate, see the full working example if you’re unsure of how to do this.

This method returns a CLRegion object from a dictionary with keys: longitude, latitude, radius and title.


- (CLRegion*)mapDictionaryToRegion:(NSDictionary*)dictionary
{
    NSString *title = [dictionary valueForKey:@"title"];
    
    CLLocationDegrees latitude = [[dictionary valueForKey:@"latitude"] doubleValue];
    CLLocationDegrees longitude =[[dictionary valueForKey:@"longitude"] doubleValue];
    CLLocationCoordinate2D centerCoordinate = CLLocationCoordinate2DMake(latitude, longitude);
    
    CLLocationDistance regionRadius = [[dictionary valueForKey:@"radius"] doubleValue];
    
    return [[CLRegion alloc] initCircularRegionWithCenter:centerCoordinate
                                                   radius:regionRadius
                                               identifier:title];
}


We then need to start monitoring the geofences using the aptly named startMonitoringForRegion: method. So assuming we’d created multiple regions in an array, we’d cycle through them like so:


 for(CLRegion *geofence in geofences) {
        [_locationManager startMonitoringForRegion:geofence];
    }

and then we handle the entry and exit using the didEnterRegion: and didExitRegion: methods


 - (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
    NSLog(@"Exited Region - %@", region.identifier);
   // post a notification
}

- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
    NSLog(@"Exited Region - %@", region.identifier);
    // post a notification
}

It’s that simple. There are a number of things to take into account when creating a geofence enabled app:

1. There are a limited number of regions that can be monitored by the phone at anyone time, and each app is limited to 20 regions. You should only register regions that are in the users immediate vicinity. However regions can be switched on off and new ones added dynamically based on your position, so for most applications this should be fine. If you attempt to register a region and space is unavailable the delegate method locationManager:monitoringDidFailForRegion:withError: will be called.

2. Regions must have unique identifiers otherwise the older region will be replaced by the newer one.

3. An app can register up to 20 regions at a time.

4. In order to report region changes in a timely manner, the region monitoring service requires network connectivity.

5. Regarding a region’s radius and accuracy, I’ve set radii to 20 meters and found them to be accurate to about half a NYC block. This could also have to do with latency from mobile phone tower connections, other related geo location/maps inaccuracies and the pace of my walking. The docs do mention a time lag when a user enters a region, apparently the app checks to make sure they have fully entered the region before updating, so as not to continually update if the users is positioned o the edge of a region. Although the docs mention a time lag of between 2-5 minutes, I have found the notification to be much more accurate. The best way to find out is to do a bit of on the ground testing in the desired location. From Apple’s docs: “In iOS 6, regions with a radius between 1 and 400 meters work better on iPhone 4S or later devices. (In iOS 5, regions with a radius between 1 and 150 meters work better on iPhone 4S and later devices.) On these devices, an app can expect to receive the appropriate region entered or region exited notification within 3 to 5 minutes on average, if not sooner.”

6. Before implementing any type of Core Location service we should check to see if Core Location services are available. The user may have disabled location services for this app or altogether in Settings, or may be in airplane mode.

7. Monitoring of regions happens instantaneously, however only occurs if the user has entered or exited a region, so if they are alrady in the region a notification will not be sent.

For more information check out Apple’s Core Location documentation here

Download the example project here.

13 comments

  1. How can i add geo fencing location from my application.Am very new to geo fencing as per your tutorial your telling like your fetching the latitude and longitude and other required fields from server .
    My question is where user can register geo fencing region and is it any way to register from application.

    Thanks
    shabeer

    Reply

    1. Hey Shabeer,
      Thanks for reading the tut, and thanks for the comment.
      If you look at the example project on GitHub I’m not actually pulling the longs and lats from a server (although you could do this and it is why I set the project up the way I did), but from a .plist in the project bundle, take a look at it here. This plist contains all the info you need to register a CLRegion (and then some), so you would only need to update this file with your longs and lats. You’ll notice the method at line 40 buildGeofenceData is pulling in this plist, cycling through it and creating CLRegion objects from it. Another, simpler way to do all this would be to bypass the mapDictionaryToRegion method at line 77 and hardcode your own CLRegion objects using all of your longs and lats. I hope this answers your question.

      Reply

  2. Hi

    I have downloaded your sample project and i have added my location lat and long in plist .
    when am walking inside that region i didnt get any notification .i kept radius 10 and am giving difference lat and long max in 30 meters.ca you please help me to slove this issue

    Reply

    1. Make sure you fully exit the region. This could be the problem. You need to walk very far away, then wait for a while, then try and enter again.

      Reply

  3. Hi, Nice tutorial. I have gone through quite of few tutorials on geofencing, but none seem to mention how to watch and register regions in the users immediate vicinity including Apple.

    A good example will be a speed cameras app. Let’s say you have a database of 100 speed cameras and you are only allowed to monitor twenty cameras at one time. How do you know which cameras to load as you approach them? I am assuming that, you would have to implement a pretty sophisticated algorithm to search the data correct?

    Thanks
    David

    Reply

    1. Hi David, this is where it gets tricky, although not impossible. You need to have a list/JSON file of all the regions you wish to monitor and then listen for the significantUpdateInLocation delegate method, then load in the closest 20 regions and start monitoring for them. So you’re dynamically loading and unloading the closest 20 regions based on the users current location.

      Reply

  4. Hi Sam.

    Great tutorial.
    I have this working on my iPhone 4 now.
    What would be the best way to fire a different notification message for each location? Currently all fences launch “You’ve entered a geofence.”

    Thank you.

    Reply

    1. Good question, you’ll need to check your region against a white list of regions you are monitoring for and then change the notification accordingly

      Reply

  5. … and along comes iOS 8 and everything breaks.

    Hi Sam. I had this working perfectly, but with the new Xcode and iOS 8, things are broken.
    Any chance you will update this tut?

    Thank you

    Reply

  6. Hi, Very good tut, i have question about while registering all geo location need network connectivity or just need the Location service enabled for registration ?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *