A Guide to Interacting with iBeacons in iOS using Swift

I’ve recently been working on an iOS project that uses iBeacons. In this post, I’ll provide a comprehensive guide for working with iBeacons in iOS using Swift. I’ll describe what iBeacons are, how you can use them, and what you should know about the programming model for interacting with them in iOS. I’ll also share some of the best practices that I learned.

What Are iBeacons?

iBeacons are a class of Bluetooth Low Energy (BLE) devices that continuously broadcast identifying information about themselves using the iBeacon protocol. They are meant to be placed in the physical world (usually indoors) at locations of interest. Mobile apps can then ask to be notified when they move within range of a beacon, and then react appropriately.

You may read things that claim that iBeacons act as an alternative to GPS for indoor location sensing, in situations where GPS does not work reliably. While this is largely true, you shouldn’t necessarily think of iBeacons as a perfect substitute for GPS. Their operating model and location sensing precision are quite different compared to traditional GPS. Still, with a little creativity, you can do some pretty cool things with iBeacons.

iBeacon Applications

Before we get into how iBeacons work, it might be useful to discuss the ways you can use them. While there are a variety of different iBeacons applications, I like to group them into the following categories: 1. context-aware content delivery and 2. passive tracking.

Context-aware content delivery

iBeacons can be used to deliver content to users when they move near an area of interest. One classic example involves a museum. Imagine placing an iBeacon near each museum exhibit, and when users move near an exhibit, automatically displaying more information about what they are viewing. This requires the mobile app listening for beacons to know which beacons are near which exhibits. With this association, though, the beacons help determine where the users are in the museum and allow the app to react appropriately.

Passive tracking

Extending the concept of context-aware content delivery, you can also use iBeacons as a means to track users. Imagine placing iBeacons throughout a building–perhaps a museum or grocery store. As users move around, you can detect the route they take through the building by recording the sequence of beacons they pass. This would allow you to track users and learn common pathways and traffic patterns.

iBeacon Addressing Scheme

If you have worked with Bluetooth Low Energy devices in the past, you might be familiar with the concept of a Bluetooth advertising packet. iBeacons are essentially BLE devices that only send advertising packets. When they advertise themselves, they broadcast packets that contain three values: a UUID, a major number, and a minor number. The combination of these three numbers is used to uniquely identify an iBeacon, and it can be configured by the user.

The UUID, combined with the major and minor number, allows for nice hierarchal addressing schemes. Imagine if a retail chain like Walmart were to deploy iBeacons in their stores. They might choose to use a single UUID to denote Walmart and have all Walmart iBeacons use that UUID (which makes the UUID not unique at all, but this seems to be the common way to address iBeacons). They could then use the iBeacon major number to denote the store number, and the minor number to differentiate between different departments or locations within that store.

iBeacon Manufacturers

iBeacons are named as such because Apple defined the iBeacon protocol (e.g., the format for the advertising data that they broadcast). However, they are not made or sold by Apple.

Instead, there are several companies that make and sell iBeacons, and each manufacturer is a little different. Some of the more popular manufacturers include Estimote, Aruba, and Radius Networks. If you’re looking to get started with iBeacons, any of these manufacturers would be a good choice. I have personally used the Estimote and Radius Networks beacons, so I can talk about them in more detail.

Below are some photos of Estimote and Radius Networks iBeacons.

Estimote iBeacons
Radius Networks iBeacons. The top comes off for easy access to the battery.

The Radius Networks beacons are nice because you can turn them on and off and easily change their battery. The Estimote beacon battery lasts substantially longer, but you can’t easily change it when it dies, and you can not easily stop the Estimote beacons from broadcasting.

Note: There are several other similar protocols to iBeacon, such as Eddystone. Most manufactures make beacons that broadcast several different protocols.

Configurability

When you purchase iBeacons, the manufacturer usually provides a mobile app to help you configure them. This app allows you to do things like set each beacon’s UUID and major and minor number, adjust their advertising rate, and configure their Bluetooth broadcast strength. Some manufacturers don’t provide an app or any other easy way to configure their iBeacons. I would recommend avoiding these manufacturers unless you have a good reason not to.

Below are some screenshots of the Estimote and Radius Networks iBeacon configuration apps.

Estimote Configuration App Screenshots


In the Estimote app (shown above), you can see the screens for adjusting the beacon’s advertising rate and Bluetooth broadcast strength.

