Cross Platform C#
Build Context-Aware Apps with iBeacons
Context is king, and your app can easily create hyper-local experiences with iBeacons!
- By Greg Shackles
- 09/04/2014
One of the most powerful things about mobile apps is their ability to provide a customized experience for the user based on where the user is at any given moment -- right here, right now. Previously in this column I discussed SignalR, and how easy it makes adding real-time communications between your server and your app. This is extremely useful for addressing the "right now" side of the UX.
For catering your UX to the user's current location, there have been several options available over the years. The most common way of getting a user's location is to use the device's built-in location services, which can take advantage of GPS, Wi-Fi, and the cellular signal to determine where he is. For many purposes these methods work well, but they do come with some trade-offs. For one, the accuracy can vary depending on which services are available. They also tend to be wasteful in terms of battery power, and don't work well indoors, which can be problematic for some applications.
Bluetooth has been around for awhile for providing short-range communication between devices, but with the release of Bluetooth 4.0 came an important new piece: Bluetooth Low Energy (BLE), also called Bluetooth Smart. BLE has seen a real surge in use over the last few years, and is used in all sorts of devices, such as the Pebble watch and the Fitbit fitness tracker. As the name implies, BLE provides a way to use Bluetooth to create pairings between devices that come with a much lower energy cost than standard Bluetooth.
Introducing the iBeacon
Building on top of BLE, Apple introduced its iBeacon standard with the release of iOS 7, which allows for easily creating a low-energy indoor proximity system. This type of system opens up all sorts of possibilities for creating rich, context-aware applications based on a user's location, even as he moves within a particular location. Imagine a scenario where beacons are placed throughout different departments of a store, even in different areas within those departments, and an app can easily detect where the user is and deliver content based on his location. This is the kind of experience GPS simply can't provide. This can already be seen in Apple stores, and is being adopted in many other places such as Major League Baseball ballparks, museums and so on.
The primary function of a beacon is to allow an app to determine proximity, rather than a specific geographic location. Although I shared the use case of putting beacons throughout a store to designate different departments, there's no rule saying a beacon needs to be stationary. Once you realize that beacons can be attached to anything, the possibilities really start to open up. Employees of the store could have beacons so customers can find them, or you could attach a beacon to your luggage to determine when it's coming on the carousel at an airport.
On the technical side, a big difference between iBeacons and normal BLE devices is that all iBeacon information is contained in its Bluetooth advertisement that it regularly broadcasts, so there's never any need to actually pair with it. Included in this advertisement are three key pieces of information that make up an iBeacon:
- UUID: a 128-bit value used to define a group of beacons
- Major: a 16-bit unsigned integer used to group related beacons within the same UUID
- Minor: a 16-bit unsigned integer
Using the department store example, you can imagine the UUID being unique to the chain of stores, the major identifier being unique to a particular location, and the minor identifier signifying a department within that store. It's completely up to you as a developer to decide what makes sense for your applications.
There are two different modes of interacting with iBeacons in your apps:
- Monitoring: listen for when the user enters or exits a particular region
- Ranging: listen for updates on the user's distance from a particular beacon based on its signal strength
These two modes can be combined to create a contextually aware experience for users of an app.
iBeacon Support
iBeacons come in both software and hardware options. Any iOS 7 device with BLE support can be turned into an iBeacon, so this includes the iPhone 4S+ and the iPad 3+. Similarly, a Mac running OS X Mavericks and onward can also be turned into an iBeacon. BLE was added to Android in version 4.3, so devices running this or newer can detect beacons, but cannot transmit as one.
There are also many companies selling different hardware solutions. Some examples are: Estimote, Gimbal and Radius Networks.
These hardware offerings often come with a corresponding SDK from that vendor. These SDKs aren't actually necessary for interacting with the beacons, but can provide vendor-specific functionality on top of the standard APIs for things such as beacon management, security, cross-platform APIs and so on.
Creating a Software iBeacon on iOS
Creating and scanning for iBeacons using the built-in iOS APIs is trivial. To start off, I'll show how you can turn your compatible iOS device into a beacon, which requires just a few lines of code, as shown in Listing 1. Most of the code is boilerplate iOS code, with the real meat happening in ViewDidLoad. A beacon region is created using a predefined UUID, and then I tell it to start advertising. It's as simple as that! Whenever the beacon state is updated, the change is mirrored in the text of a label in the UI. The UUID here can be anything you want, but for the sake of these demos it's hardcoded so it can easily be shared with the next part.
Listing 1: Code to Turn iOS Device into a Beacon
using System;
using MonoTouch.CoreBluetooth;
using MonoTouch.CoreFoundation;
using MonoTouch.CoreLocation;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace BeaconDemo
{
partial class BroadcastViewController : UIViewController
{
private static readonly string _beaconUUID =
"62185e52-a8a8-4860-b077-350d620c09a0";
private CBPeripheralManager _peripheralManager;
public BroadcastViewController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
var beaconRegion = new CLBeaconRegion(new NSUuid(_beaconUUID), "iOS Beacon");
var peripheralData = beaconRegion.GetPeripheralData(null);
_peripheralManager = new CBPeripheralManager(new PeripheralManagerDelegate(this),
DispatchQueue.DefaultGlobalQueue);
_peripheralManager.StartAdvertising(peripheralData);
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
_peripheralManager.StopAdvertising();
}
private class PeripheralManagerDelegate : CBPeripheralManagerDelegate
{
private readonly BroadcastViewController _parentViewController;
public PeripheralManagerDelegate(BroadcastViewController parentViewController)
{
_parentViewController = parentViewController;
}
public override void StateUpdated(CBPeripheralManager peripheral)
{
InvokeOnMainThread(() =>
_parentViewController.Status.Text = peripheral.State.ToString());
}
}
}
}
Scanning for iBeacons on iOS
Now that I have a way to advertise an iBeacon, I'll show you how easy it is to detect that beacon and react to it (see Listing 2).
Listing 2: Detecting and Reacting to the iBeacon
using System;
using System.Linq;
using MonoTouch.CoreLocation;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
namespace BeaconDemo
{
partial class ScanViewController : UIViewController
{
private static readonly string _beaconUUID = "62185e52-a8a8-4860-b077-350d620c09a0";
private readonly CLBeaconRegion _beaconRegion =
new CLBeaconRegion(new NSUuid(_beaconUUID), "iOS Beacon");
private CLLocationManager _locationManager;
public ScanViewController (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
_locationManager = new CLLocationManager();
_locationManager.RegionEntered += (sender, e) =>
{
if (e.Region.Identifier != _beaconUUID)
return;
Status.Text = "Entered region";
};
_locationManager.RegionLeft += (sender, e) =>
{
if (e.Region.Identifier != _beaconUUID)
return;
Status.Text = "Left region";
};
_locationManager.DidRangeBeacons += (sender, e) =>
{
if (!e.Beacons.Any())
return;
var beacon = e.Beacons.First();
Status.Text = "Beacon Proximity: " + beacon.Proximity;
};
_locationManager.StartMonitoring(_beaconRegion);
_locationManager.StartRangingBeacons(_beaconRegion);
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
_locationManager.StopMonitoring(_beaconRegion);
_locationManager.StopRangingBeacons(_beaconRegion);
}
}
}
Once again, there isn't much code involved in detecting an iBeacon. It's as easy as creating a new instance of CLLocationManager, starting monitoring and ranging on the region, and subscribing to the relevant events. In the DidRangeBeacons callback I can use the Proximity property to get a sense of how far away the beacon is. There are three values that can be returned:
- Immediate: within a few centimeters
- Near: within a couple meters
- Far: more than 10 meters away
This proximity is approximated based on the observed signal strength and the calibrated transmitter power of the beacon, which is the signal strength measured at 1 meter away from the beacon. These are approximate calculations and can vary depending on proper calibration or obstructions between the beacon and the user that can impact the signal strength.
Wrapping Up
With technologies like iBeacon so easy to create and interact with, we're definitely entering a new era of what's possible with apps. An app can deliver an experience based on the moment, where the user is at that very moment, and where the user is compared to anything in his area relevant to the app. Even more than that, apps can proactively deliver this experience, rather than having to make the user wait while the information is gathered and processed. This has obvious applications in shopping, recreational areas such as museums and sports arenas, and even in the home. A beacon can be attached to anything, making it trivial to create a real network of things in any way you want. Context is king!