Estimote iBeacons developer kit

in iOS, Swift

Getting started with iBeacons in Swift


In this article, you will learn the basics of Bluetooth LE (which stands for Low Energy), understand how to work with iBeacons, how to properly configure the devices, learn the prons and cons of Bluetooth LE and finally you will learn how to connect and use them with your application using Swift programming language for iOS. The final result will be an application that will be able to identify all the beacons that are in your range and tell the distance from your phone and display notifications and alerts. This is an upgrade to an old tutorial I wrote for CityOS foundation with the latest Swift syntax.

Acquiring the necessary software and hardware

iBeacon example

Since we are developing an application for iOS you will need to have some of the Apple mobile devices since beacons will not work on a simulator. The good news is that Apple started supporting Bluetooth LE with the release of iPhone 4S so there’s a large number of devices that support this technology:

iPhone 4s and later
iPad 3 and later
iPad mini and later
iPod touch 5 and later

Based on the testings done so far, I would recommend using an iPhone rather than iPad for your application since iPhone seems to perform better compared to iPad in some situations that are likely to hapen in real life scenario. However for this tutorial any of the above mentioned devices should be fine. You should also update your iOS and your xCode to versions 9 and 7 respectively. Finally, you need to have active Apple developer account in order to build apps on your device due to Apple security regulations. All done? Perfect! Let’s move on.

The next thing are the beacons. Although the name iBeacons might suggest that you buy them from Apple, they are actually made by third-party manufacturers that follow the guidelines Apple defined. In this tutorial we will be using beacons built by Estimote (http://www.estimote.com) which are one of the most popular at the market currently, but there are many other such as Kontakt, Gelo, BlueCats etc.
If you don’t have them already, you can buy the beacons from Estimote online store. Developer kit comes with 3 iBeacons but for this tutorial one will be enough, however, we recommend aquiring more than 3 if you plan to follow our future tutorials for indoor location.

iBeacon components

Understanding beacons technology and terminology

In this step we are going to focus on understanding some key terminology used with iBeacons and how do they actually work. If you’re familiar with Bluetooth LE you can freely skip to the next step, otherwise we recommend reading it thorugh.

Bluetooth LE (already said LE stands for Low energy) is a very similar to classicBluetooth (Bluetooth v3.0) we all know and use to connect headsets or transfer files between mobile phones, however there is one crucial difference. While “regular” needs to pair with the device before it can communicate with it Bluetooth LE doesn’t. It allows to be detected by another scanning device and optionally connect to it to retrieve additional data. This is something which makes Bluetooth LE extremelly convinient for indoor location especially compared do WI-FI. In addition, because of its LE sufix becaons have ultra-low power consumption and can last up to two years without battery replacement.

Devices that can recieve one of the two Bluetooth signals are known as single-mode devices. Devices that support both Bluetooth technologies are dual-mode devices . One important information you have to understand is that iBeacon by itself does not transmit any content to your mobile phone. It only transmits signals that can tell how far you are from the beacon.

In your application iBeacon is identified using three values: Proximity UUID (16-bytes), Major value (2 bytes), and Minor value (2 bytes). It is up to the developer on how these numbers will be used in their app, but here’s one example.

Let’s say you’re developing an application for a shopping mall which has several branches and each branch has some number of stores. You would set Proximity UUID to be the same in every beacon so your application would work in each branch. Major value would be used to uniquely identify branches of your store ranging from 1 to n and minor values to represent stores inside the branch.

Create new project

Create new project

Creating Xcode project

It’s time to start with the coding.

Open your Xcode editor and create a new project. Chose to create single view application. Make sure that you have selected iOS as your build platform and Swift as programming language.

Name your project as you wish, and click create. Creating a Github repository is optional and not relevant at this point.

As we said at the beginning, we will create an application that will look for beacons in range and display notifications depending on how far you are.

Core Location

Core Location

Import CoreLocation

The first thing that you need to to is to import CoreLocation in your AppDelegate.

After that create locationManager variable which is an instance of a CLLocationManager.

CLLocationManager is used for configuring location- and heading-related events to your application. You use an instance of this class to establish the parameters that determine when location and heading events should be delivered and to start and stop the actual delivery of those events.With CLLocationManager you can track the changes in the user’s current location, report heading changes, monitor distinct regions represented by the beacons, running updates in the backgorund and reporting the range of beacons.

CLLocationManager is one of the most importnat classes for indoor location and it is important to understand for what it can be used for. While you’re working in the AppDelegate, remove the pre-built comments for easier code organization.

Your AppDeleagete should now look like this:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?
let locationManager = CLLocationManager()

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    return true
}
info.plist example

