Hot&Cold - Prototype Code Review

Aaand I have iBeacons. (v0.1)

All told, I had the iBeacon code up and running in a little less that 2 hours. Unfortunately, those 2 hours were spread over three weeks as my 9 month old son decided to go through a sleep regression. Babies!

Back to the code.

The code itself was simple after reading the API docs. I’ve broken out the applications into three beautifully designed screens.

The app is using Storyboards.


Honestly, nothing too much of interest here. The app has a couple of segues set up. Each of the two buttons are hooked up to the same IBAction call and depending on which one you select you end up going to the required screen.

- (IBAction)gotoSegue:(UIButton *)sender {
    NSString *segueIdent = @"";
    if ([sender.titleLabel.text isEqualToString:@"Hide"]) {
        segueIdent = @"toSend";
    } else {
        segueIdent = @"toReceive";
    [self performSegueWithIdentifier:segueIdent sender:self];


The implementation of this is almost identical to Apple’s AirLocate APLDefaults file


extern NSString *BeaconIdentifier;
NSString *BeaconIdentifier = @"com.example.ampersand-softworks.HotCold";

The BeaconIdentifier is simply being stored as a global string constant and is declared in the header file.

-(id) init { 
    self = [super init];
    if (self){
        _supportedProximityUUIDs = @[[[NSUUID alloc] initWithUUIDString:@"D9EED498-BFDB-43C0-8B55-D06BB74C430B"]];
        _defaultPower = @-59;
    return self;

The init method populates an array of supported UUIDs, I simply used the uuidgen command in the terminal to get myself a new one and added it to the list. This is future-proofing the app as well since adding support for a whole new set of beacons is trivial.

+(ASWDefaults*) sharedDefaults {    
    static id sharedDefaults = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedDefaults = [[self alloc] init];
    return sharedDefaults;

In the past I’ve used the @synchronized in order to thread this. This is my first chance to use GCD and blocks in this manner. I heart blocks.

- (NSUUID *) defaultProximityUUID {
    return _supportedProximityUUIDs[0];

Simply returns the one (and only) uuid that we’re using. Again, future proofing.


Simply exposes the stuff that was set up.

extern NSString *BeaconIdentifier;

@interface ASWDefaults : NSObject

+(ASWDefaults *) sharedDefaults;

@property (nonatomic, copy, readonly) NSArray *supportedProximityUUIDs;
@property (nonatomic, copy, readonly) NSUUID *defaultProximityUUID;
@property (nonatomic, copy, readonly) NSNumber *defaultPower;



Let’s get our device broadcasting as an iBeacon. I was surprised at just how little code was needed in order to get the beacon broadcasting with the required region information.

@import CoreLocation;
@import CoreBluetooth;

Awwwwww yeah, precompiled header modules. I’m pretty sure the guy giving the talk on modules at WWDC mentioned that all #import calls are mapped to @import behind the scene.

CBPeripheralManager *perhipheralManager = nil;
CLBeaconRegion *region = nil;

NSDictionary *beaconPerhipheralData;
NSNumber *power = nil;

We need an instance of the CBPerhipheralManager and the CLBeaconRegion classes to begin broadcasting.

The beaconPerhipheralData dictionary and the power variables are simply there to hold setup values.

@interface ASWHideViewController () <CBPeripheralManagerDelegate>

@property NSUUID *uuid;
@property NSNumber *major;
@property NSNumber *minor;

- (void)viewDidLoad {
    [super viewDidLoad];
    self.uuid = [ASWDefaults sharedDefaults].defaultProximityUUID;
    self.major = [NSNumber numberWithShort:0];
    self.minor = [NSNumber numberWithShort:0];
    power = [ASWDefaults sharedDefaults].defaultPower;
    region = [[CLBeaconRegion alloc] initWithProximityUUID:self.uuid
                                                     major:[self.major shortValue]
                                                     minor:[self.minor shortValue]
    beaconPerhipheralData = [region peripheralDataWithMeasuredPower:power];

Inside the viewDidLoad we pull the uuid, major, minor, identifier and power values from the ASWDefaults file and alloc a new instance of the CLBeaconRegion. From there, we store that value in a dictionary for future peripheral manager use.

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    if (!perhipheralManager) {
        perhipheralManager = [[CBPeripheralManager alloc] initWithDelegate:self
                                                                     queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    } else {
        perhipheralManager.delegate = self;

Lazy load the peripheral manager with a default background thread.

-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    if (peripheral.state == CBPeripheralManagerStatePoweredOn) {
        [perhipheralManager startAdvertising:beaconPerhipheralData];
    } else if (peripheral.state == CBPeripheralManagerStatePoweredOff){
        NSLog(@"Transmission Ceased");
        [perhipheralManager stopAdvertising];

Here’s where the magic happens. If bluetooth is turned on we begin broadcasting the iBeacon region information using startAdvertising. Just like that, we have an iBeacon.


The first bit of surprising information that I discovered is that all of the iBeacon receiving code is handed by a Core Location locationManager instance. The minute that you initialize it, the user is asked if they will allow you app to know your location.

It makes sense, you could get someone’s location within a centimetre with a combination of an iBeacon and GPS. It was just a bit unexpected that the first time I ran the app.

Right now, the prototype simply shows the distance to the beacon (the value is actually the accuracy value from a CLBeacon).

@interface ASWSeekViewController () <CLLocationManagerDelegate>

@property (weak, nonatomic) IBOutlet UILabel *howClose;

@property CLLocationManager *locationManager;
@property NSMutableDictionary *rangedRegions;


The CLLocationManager does all of the magic and this view controller is it’s delegate. Some other properties are simply set up.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;
    self.rangedRegions = [[NSMutableDictionary alloc] init];
    for (NSUUID *uuid in [ASWDefaults sharedDefaults].supportedProximityUUIDs) {
        CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:[uuid UUIDString]];
        self.rangedRegions[region] = [NSArray array];

The loop here is pretty interesting.

What we’re doing is grabbing all of the beacon UUIDs from ASWdefaults file and filling the rangedBeacons array with CLBeaconRegion instances containing those values. This is used in the locationManager:didRangeBeacons and locationManager:startRangingBeaconsInRegion methods.

-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    for (CLBeaconRegion *region in self.rangedRegions) {
        [self.locationManager startRangingBeaconsInRegion:region];

This is where the magic happens. Just loop through the CLBeaconRegions in the rangedBeacons dictionary and start listening for events.

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

    if ([beacons count] > 0) {
        // Let's assume we're getting one beacon for now.
        CLBeacon *beacon = beacons[0];
        self.howClose.text = [NSString stringWithFormat:@"%f", beacon.accuracy];
    } else {
      self.howClose.text = @"No Beacons Found";

If a beacon is found we simply show the accuracy value on screen. I do get weird instances when I get -1.000 as the value. It seems to happen if there’s a lot of interference.

I’ll have to do more testing with the accuracy values to see what can happen.

Final thoughts