Günay Mert Karadoğan    About    Archive

Range while monitoring your beacons

Say that you want to develop an app to deliver notifications based on iBeacons. App users will get a notification when they enter a beacon's region. You want to deploy beacons for over 50 locations. Knowing that an app can register up to 20 regions at a time, you should use similar configuration for these beacons. What I mean by similar is that sharing the same UUID but differing in major and minor numbers (or sharing the same UUID and major number, but differing in minor number). Let's monitor a region defined by only UUID. Then didEnterRegion will be called for all beacons with this UUID no matter what major or minor are. But now the major and minor parameters will return nil in the didEnterRegion message.

You need the major and minor numbers to understand in which specific location you are. Here comes the beacon ranging APIs that will give the individual identifiers of the beacons detected. One problem with ranging is that it can only be used when the app is in the foreground. Fortunately, monitoring works even if the app is in the background and it wakes up the app for ~10 seconds when it detects a region transition. This is where you can start ranging for this particular region.

For power efficiency, better you stop ranging when the region is exited.

You can rather use didDetermineState which is called whenever there is a boundary transition for a region. Note that didDetermineState is called in addition to calling the didEnterRegion and didExitRegion methods. So, you can remove didEnterRegion and didExitRegion delegates, and add didDetermineState as below.

Now you need to keep track of which beacon regions are entered or exited. Say that you have a set of current regions in which you are. Then in didRangeBeacons you can compare the set of current regions to the ranged beacons.

The code above only prints the entered and exited regions. What we built here as a UIViewController is actually not controlling any views. It is more like a manager that controls the beacon regions by using a CLLocationManager. So it is better if we encapsulate this into another object (let's call it BeaconManager) to be used by other view controllers. Also instead of just printing the regions, it will send NSNotication saying that there are entered/exited regions. You can listen these notifications in another view controller and do whatever you want such as connecting to your backend and showing a product info based on this beacon.

You can find the whole project's repository where a view controller sends local notifications by listening this beacon manager.

All good! Except there is a problem with overlapping regions. Think about this scenario. You configure two beacons with the same UUID but different major/minor numbers. The app is in the background and it gets a didEnterRegion when you enter the first beacon's region. Then the app goes back into background. You are still in the first beacon's region. Now it will not get a second didEnterRegion message. Because even the beacons have different major and minors, they are actually the same CLBeaconRegion represented by only the UUID. To overcome this problem, you can use different UUIDs for overlapping beacons so that you will get didEnterRegion messages that will wake up your app. Another solution would be to request extra background running time on beacon region transitions. RadiusNetworks provides an example for extending background ranging time.

If you liked this post, you can share it with your followers or follow me on Twitter!