Radius Networks Configuration App Screenshots


In addition to the mobile app, there are several other iBeacon differences across manufacturers. When choosing an iBeacon, you may want to consider some of the following, as they tend to vary between vendors:

  • Beacon battery life
  • Beacon battery replaceability
  • Minimum and maximum beacon range
  • Ability to turn beacons off, or at least stop them from advertising
  • Other protocols that the beacons support (e.g., Eddystone)
  • SDK functionality (if one exists and you plan on leveraging it)

iOS Programming Model

Now that we have discussed what iBeacons are, how they work, and how to compare some manufacturers, let’s take a look at the iOS programming model for interacting with them.

All iBeacon functionality is provided through the iOS CoreLocation library. This is actually kind of interesting, given that iBeacons are Bluetooth devices. The Bluetooth nature of the beacons is completely abstracted away from the developer.

Permission

For your app to use iBeacons, users must allow it to access their location. You can prompt users for permission by adding keys to your Info.plist file. The NSLocationWhenInUseUsageDescription key will prompt users for permission to access their location when your app is in the foreground. If your app needs to receive iBeacon location notifications when in the background, the NSLocationAlwaysUsageDescription key should be used instead.

In addition to setting these keys, you will need to call requestWhenInUseAuthorization() or requestAlwaysAuthorization() on your CLLocationManager class instance when your app starts up.

iBeacon Listening Modes

There are two modes for receiving iBeacon notifications: monitoring and ranging. They work quite differently. Depending on what your app does, you may need to use one or both of them.

Monitoring

Monitoring is the simpler of the two modes. It can alert your app of region enter and exit events, but that’s about it. To listen for these events, instantiate a new CLLocationManager and implement the didEnterRegion and didExitRegion delegate methods. In my experience, the exit events can be delayed for about 30 seconds after you leave a region. The enter events, however, seem to happen reliably with little delay. If you start inside of a region, you will not receive an event until you leave it (and trigger an exit event).

The code below sets up monitoring for a region.


var locationManager: CLLocationManager = CLLocationManager()
if let uuid = UUID(uuidString: "B9407F30-F5F8-466E-AFF9-25556B57FE6D") {
    let beaconRegion = CLBeaconRegion(
        proximityUUID: uuid,
        major: 100,
        minor: 50.
        identifier: "iBeacon")
    locationManager.startMonitoringForRegion(beaconRegion)
}

After setting up monitoring, you can listen for enter and exit region callbacks by implementing the following two delegate methods:


func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
    if let beaconRegion = region as? CLBeaconRegion {
        print("DID ENTER REGION: uuid: \(beaconRegion.proximityUUID.UUIDString)")
    }
}
    
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
    if let beaconRegion = region as? CLBeaconRegion {
        print("DID EXIT REGION: uuid: \(beaconRegion.proximityUUID.UUIDString)")
    }
}

There are also two CLLocationManagerDelegate methods that can be helpful: didStartMonitoringForRegion confirms that monitoring was started correctly, and monitoringDidFailForRegion provides error information if something goes wrong in the monitoring setup.

The big limitation to be aware of with monitoring is that you can only monitor for 20 beacons at a given time. If you have more than 20 beacons, you will need to continuously manage which ones your app is monitoring for.

Ranging

Ranging is the other iBeacon notification mode. It is substantially more informative than monitoring and sends updates to your app much more frequently. Setting up ranging is similar to monitoring, except you call startRangingBeaconsInRegion instead of startMonitoringForRegion. Once set up, you listen for didRangeBeacons callbacks. This delegate method can look like this:


func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
    for beacon in beacons {
        var beaconProximity: String;
        switch (beacon.proximity) {
        case CLProximity.Unknown:    beaconProximity = "Unknown";
        case CLProximity.Far:        beaconProximity = "Far";
        case CLProximity.Near:       beaconProximity = "Near";
        case CLProximity.Immediate:  beaconProximity = "Immediate";
        }
        print("BEACON RANGED: uuid: \(beacon.proximityUUID.UUIDString) major: \(beacon.major)  minor: \(beacon.minor) proximity: \(beaconProximity)")
    }
}

These callbacks can happen as often as one per second. Each callback provides a CLBeacon object, which contains the following information about the beacon being ranged:

  • ProximityCLProximity describes how close the beacon is. Values include Near, Far, Immediate, and Unknown.
  • AccuracyCLLocationAccuracy captures a distance estimate to the beacon in meters.
  • RSSIInt captures the signal strength of the beacon measured in decibels.