info.plist example

Updating info.plist file

You app has to request authorization from the user in order to discrover the location of the device. Before we write the code for this we have to update the info.plist file in your project.

In order to access your info.plist file hit CMD+Shift+O and type in the pop up dialog box “info.plist”

IMPORTANT NOTE
The structure of your project has two folders and the xcodeproject file at the begining, which looks something like this:

yourAppName (folder)
yourAppNameTest (folder)
yourAppName.xcodeproj

Both of these folders have info.plist file, and it is important to select the one which is not in the test folder. Once you open the file in the Information propery list click the plus icon to add another row to the list and type the following values:

Key: NSLocationAlwaysUsageDescription
Type: String
Value: This application monitors your location to show notifications when entering and exiting regions. (optional but good practice)
Once you finish your info.plist should look like the one from the image above.

Defining location region

In this step we are going to define the region which is an instance of CLBeaconRegion class. Region represents a device’s proximity to a beacon as opposed to a greographic location. If you recol our short example from step two about shopping malls, one shopping mall represents one region.

Now that you understand what reagion means, go to your AppDeleage and firstly implement CLLocationManagerDeleagate protocol which lets us receive all location updates and information and handle them accordingly.

Your AppDeleagate class declaration shoud look like this:

class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDeleage

At the top of the class define the region as a variable. We called our region “nest71region”, but you can call yours whatever you like. The code should look something like this:

// Define region for monitoring
let region = CLBeaconRegion(proximityUUID: NSUUID(UUIDString: "--INSERT YOUR UUID HERE--")!, identifier: "Region")

Referring again to step two, we mentioned Proximity UUID and what it means. It is also worth mentioning that each beacon has a default UUID which you can see above. For this tutorial it’s perfectly fine to use the default value, but for developing custom applications you should consider making your own.

Beacon proximity sketch

Beacon proximity sketch

Requesting user location

In order for your application to get the location, user has to allow it. You should know that there are two types of permisions user can give you:

  • requestAlwaysAuthorization
  • requestWhenInUse

The difference is that requestWhenInUse runs only when the app is in foreground, while requestAlwaysAuthorization works when the app runs in background which is what we need. Go back again to your AppDeleage and in the didFinishLaunchingWithOptions function call requestAlwaysAuthorization fuction:

locationManager.requestAlwaysAuthorization()
locationManger.delegate = self

Recol that we previously defined locationManager.

Now at the bottom of the class implement a function didChangeAuthotizationStatus which will check if the app has the location permisions. If the user allows location tracking we will start monitoring for reagion in range, otherwise we will display a small alert saying that in order for app to work permision has to be granted. Here’s what the code looks like:

func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
    
    switch status {
        
    case .AuthorizedAlways:
        locationManager.startMonitoringForRegion(region)
        locationManager.startRangingBeaconsInRegion(region)
        locationManager.requestStateForRegion(region)
        
    case .Denied:
        let alert = UIAlertController(title: "Warning", message: "You've disabled location update which is required for this app to work. Go to your phone settings and change the permissions.", preferredStyle: UIAlertControllerStyle.Alert)
        let alertAction = UIAlertAction(title: "OK!", style: UIAlertActionStyle.Default) { (UIAlertAction) -> Void in }
        alert.addAction(alertAction)
        
        // Display error message if location updates are declined
        self.window?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
        
    default:
        break
    }
}

Notice that in the first case we called three functions

locationManager.startMonitoringForRegion(region)
locationManager.startRangingBeaconsInRegion(region)
locationManager.requestStateForRegion(region)

startMonitoringForRegion function as its name suggests, tells the app to check if the region is in range.

startRanginBeaconsInRegion gives you the distance of the beacons of type CLProximity defined within the region. CLProximity which is an enum has 4 different states:

.Unknown
.Immediete
.Near
.Far

requestStateForRegion gives the locationManager updates on the region prixmity. This belongs to CLRegionState which is also an enum and can be:

.Unknown
.Inside
.Outside

iBeacon usage example

iBeacon usage example

Getting user location

We need to implement didDeterminState for a region and a switch inside which will give notifications when the user enters and leaves the region. The code is simple and straitforward and looks like this:

func locationManager(manager: CLLocationManager!, didDetermineState state: CLRegionState, forRegion region: CLRegion!) {

    switch state {      

    case .Unknown:
        println("unknown") 

    case .Inside:
        println("inside")

    case .Outside:
      println("outside")

    }
} 

Implement this function at the bottom of the AppDelegete. Notice that we wrote println() inside the cases. We will replace those with notification calls in the following steps.

iBeacon usage inside shopping mall

