Alexander BerguerApr 25, 2017

iBeacon technology in Swift

In the past days, we’ve been working on an iOS project that uses iBeacon technology, and we believe that is an excellent opportunity to share a bit of our work with it. In this post, we’ll show you the very basics on how to use them in Swift.

If you don’t know what iBeacons are, we can say that an iBeacon is nothing more than a small Bluetooth Low Energy (bLE) device that enables a new way of location awareness, sending some information in a specific structure. Usually, iBeacons are used for indoor tracking or context-aware content delivery. For example, it can be used in stores to send customers discounts or information based on their in-store location. It is also used in airports to give the users tips or directions with interactive maps, and gives the stakeholders information about the passenger’s zones of interest.

More precisely, these devices use the iBeacon protocol to broadcast small packets that include three values: UUID, Major and Minor. Those values specify a hierarchal architecture, allowing the app know where the user is located. For a full explanation of these values, go to the official documentation.

Beacons in Swift

Let’s see how it can be implemented in Swift. IBeacon is part of Core Location framework, so it requires the same permission that you request for GPS location. It is important to mention that while beacons are Bluetooth devices, they do not require the Bluetooth permission to work, only Core Location is required. To achieve this, import Core Location to your ViewController and create an instance of Core Location Manager. We’ll request Always authorization, because we want the app start when a beacon advertising is available, even if the app is not running.

import CoreLocation

let locationManager = CLLocationManager()

and in viewDidLoad:

override func viewDidLoad() {
  super.viewDidLoad()
  locationManager.requestAlwaysAuthorization()
  locationManager.delegate = self
  . . .
}

Next, we have to provide the beacon information that we want to monitor. To do this, create a CLBeaconRegion with the data, for example

let beaconUUID = UUID(uuidString: "62550c6a-29ec-11e7-93ae-92361f002671")
let region = CLBeaconRegion(proximityUUID: beaconUUID)

Here we are specifying that we want to track a region where beacons have the given UUID. You can specify the major and the minor value as well. Once we’ve added the region information of all areas we want to track, it’s time to start listening to the signals. Your app can interact with the beacons in two ways: Monitoring or Ranging. When you are Monitoring, the actions are triggered when the app is entering or exiting a region, for example you can send a notification message to the user when a region is reached. On the other hand, when the app is Ranging beacons, the actions are triggered based on the proximity to the beacons. Based on your needs, you may want to monitor or range beacons, which can be done with the following lines respectively.

locationManager.startMonitoring(for: region)
// or
locationManager.startRangingBeacons(in: region)

In order to handle all the mentioned events, we need to set the locationManager’s delegate. We can extend the ViewController to conform the CLLocationManagerDelegate protocol, and add the handler methods.

extension ViewController: CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
        if let beaconRegion = region as? CLBeaconRegion {
            switch state {
            case .inside:
                print("did enter region: uuid: \(beaconRegion.proximityUUID.UUIDString)")
            case .outside:
                print("did left region: uuid: \(beaconRegion.proximityUUID.UUIDString)")
            case .unknown:
                break
            }
        }
    }


func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
        for beacon in beacons {
            var proximity: String;
            switch (beacon.proximity) {
                case .unknown:    proximity = "Unknown"
                case .far:        proximity = "Far"
                case .near:       proximity = "Near"
                case .immediate:  proximity = "Immediate";
            }

            print("Beacon Ranged!")
            print("uuid: \(beacon.proximityUUID.UUIDString), major: \(beacon.major),  minor: \(beacon.minor), proximity: \(proximity)")
        }
}

The Ranging method offers more information such as the number of beacons ranged along with their proximity, the accuracy and the signal strength of every beacon. It is important to mention that the Ranging method works only when the app is running.

And that’s all we need to interact with iBeacons. The beacon-dependent logic goes inside these methods. Simple, right?