The caveat with ranging is that it consumes significantly more power than monitoring. Further, while you can technically range in the background, it is strongly frowned upon. Most online discussions warn that Apple may reject your app if you do.

Further Discussion

Is the distance accurate?

There is a lot of discussion online surrounding the CLLocationAccuracy value returned with each ranging event. Folks often want to interpret this as the literal distance to the beacon. In my experience, you can attempt to interpret the value as a literal distance, but it will not always be accurate. The Apple docs suggest you use the CLProxity enumeration value as the initial way to determine which beacons are closest, and then break ties using the CLLocationAccuracy value.

This blog post conducts some interesting experiments to test the accuracy of the distance value.

Monitoring more than 20 beacons

As I mentioned earlier, you can only monitor 20 iBeacons concurrently. If you need to monitor more than 20, you will need to update the set being monitored as the app runs. One way to do this is to represent your iBeacon network as a graph, where the iBeacons are vertices and they are connected by an edge if they are near each other. When the user enters near an iBeacon, you can quickly search for its 20 nearest neighbors and monitor for them. This requires some work, but defining a topology like this is one way to work around the 20 iBeacon limitation.

Faraday containers

If your iBeacons do not turn off or can’t easily be stopped from advertising, you might consider purchasing a Faraday bag or cage. It can be tricky attempting to configure each iBeacon, one at a time, when you have several nearby. We certainly had this problem with the Estimote iBeacons. Placing all but one in a Faraday cage will make it easy to isolate the beacon you want to configure.

Example Project

If you are looking for a simple example project that allows you to monitor or range for user-defined iBeacons, check out this GitHub project of mine.

Conversation
  • Don Whiteside says:

    You don’t mention it but it’s useful for anyone who might be interested in fiddling with iBeacon code: there’s a number of software solutions out there to allow you to make a device like a Raspberry Pi or an ESP8266 (or any pc, for that matter) into an iBeacon.

    Adafruit has one tutorial here but there’s lots of resources out there. https://learn.adafruit.com/pibeacon-ibeacon-with-a-raspberry-pi?view=all

  • Mr Frank Viljoen says:

    Seriously, I don’t mean to be cynical here but beacons have been around for over 3 years now, and besides so many different explanations of what they are and how they work, it still seems that the description is being misrepresented ie “iBeacons can be used to deliver content to users when they move near an area of interest. “? iBeacons do NOT deliver content to users, they merely trigger an event which the App responds to. In order for the App to carry out the response request, the user needs to have connectivity be it WiFi or 3/4g! While most references are made about iBeacons and Apple/iOS, however Android handsets also happen to be able to listen for beacons and are able to react differently to beacon triggers.

    • Matt Nedrich Matt Nedrich says:

      You are correct that iBeacons can be used by anything that supports BTLE, and that they don’t “deliver” anything. However, users do not need a WiFi or LTE connection to respond when near an iBeacon.

  • Peter says:

    Matt,

    I will be glad if you could help me. I am looking for method that provide me two things: app will analize around beacons in background and send on the server information about it. I want to make VR game based on location inside the labyrinth but the point is that app should be in background mode.

    Is it possible?

    I will be glad :)

  • Neal says:

    I’d like to tlanslate your posts in Korean and put on my blog.
    Plz visit my blog!
    nealbaek.tistory.com/

  • Nick B says:

    Hi Matt ,Great tutorial looking forward to future blog posts!

  • Fin Horsley says:

    Is there any way of ranging beacons when the app is shut down? At the moment I listen for didEnterRegion, and within this method i call startrangingbeacon. I have enabled the BackgroundMode, and believe this allows the app to run in the background indefinitely?

  • Pedram says:

    Hello
    i am Micro controller (AVR) programmer
    i have HC-05 Bluetooth BT8X module.
    i want read tag iBeacon via my micro controller
    can any one help me?

  • Tariq Aris says:

    I am always confused about the permissions needed. So if you have an app that drives footfall to venues (a guide). You want to know if people did actually go to that place they found earlier that day… so you place beacons at the venue… The user goes to the venue but does not open the app while at the venue…

    Does the app need to have ‘always on or Background location’ permission in order to pick up the iBeacon signal or is a ‘Location while in app’ enough to be able to detect the beacon signals?

    Thanks in advance.

  • Comments are closed.