iBeacon usage inside shopping mall

Handling location events

There are several cases that can hapen that we need to consider. We need to display messages for each of following scenarios:

  • When the user is inside the region and opens an app
  • When the user just entered the region
  • When the user exits the region
  • When the user is outside the region
  • When the location is unknown (safety backup)

First thing that we need to do is define a varaible through which we will control if the user has entered the region or not. Define a boolean enteredRegion variable at the top of the AppDelegate and set it to false:

var enteredRegion = false

At the bottom of the AppDelegate implement functions didEnterRegion and didExitRegion and set the enteredRegion variable to true and false respecively. Your functions should look like this:

func locationManager(manager: CLLocationManager!, didEnterRegion region: CLRegion!) {
    enteredRegion = true
}

func locationManager(manager: CLLocationManager!, didExitRegion region: CLRegion!) {
    enteredRegion = false
}
Create a new class in Xcode

Create a new class in Xcode

Create notifications class

We need to create a separate class for notifications, so go ahead and create a new Swift file and name it “Notifications” and implement the function display():

import Foundation
import UIKit

class Notifications {

  class func display(text: String){

      let notification: UILocalNotification = UILocalNotification()
      notification.timeZone = NSTimeZone.defaultTimeZone()

      let dateTime = NSDate()
      notification.fireDate = dateTime
      notification.alertBody = text
      UIApplication.sharedApplication().scheduleLocalNotification(notification)

  }

}

Display notifications

Now it’s finaly time to display the notifications based on the user location. Go back to the AppDelegate and add a line of code that will ask the user for displaying notifications permision. Add the code at the top of the class:

application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: UIUserNotificationType.Alert, categories: nil))

Next, find the didDetermineState function that you’ve implemented previously and instead of println() placeholders add the cases that we described. Your updated function should look like this:

func locationManager(manager: CLLocationManager, didDetermineState state: CLRegionState, forRegion region: CLRegion) {
    
    switch state {
        
    case .Unknown:
        print("I have no memory of this place.")
    
    case .Inside:
        var text = "All aboard the hype train."
    
        if (enteredRegion) {
            text = "Welcome to Millennium Phalcon kiddo."
        }
        
        Notifications.display(text)

    case .Outside:
        var text = "Sthaaaaap."
    
        if (!enteredRegion) {
            text = "I find your lack of faith, distrubing!"
        }
        
        Notifications.display(text)
    }
}

In the .Inside case first notification is displayed when the user is inside the region and opens an app. Second notification is shown when the app is in background and the user enters the region. Similar logic follows the .Outside case. It should be noted that the further you are from the beacons the longer it will take for the signal to propagate which means it may take few seconds for the notifications to be displayed when you leave or enter the region.

Notifications example

Notifications example

Adding TableViewController

Adding UITableViewController

Adding UITableViewController

We’re done with the notifications, and the next step is to display the beacons in range. Go to your MainStoryboard and delete the ViewController that the Xcode generated. You can also delete the ViewController.swift file from your project. In the Object library in the right bottom of your screen find the TableViewController and drag it to the Storyboard. Next, in the Attribute inspector under the section “View Controlelr” check the checkbox “Is Initial View Controller”.

Create BeaconTableViewController.swift file

Create new iOS Swift file and name it BeaconTableViewController. This is going to be a class for the TableViewController. For now add the code below to your file:

import Foundation
import UIKit

class BeaconTableViewController : UITableViewController {

    override func viewDidLoad() {

    }

    override func didReceiveMemoryWarning() {

    }

}

Now we have to connect the class with the TableViewController that we dragged. In your MainStoryboard select TableViewController and in the Identity inspector change the class to BeaconTableViewController.

Create TableView and TableViewCell IBOutlet

Outlet connection example

Outlet connection example

In the MainStoryboard select TableView and while holding Control key on the keyboard drag it to the BeaconTableVIewController.swift to create the outlet. Name the IBOutlet beaconTableView. With this you have sucessfully referenced TableView from the storyboard.

The code that should have been generated for you looks like this:

 @IBOutlet var beaconTableView: UITableView!

Add cell identifier

We have to add identifer for the TableViewCell. Go to your MainStoryboard and select TableViewCell. In the attributes inspector type “beaconCell” as an Identifer and hit enter.

Enable background data fetch

Enable background data fetch

Pass the beacons from AppDelegate

Once we identify which beacons are in range we have to pass them from AppDelegate to our BeaconsTableViewController. In order to do that, go to your AppDelegate and instanciate an empty array of beacons:

 var beacons = []

Then, at the bottom of the class, implement didRangeBeacons function and populate the array with beacons in range:

