Blended Cocoa

Adventures in Objective-C, Swift and Cocoa

Mavericks as an iBeacon

With the release of Mavericks, Apple have brought the capabilities of Core Bluetooth in line with those of iOS. In particular Mavericks can now function as a peripheral thanks to the inclusion of the CBPeripheralManager class.

However, Apple have chosen not to include support for iBeacons in Mavericks. iBeacon support on iOS 7 has been implemented as part of the Core Location framework but Core Location on Mavericks doesn’t contain any iBeacon support.

iBeacons are built on top of Core Bluetooth so I wondered whether it would be possible to use a MacBook running Mavericks to create an iBeacon.

iOS as an iBeacon

On iOS we can use a Bluetooth 4 enabled device to create an iBeacon using the following code:

BLCAppDelegate.mlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//
//  BLCAppDelegate.m
//  Beacon
//
//  Created by Matthew Robinson on 15/09/13.
//  Copyright (c) 2013 Blended Cocoa. All rights reserved.
//

#import "BLCAppDelegate.h"

#import <CoreLocation/CoreLocation.h>
#import <CoreBluetooth/CoreBluetooth.h>

@interface BLCAppDelegate () <CBPeripheralManagerDelegate>

@property (strong,nonatomic) CBPeripheralManager *peripheralManager;

@end

@implementation BLCAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self
                                                                 queue:nil];
    return YES;
}

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    if (peripheral.state == CBPeripheralManagerStatePoweredOn) {

        NSUUID *proximityUUID = [[NSUUID alloc] initWithUUIDString:@"A7C4C5FA-A8DD-4BA1-B9A8-A240584F02D3"];

        CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:proximityUUID
                                                                         major:1
                                                                         minor:1000
                                                                    identifier:@"com.blendedcocoa.beacon"];

        NSDictionary *proximityData = [region peripheralDataWithMeasuredPower:nil];


        [peripheral startAdvertising:proximityData];
    }
}

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error {
    NSLog(@"Started advertising");
}

Creating an iBeacon on iOS is quite simple. First we create a CLBeaconRegion with the required proximityUUID, major & minor and then use the peripheralDataWithMeasuredPower: method to get an NSDictionary containing the advertising data to be passed to Core Bluetooth.

Advertisement Data

It occurred to me that it may be possible to take this NSDictionary and pass it to a CBPeripheralManager instance on Mavericks.

Using NSKeyedArchiver and NSKeyedUnarchiver it was easy to transfer the NSDictionary to an OS X app running on Mavericks and passing the dictionary to a CBPeripheralManager resulted in an iBeacon that could be detected by an iOS device.

Obviously it isn’t very convenient to create the adverisement data on iOS and then transfer the archived data to an OS X app so the next step was to investigate to see exactly what was being stored in the dictionary.

1
2
3
{
    kCBAdvDataAppleBeaconKey = <a7c4c5fa a8dd4ba1 b9a8a240 584f02d3 00040fa0 c5>;
}

The dictionary key is the NSString literal kCBAdvDataAppleBeaconKey. The value is an NSData object contaning the proximityUUID, major, minor and the calibrated measured power (in 2’s complement). It wasn’t very hard to take this information and create a class to generate the advertisement dictionary directly on OS X.

BLCBeaconAdvertisementData.hlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//
//  BLCBeaconAdvertisementData.h
//  BeaconOSX
//
//  Created by Matthew Robinson on 1/11/2013.
//  Copyright (c) 2013 Blended Cocoa. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface BLCBeaconAdvertisementData : NSObject

@property (strong,nonatomic) NSUUID *proximityUUID;
@property (assign,nonatomic) uint16_t major;
@property (assign,nonatomic) uint16_t minor;
@property (assign,nonatomic) int8_t measuredPower;

- (id)initWithProximityUUID:(NSUUID *)proximityUUID
                      major:(uint16_t)major
                      minor:(uint16_t)minor
              measuredPower:(int8_t)power;


- (NSDictionary *)beaconAdvertisement;

@end
BLCBeaconAdvertisementData.mlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//
//  BLCBeaconAdvertisementData.m
//  BeaconOSX
//
//  Created by Matthew Robinson on 1/11/2013.
//  Copyright (c) 2013 Blended Cocoa. All rights reserved.
//

#import "BLCBeaconAdvertisementData.h"

@implementation BLCBeaconAdvertisementData

- (id)initWithProximityUUID:(NSUUID *)proximityUUID major:(uint16_t)major minor:(uint16_t)minor measuredPower:(int8_t)power {
    self = [super init];

    if (self) {
        self.proximityUUID = proximityUUID;
        self.major = major;
        self.minor = minor;
        self.measuredPower = power;
    }

    return self;
}


- (NSDictionary *)beaconAdvertisement {
    NSString *beaconKey = @"kCBAdvDataAppleBeaconKey";

    unsigned char advertisementBytes[21] = {0};

    [self.proximityUUID getUUIDBytes:(unsigned char *)&advertisementBytes];

    advertisementBytes[16] = (unsigned char)(self.major >> 8);
    advertisementBytes[17] = (unsigned char)(self.major & 255);

    advertisementBytes[18] = (unsigned char)(self.minor >> 8);
    advertisementBytes[19] = (unsigned char)(self.minor & 255);

    advertisementBytes[20] = self.measuredPower;

    NSMutableData *advertisement = [NSMutableData dataWithBytes:advertisementBytes length:21];

    return [NSDictionary dictionaryWithObject:advertisement forKey:beaconKey];
}

@end

Mavericks as an iBeacon

Finally, we are now able to create an iBeacon on OS X using the following code which is similar to the iOS version but uses BLCBeaconAdvertisementData instead of a CLBeaconRegion to create the adverisement.

BLCAppDelegate.mlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//
//  BLCAppDelegate.m
//  BeaconOSX
//
//  Created by Matthew Robinson on 1/11/2013.
//  Copyright (c) 2013 Blended Cocoa. All rights reserved.
//

#import "BLCAppDelegate.h"

#import <IOBluetooth/IOBluetooth.h>

#import "BLCBeaconAdvertisementData.h"

@interface BLCAppDelegate () <CBPeripheralManagerDelegate>

@property (nonatomic,strong) CBPeripheralManager *manager;

@end

@implementation BLCAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

    _manager = [[CBPeripheralManager alloc] initWithDelegate:self
                                                       queue:nil];
}

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {

    if (peripheral.state == CBPeripheralManagerStatePoweredOn) {

        NSUUID *proximityUUID = [[NSUUID alloc] initWithUUIDString:@"A6C4C5FA-A8DD-4BA1-B9A8-A240584F02D3"];

        BLCBeaconAdvertisementData *beaconData = [[BLCBeaconAdvertisementData alloc] initWithProximityUUID:proximityUUID
                                                                                                     major:5
                                                                                                     minor:5000
                                                                                             measuredPower:-58];


        [peripheral startAdvertising:beaconData.beaconAdvertisement];
    }
}

@end

That is all that is needed to create an iBeacon using a Bluetooth 4 enabled Mac running Mavericks. Fortunately, the Core Bluetooth framework on Mavericks understands what to do with the iBeacon advertisement data even though iBeacon support hasn’t been added to Core Location.

Source code is available on GitHub at https://github.com/mttrb/BeaconOSX.

Comments