Developing iOS 7 Applications with iBeacons Tutorial

Developing iOS 7 Applications with iBeacons Tutorial

Have you ever wished that your phone could show your location inside a large building like a shopping mall or baseball stadium?

Sure, GPS can give you an idea on which side of the building you are. But good luck getting an accurate GPS signal in one of those steel and concrete sarcophaguses. What you need is something inside of the building to let your device determine its physical location.

Enter… iBeacons!

In this iBeacons tutorial you’ll create an app that lets you register known iBeacon emitters and tells you when your phone has moved outside of their range. If you’re wondering how this could be useful, just think about how many times you’ve arrived at work or school early, only to have to turn around because you forgot your laptop bag.

This handy little app and an iBeacon could save you gas, time and even face — when you throw out some choice language around your colleagues first thing Monday morning as you storm out the door to retrieve your laptop bag!

The use case for this app is attaching an iBeacon emitter to your laptop bag, purse, or even your cat’s collar — anything important you don’t want to lose. Once your device moves outside the range of the emitter, your app detects this and notifies you.

By the end of this iBeacons tutorial you’ll understand how to monitor iBeacons and react appropriately when you encounter one.

iBeacon Hardware

When Apple introduced iBeacon in iOS 7, they also announced that any compatible iOS device could act as an iBeacon. However, they also stated that hardware vendors could create stand-alone, low-cost iBeacons as well. As of this posting, it’s been about six months since iOS 7 was released and multiple companies have announced and released stand-alone hardware iBeacon emitters.

iBeacons use Bluetooth LE technology, so you must possess an iOS device with built-in Bluetooth LE to work with iBeacons. The list currently includes the following devices:

  • iPhone 4s or later
  • 3rd generation iPad or later
  • iPad Mini or later
  • 5th generation iPod touch or later


I was lucky enough to get my hands on some evaluation units created by the talented (and friendly!) team at KS Technologies. Their iBeacon hardware, named Particle, comes pre-programmed to broadcast a specific UUID, major and minor combination — you’ll learn what these are shortly. It also boasts a button cell battery advertised to keep your iBeacon running for up to six months.

KS Technologies has two apps available the App Store: Particle Detector and Particle Accelerator. Particle Detector allows you to easily monitor for iBeacons without writing a single line of code, while Particle Accelerator allows you to wirelessly reconfigure the devices as well as update their firmware.

There are other iBeacon offerings out there as well, a quick Google search should reveal them to you. For the purposes of this tutorial, you’ll focus on the Particle by KS Technologies, although the basic concepts should apply to most other iBeacon hardware.

Note: If you do not have a standalone iBeacon emitter but you do have another iOS 7 device that supports Bluetooth LE, you can follow along by creating an app that acts as an iBeacon as described in Chapter 22 — What’s new in Core Location of iOS 7 by Tutorials.

UUID, Major, and Minor identifiers

If you’re unfamiliar with iBeacons, you might not be familiar with the terms UUID, major value and minor value.

An iBeacon is nothing more than a Bluetooth Low Energy device that advertises information in a specific structure. Those specifics are beyond the scope of this tutorial, but the important thing to understand is that iOS can monitor for iBeacons based on these UUID, major and minor values.

UUID is an acronym for universally unique identifier, which is effectively a random string; B558CBDA-4472-4211-A350-FF1196FFE8C8 is one example. In the context of iBeacons, a UUID is generally used to represent your top-level identity. If you generate a UUID as a developer and assign it to your iBeacon device, then when a device detects your iBeacon it knows exactly which iBeacon it’s talking to.

Major and minor values provide a little more granularity on top of the UUID. These values are simply 16 bit unsigned integers that identify each individual iBeacon, even ones with the same UUID.

For instance, if you owned multiple department stores you might have all of your iBeacons emit the same UUID, but each store would have its own major value, and each department within that store would have its own minor value. Your app could then respond to an iBeacon located in the shoe department of your Miami, Florida store.

You can learn more about UUIDs in this Wikipedia article on UUIDs