func locationManager(manager: CLLocationManager!, didRangeBeacons beacons: [AnyObject]!, inRegion region: CLBeaconRegion!) {
    self.beacons = beacons
}

We want to have a real time update of the beacon proximity in our BeaconsTableView. In order to that, AppDelegate has to tell the ViewController each time the beacon range changes to reload the data in the table. We will implement NSNotification class methods in both AppDelegate and our BeaconTableViewController, so each time a beacon proximity is changed we will send a notification to Beacon controller and update the table data. First thing we have to do is enable remote notifications in Background modes. In your project targets under Capabilities tab turn on Background modes and make sure remote notification checkbox is checked. Check the image above for reference.

Final thing, in the AppDelegate we have to add one line of code that will send notification to our Beacon controller every time you change your location inside the region. In the didRangeBeacons function add the following line of code at the end of the function:

 NSNotificationCenter.defaultCenter().postNotificationName("updateBeaconTableView", object: self.beacons)

Update TableView in BeaconTableViewController

We’re done with the AppDelegate and now in the BeaconTableViewController we have to call the function that will check for new notifications and assign a selector that we will use to update the table data.

First, instantiace beacons array at the top of the class:

 var beacons : [CLBeacon] = [CLBeacon]()

Do not forget to include CoreLocation as well. In the viewDidLoad function inside your BeaconTableViewController add the following line of code:

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.updateView(_:)), name: "updateBeaconTableView", object: nil)

Now we have to implement the function updateView. Note that we passed beacons as an object through NSNotificationCenter

func updateView(note: NSNotification!){
    beacons = note.object! as! [CLBeacon]
    beaconTableView.reloadData()
}
Table view

Table view

Implement TableView functions

We’re almost done! Final step is to implement several of the TableView functions so we can display our beacons nicely. Our table will have only one section so the first, we will add is numberOfSectionsInTableView and return 1

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

Next function defines the number of rows in the table, which in our case is the number of beacons:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return beacons.count
}

Finaly, we instantiate the TableView cell. For each beacon we will display major, minor value and the proximity which is an enum with these values:

– .Near
– .Immediate
– .Far
– .Unknown

Your code should look like this :

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCellWithIdentifier("beaconCell", forIndexPath: indexPath) as UITableViewCell
    
    let major = (beacons[indexPath.row].major as NSNumber).stringValue
    let minor = (beacons[indexPath.row].minor as NSNumber).stringValue
    
    let proximity = beacons[indexPath.row].proximity
    var proximityString = String()
    
    switch proximity {
        case .Near:
            proximityString = "Near"
        
        case .Immediate:
            proximityString = "Immediate"
        
        case .Far:
            proximityString = "Far"
        
        case .Unknown:
            proximityString = "Unknown"
    }
    
    cell.textLabel?.text = "Major: \(major) Minor: \(minor) Proximity: \(proximityString) "
    
    return cell
}

We can also add a title to our table:

override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return "Beacons in range"
}

And that’s about it! Before you run the code, go to your MainStoryboard, click on the BeaconTableViewController and in the top menu select Editor and embed in Navigation Controller.

Application running

Application running

Conclusion

When you run your app you should see something similar to the image above. We have a lot of beacons in our office so the list is quite large. Depending on the signal emiting interval on your beacon you will notice the flactuation and how the distance changes even when your device is stationary. The reason this happens is because beacons use unlicenced 2.4 Ghz frequency on which probably your WI-FI router works so the signal interfeeres. In addition this frequency is absorbed by our body and in general by any object in the room which means, more obsticles more flactuation.

This could be solved by increasing the strenght of the beacon signal and the interval of the signal emition, however your beacon battery would last much shorter and that kind of kills the whole point of having beacons.

So, you might ask yourself what can I do with a device that acts as a drunk guy in a bar? Well, eventhough iBeacons (still) cannot be used as a reliable hardware for implementing trilaterration algorithm and simulating GPS inside the building, they are good enough to give us an approximate position, and with a little bit of creativity in our code that’s more than we need.

You can find a real life example (very extreme one) of using iBeacons for indoor location on the following link: https://www.youtube.com/watch?v=oKQP2yuFyjM. Also, if your’re interested in further exploring this field here are few useful links you can use as a good reference or a starting point for your project:

Apple Core Location official documentation: developer.apple.com

Estimote iOS SDK: https://github.com/Estimote/iOS-SDK

The state of iBeacons – panel discusion https://www.youtube.com/watch?v=9m56kWk_M1k

If you have any questions related to the tutorial feel free to comment and ask. You can find the complete code for the tutorial on GitHub here.