Getting Started

Download the starter project here — it contains a simple interface for adding and removing items from a table view. Each item in the table view represents a single iBeacon emitter, which in the real world translates to an item that you don’t want to leave behind.

Build and run the app; you’ll see an empty list, devoid of items. Press the + button at the top right to add a new item as shown in the screenshot below:

First Launch

First Launch

To add an item, you simply enter a name for the item and the values corresponding to its iBeacon. Try using 8AEFB031-6C32-486F-825B-E26FA193487D for the UUID, 1 for the major value and 2 for the minor value as placeholder values for now, as shown below:

Adding an Item

Adding an item

Note: You can find your iBeacon’s UUID using companion apps such as Particle Detector or by reviewing your iBeacon’s documentation.

Press Save to return to the list of items; you’ll see your item with a location of Unknown, as shown below:


You can add more items if you wish, or swipe to delete existing ones. NSUserDefaults persists the items in the list so that they’re available when the user re-launches the app.

On the surface it appears there’s not much going on; most of the fun stuff is under the hood. The majority of the project will be straightforward for the seasoned iOS developer. The unique aspect in this app is the RWTItem model class which represents the items in the list.

Open RWTItem.h and have a look at it in Xcode. The model class mirrors what the interface requests from the user, and it conforms to NSCoding so that it can be serialized and deserialized to disk for persistence.

Now take a look at RWTAddItemViewController.m. This is the controller for adding a new item. It’s a simple UITableViewController, except that it does some validation on user input to ensure that the user enters valid names and UUIDs. The name field is only required to be at least one character, whereas the UUID field has more strict requirements based on the UUID specification.

Take a closer look at the following line in viewDidLoad:.

NSString *uuidPatternString = @"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";

This is a regular expression pattern that checks the entered UUID against the standard UUID format. It returns TRUE if the string conforms to the format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, where all X’s are a hexadecimal value 0 through F. All iBeacons must broadcast a UUID and it must meet the above specifications.

The Save button at the top right becomes tappable as soon as nameTextField and uuidTextField are both valid.

Now that you’re acquainted with the starter project, you can move on to implementing the iBeacon bits into your project!

Listening for Your iBeacon

Your device won’t listen for your iBeacon automatically — you have to tell it to do this first. The CLBeaconRegion class represents an iBeacon; the CL class prefix infers that it is part of the Core Location framework.

It may seem strange for an iBeacon to be related to Core Location since it’s a Bluetooth device, but consider that iBeacons provide micro-location awareness while GPS provides macro-location awareness. You would leverage the Core Bluetooth framework for iBeacons when programming an iOS device to act as an iBeacon, but when monitoring for iBeacons you only need to work with Core Location.

Your first order of business is to adapt the RWTItem model for CLBeaconRegion.

Open RWTItem.h and replace it’s contents with the code shown below:

#import <Foundation/Foundation.h>
@import CoreLocation;
@interface RWTItem : NSObject <NSCoding>
@property (strong, nonatomic, readonly) NSString *name;
@property (strong, nonatomic, readonly) NSUUID *uuid;
@property (assign, nonatomic, readonly) CLBeaconMajorValue majorValue;
@property (assign, nonatomic, readonly) CLBeaconMinorValue minorValue;
- (instancetype)initWithName:(NSString *)name
                        uuid:(NSUUID *)uuid

The only differences here are that you import CoreLocation and replace the uint16_t types with CLBeaconMajorValue and CLBeaconMinorValue types for the majorValue and minorValue properties respectively.

Although the underlying data type is the same, this improves readability of the model.

Open RWTItem.m and update the types in the initWithName:uuid:major:minor: method’s signature as shown below:

- (instancetype)initWithName:(NSString *)name
                        uuid:(NSUUID *)uuid

This simply suppress the compiler warnings.

Open RWTItemsViewController.m, add an import statement for Core Location below the other imports, add CLLocationManagerDelegate as a protocol that this class conforms to, and finally create a new locationManager property of type CLLocationManager, as shown below:

#import "RWTItemCell.h"
@import CoreLocation;
static NSString * const kRWTStoredItemsKey = @"storedItems";
@interface RWTItemsViewController () <UITableViewDataSource, UITableViewDelegate, CLLocationManagerDelegate>
@property (weak, nonatomic) IBOutlet UITableView *itemsTableView;
@property (strong, nonatomic) NSMutableArray *items;
@property (strong, nonatomic) CLLocationManager *locationManager;

Next, in viewDidLoad: add a call to initialize the self.locationManager property and assign its delegate to self, as shown below:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    [self loadItems];

This simply instructs the location manager that the class wants to receive its delegate method calls.

Now that you have an instance of CLLocationManager, you can instruct your app to begin monitoring for specific regions using CLBeaconRegion. When you register a region to be monitored, those regions persist between launches of your application. This will be important later when you respond to the boundary of a region being crossed while your application is not running.

Your iBeacon items in the list are represented by the the RWTItem model via the items property. CLLocationManager, however, expects you to provide a CLBeaconRegion instance in order to begin monitoring a region.

Open RWTItemsViewController.m and create the following helper method:

- (CLBeaconRegion *)beaconRegionWithItem:(RWTItem *)item {
    CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:item.uuid
    return beaconRegion;

This returns a new CLBeaconRegion instance derived from the provided RWTItem.

You can see that the classes are similar in structure to each other, so creating an instance of CLBeaconRegion is very straightforward with initWithProximityUUID:major:minor:identifier:.

Now you need a method to begin monitoring a given item.

Still working in RWTItemsViewController.m, add the following code directly below beaconRegionWithItem::

- (void)startMonitoringItem:(RWTItem *)item {
    CLBeaconRegion *beaconRegion = [self beaconRegionWithItem:item];
    [self.locationManager startMonitoringForRegion:beaconRegion];
    [self.locationManager startRangingBeaconsInRegion:beaconRegion];

This method takes an RWTItem instance and creates a CLBeaconRegion instance using the method you defined earlier. It then tells the location manager to start monitoring the given region, and to start ranging iBeacons within that region. Ranging is the process of discovering iBeacons within the given region, and determining their distance. An iOS device receiving an iBeacon transmission can approximate the distance from the iBeacon. The distance (between transmitting iBeacon and receiving device) is categorised into 3 distinct ranges:

  • Immediate Within a few centimetres
  • Near Within a couple of metres
  • Far Greater than 10 metres away

By default, monitoring notifies you when the region is entered or exited regardless of whether your app is running. Ranging, on the other hand, monitors the proximity of the region only while your app is running.

You’ll also need a way to stop monitoring an item’s region after it’s deleted.

Add the following code to RWTItemsViewController.m directly below startMonitoringItem::

- (void)stopMonitoringItem:(RWTItem *)item {
    CLBeaconRegion *beaconRegion = [self beaconRegionWithItem:item];
    [self.locationManager stopMonitoringForRegion:beaconRegion];
    [self.locationManager stopRangingBeaconsInRegion:beaconRegion];

The above method is identical to startMonitoringItem: except that it calls the stop variants of the location manager’s monitor and ranging activities.

Now that you have the start and stop methods, it’s time to put them to use! The natural place to start monitoring is when a user adds a new item to the list.

Have a look at prepareForSegue: in RWTItemsViewController.m; you’ll see that RWTAddItemViewController has a callback that runs when you add items via the itemAddedCompletion block property.

Add a call to startMonitoringItem: in the itemAddedCompletion block as shown below:

[addItemViewController setItemAddedCompletion:^(RWTItem *newItem) {
    [self.items addObject:newItem];
    [self.itemsTableView beginUpdates];
    NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:self.items.count-1 inSection:0];
    [self.itemsTableView insertRowsAtIndexPaths:@[newIndexPath]
    [self.itemsTableView endUpdates];
    [self startMonitoringItem:newItem]; // Add this statement
    [self persistItems];

Now when you add a new item you call startMonitoringItem: immediately. Take note of the subsequent call to persistItems; this takes all of the known items and persists them to NSUserDefaults so that the user won’t have to to re-enter their items each time they launch the app.

In RWTItemsViewController.m, viewDidLoad: calls loadItems which reads the persisted items from the user defaults and stores them in the items array.

Once the persisted items are loaded into memory, you have to start monitoring for them.

Still working in RWTItemsViewController.m, update loadItems to make sure each item is being monitored, as shown below:

- (void)loadItems {
    NSArray *storedItems = [[NSUserDefaults standardUserDefaults] arrayForKey:kRWTStoredItemsKey];
    self.items = [NSMutableArray array];
    if (storedItems) {
        for (NSData *itemData in storedItems) {
            RWTItem *item = [NSKeyedUnarchiver unarchiveObjectWithData:itemData];
            [self.items addObject:item];
            [self startMonitoringItem:item]; // Add this statement

Now every time you load the items from the persistence store you also instruct the location manager to begin monitoring for them.

You now have to take care of removing items from the list.

Replace the contents of tableView:commitEditingStyle:forRowAtIndexPath: with the following:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        RWTItem *itemToRemove = [self.items objectAtIndex:indexPath.row];
        [self stopMonitoringItem:itemToRemove];
        [tableView beginUpdates];
        [self.items removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        [tableView endUpdates];
        [self persistItems];

In the code above you check if the editing style is UITableViewCellEditingStyleDelete; if so, you remove the item from the items array and use it to invoke stopMonitoringItem:.

It should strike you that a lot of this work has been really simple to implement. Apple has done an incredible job of making iBeacons very easy to use as a developer.

At this point you’ve made a lot of progress! Your application now starts and stops listening for specific iBeacons as appropriate.

You can build and run your app at this point; but even though your registered iBeacons might be within range your app has no idea how to react when it finds one!

Time to fix that!

Acting on Found iBeacons

Now that your location manager is listening for iBeacons, it’s time to react to them!

When you instantiated CLLocationManager earlier, you also set its delegate to self. It’s time to implement some of those delegate methods.

First and foremost is to add some error handling, since you’re dealing with very specific hardware features of the device and you want to know if the monitoring or ranging fails for any reason.

Add the following two methods to RWTItemsViewController.m:

- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error {
    NSLog(@"Failed monitoring region: %@", error);
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    NSLog(@"Location manager failed: %@", error);

These methods simply log the received error so you can analyze the output.

If everything goes smoothly in your app you should never see any output from these calls. However, it’s possible that these log messages could provide very valuable information if something isn’t working; it’s better than failing silently. Simple NSLog statements are okay for a tutorial, but in your shipped application you should handle the error situations in a more robust manner.

The next step is to display the perceived proximity of your registered iBeacons in real-time.

Add the following stubbed-out method to RWTItemsViewController.m:

- (void)locationManager:(CLLocationManager *)manager
        didRangeBeacons:(NSArray *)beacons
               inRegion:(CLBeaconRegion *)region

The above delegate method is called when iBeacons come within range, move out of range, or when the range of an iBeacon changes.

The goal of your app is to use the passed-in array of ranged iBeacons to update the list of items and display their perceived proximity. You’ll start by iterating over the beacons array to match the ranged iBeacons with the ones in your list.

Update your stubbed-out implementation of locationManager: as shown below:

- (void)locationManager:(CLLocationManager *)manager
        didRangeBeacons:(NSArray *)beacons
               inRegion:(CLBeaconRegion *)region
    for (CLBeacon *beacon in beacons) {
        for (RWTItem *item in self.items) {
            // Determine if item is equal to ranged beacon

Now each time you range the iBeacons, you’ll iterate over both the ranged beacons and the items.

Note the comment in the second for loop; this is where you’ll need to implement some logic to check if this is the iBeacon representing the current item.

Open RWTItem.h and add the following method declaration:

- (BOOL)isEqualToCLBeacon:(CLBeacon *)beacon;

This new method compares a CLBeacon instance with the RWTItem instance to see if they are equal — that is, if all of their identifiers match.

Add the following property to RWTItem.h:

@property (strong, nonatomic) CLBeacon *lastSeenBeacon;

This property stores the last CLBeacon instance seen for this specific item; this is used to display the proximity information.

Add the following method to RWTItem.m:

- (BOOL)isEqualToCLBeacon:(CLBeacon *)beacon {
    if ([[beacon.proximityUUID UUIDString] isEqualToString:[self.uuid UUIDString]] &&
        [beacon.major isEqual: @(self.majorValue)] &&
        [beacon.minor isEqual: @(self.minorValue)])
        return YES;
    } else {
        return NO;

A CLBeacon instance is equal to an RWTItem if the UUID, major, and minor values are all equal.

Now you’ll need to complete the ranging delegate method with a call to the above helper method.

Update locationManager:didRangeBeacons:inRegion in RWTItemsViewController.m to call your new method, as follows:

- (void)locationManager:(CLLocationManager *)manager
        didRangeBeacons:(NSArray *)beacons
               inRegion:(CLBeaconRegion *)region
    for (CLBeacon *beacon in beacons) {
        for (RWTItem *item in self.items) {
            if ([item isEqualToCLBeacon:beacon]) {
                item.lastSeenBeacon = beacon;

In the code above you set lastSeenBeacon when you find a matching item and iBeacon.

Now it’s time to use that property to display the perceived proximity of the ranged iBeacon.

Open RWTItemCell.m and update the setItem: as follows:

- (void)setItem:(RWTItem *)item {
    if (_item) {
        [_item removeObserver:self forKeyPath:@"lastSeenBeacon"];
    _item = item;
    [_item addObserver:self 
    self.textLabel.text =;

When you set the item for the cell you also add an observer for the lastSeenBeacon property. Additionally, if the cell already had an item set you remove the observer to keep things properly balanced, as required by key-value observation.

You should also remove the observer when the cell is deallocated. Still in RWTItemCell.m, add the following method:

- (void)dealloc {
    [_item removeObserver:self forKeyPath:@"lastSeenBeacon"];

Now that you’re observing for the value, you can put some logic in to react to any changes in the iBeacon’s proximity.

Each CLBeacon instance has a proximity property which is an enum , so you must translate its integer value to something more meaningful.

Add the following method to RWTItemCell.m:

- (NSString *)nameForProximity:(CLProximity)proximity {
    switch (proximity) {
        case CLProximityUnknown:
            return @"Unknown";
        case CLProximityImmediate:
            return @"Immediate";
        case CLProximityNear:
            return @"Near";
        case CLProximityFar:
            return @"Far";

This returns a human-readable proximity value from proximity which you’ll use in the method below.

Now add the observeValueForKeyPath:ofObject:change:context: method, as follows:

- (void)observeValueForKeyPath:(NSString *)keyPath 
                        change:(NSDictionary *)change 
                       context:(void *)context {
    if ([object isEqual:self.item] && [keyPath isEqualToString:@"lastSeenBeacon"]) {
        NSString *proximity = [self nameForProximity:self.item.lastSeenBeacon.proximity]
        self.detailTextLabel.text = [NSString stringWithFormat:@"Location: %@", proximity];

You call the above method each time the lastSeenBeacon value changes, which sets the cell’s detailTextLabel.text property with the perceived proximity value.

Build and run your app; ensure your iBeacon is registered and move your device closer or away from your device. You’ll see the label update as you move around, as shown below:


You may find that the perceived proximity is drastically affected by the physical location of your iBeacon; if it’s placed inside of something like a box or a bag, the signal may be blocked as the iBeacon is a very low-power device and the signal can attenuate easily.

Keep this in mind when designing your application — and when deciding the best placement for your iBeacon hardware.


Things feel pretty complete at this point; you have your list of iBeacons and can monitor their proximity in real time. But that isn’t the end goal of your app. You still need to notify the user when the app is not running in case they forgot their laptop bag or their cat ran away — or worse, if their cat ran away with the laptop bag! :]


They look so innocent, don’t they?

If you’ve learned anything by this point, it’s that it doesn’t take much code to add a lot of iBeacon functionality — and these last few methods are no different.

Open RWTAppDelegate.m and import the Core Location module as such:

@import CoreLocation;

Next, update the class extension to conform to the CLLocationManagerDelegate protocol and add a property for a CLLocationManager instance as shown below:

@interface RWTAppDelegate () <CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;

Just as before, you need to initialize the location manager and set the delegate accordingly.

Add the following statements to the very top of application:didFinishLaunchingWithOptions::

self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;

At first glance, this likely seems a little too simple. What’s the use of a newly allocated instance of CLLocationManager when your app launches and how will it know about the monitored regions?

Recall that any regions you add for monitoring using startMonitoringForRegion: are shared by all location managers in your application. So you get a little persistence for free, which turns out to be extremely helpful.

Without this capability, it would be up to you to figure out which regions were being monitored and to start monitoring them again each time the app is launched. But even that wouldn’t be sufficient, since your app wouldn’t know to wake up when a region was encountered.

Thankfully Apple has done a lot of the heavy lifting here for you in Core Location. The final step here is simply to react when Core Location wakes up your app when a region is encountered.

Add the following method to the bottom of RWTAppDelegate.m:

- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
    if ([region isKindOfClass:[CLBeaconRegion class]]) {
        UILocalNotification *notification = [[UILocalNotification alloc] init];
        notification.alertBody = @"Are you forgetting something?";
        notification.soundName = @"Default";
        [[UIApplication sharedApplication] presentLocalNotificationNow:notification];

Your location manager calls the above method when you exit a region, which is the event of interest for this app. You don’t need to be notified if you move closer to your laptop bag — only if you move too far away from it.

Here you check the region to see if it’s a CLBeaconRegion, since it’s possible it could be a CLCircularRegion if you’re also performing geolocation region monitoring. Then you post a local notification with the generic message “Are you forgetting something?“.

Build and run your app; move away from one of your registered iBeacons and you’ll see the notification pop up once you move far enough away:

If it’s not practical to move far enough away from your iBeacon you can just power it down or remove the battery to test this functionality.

Note: A few final notes on iBeacon and iOS behavior:

iOS 7.1 added the ability to wake your app from the background when it encounters monitored iBeacons. Previously, users had to have the app opened to react to notifications, but now it all works for free!

Apple delays exit notifications in undocumented ways. This is probably by design so that your app doesn’t receive premature notifications if you’re loitering on the fringe of the range or if the iBeacon’s signal is briefly interrupted. In my experience, the exit notification usually occurs one to two minutes after the iBeacon is out of range.

Where to Go From Here?

You now have a very useful app for monitoring those things that you find tricky to keep track of.

You can download the final project here.

With a bit of imagination and coding prowess you could add a lot of really useful features to this app:

  • Notify the user which item has moved out of range.
  • Repeat the notification to make sure the user sees it.
  • Alert the user when iBeacon is back in range.
  • …or anything else you can dream up!

This iBeacons tutorial merely scratches the surface of what’s possible with iBeacons. At the beginning of this tutorial I’ve provided a few links to articles showcasing how Major League Baseball and shopping malls are using iBeacons in very engaging ways.

iBeacons aren’t just limited to custom apps; you can use them with Passbook passes as well. Imagine that you ran a movie theater; you could offer movie tickets as Passbook passes. Then when the patrons walked up to the ticket taker their app would present the ticket on their iPhone automatically.

If you have any questions or comments on this tutorial, or if you have any novel ideas for the use of iBeacons, feel free to join the discussion below!

Developing iOS 7 Applications with iBeacons Tutorial is a post from: Ray Wenderlich

The post Developing iOS 7 Applications with iBeacons Tutorial appeared first on Ray Wenderlich.



Write a comment