iOS7: What’s New in Bluetooth LE?

Dave Smith
Dave Smith
iOS7: What’s New in Bluetooth LE?

Core Bluetooth

The Core Bluetooth Framework, which was introduced in iOS 5, put Apple and iOS at the forefront of enabling mobile developers to integrate Smart Bluetooth LE devices with their applications. It also marked the first hardware integration framework that didn’t require iOS developers to become part of their separate MFi program for hardware integrators. Overall, Core Bluetooth was a great win for iOS and accessories, and, in iOS 7, it has been significantly enhanced yet again.

As a developer wanting to integrate Bluetooth LE into your iOS application, you will first be thrilled to learn that the Core Bluetooth Programming Guide finally exists as of early last month, providing much needed context for getting started with the existing framework reference documentation. However, you will next be saddened that simulator support for Core Bluetooth is being dropped in iOS 7. Testing Bluetooth LE applications will require access to a real iOS device going forward.

New performance and usability improvements come to Core Bluetooth in iOS 7. The peripheral data caching added in iOS 6, which reduces request latency and improves battery efficiency, is enhanced with even more data from each characteristic and service, including the last-known value of each characteristic. Core Bluetooth background modes have improved to include application snapshots when a background application is terminated. This is significant because many Bluetooth LE use cases require prolonged background access which became a problem when those applications needed to be terminated to relieve system memory pressure. These snapshots allow the application’s Bluetooth connection state to be restored later, rather than forcing those applications to start with a clean slate, enabling a seamless experience.

Additionally, the core system now implements the GATT Server role, allowing external accessories to connect and discover a new set of built-in services on each device. Some of these services, like the Battery Service and the Current Time Service, are Bluetooth LE standards. However, Apple also added a new custom service as well that is sure to please accessory manufacturers...

Apple Notification Center Service (ANCS)

ANCS is an Apple-specific GATT service that exposes the characteristics necessary for accessories to be alerted to any notification events on the iOS device (i.e. notifications arriving, being modified, or dismissed) and to get more information about those events. Accessory developers finally have the full access they desire to device events for the purposes of notifying the user on a secondary interface (think Pebble). The current version supports reading notification data only; there is no mechanism to dismiss or modify a notification remotely using ANCS. Further information is available in the Apple Notification Center Service (ANCS) specification.

Core Location

Another really interesting technology we have with iOS 7 is iBeacon. While technically part of the Core Location Framework, iBeacon uses Bluetooth LE technology under the hood. iBeacon is an Apple-specific Bluetooth profile designed to provide similar functionality to the existing LE Proximity Profile but in a package more friendly to location services. In essence, developers can use iBeacon to add precise location region monitoring to their application without spending much, if any, time with the Core Bluetooth APIs.

iBeacons are devices that work in concert with the region monitoring features already present within iOS. Applications set themselves up to listen for certain iBeacon signatures as a “region” referenced by a UUID, and when the iOS device comes within proximity of that iBeacon, the application is called back to alert that the user has entered that region.

iBeacons also support a ranging feature that allows an application to get a coarse measurement of the distance between the iBeacon and the device: Far, Near, or Immediate. The idea is that the application may want to change its state when the user’s device moves within range of the iBeacon (such as entering the store where the iBeacon is placed) versus moving right up to it (e.g. walking up to the counter).

How it Works

To turn an iOS7 device into an iBeacon, you must create an instance of CLBeaconRegion passing it a combination of the four following parameters:

  • UUID (Required)
  • Identifier (Required)
  • Major Value
  • Minor Value

The UUID parameter should be unique to your application; if your application uses multiple devices as iBeacons they should all share the same UUID. Providing an identifier is the first level of differentiation for multiple locations where the same application may have iBeacons placed. For example, if your application has iBeacons in each retail store location, they would each have their own identifier. For further division of iBeacons that may be in closer proximity, the major and minor value can be used to create groups of beacons and/or uniquely identify each one.

Once a CLBeaconRegion is initialized, we can use Core Bluetooth to start advertising this iBeacon data to nearby devices. Calling peripheralDataWithMeasuredPower: on the CLBeaconRegion will construct the advertisement data to pass on to a CBPeripheralManager via startAdvertising:.

The following code snippet shows the code to create and begin advertising a simple iBeacon:

#import <CoreBluetooth/CoreBluetooth.h>
#import <CoreLocation/CoreLocation.h>
@interface MyClass ()
    @property (nonatomic, retain) CLBeaconRegion *beaconRegion;
    @property (nonatomic, retain) CBPeripheralManager *manager;
@end

/* Initialization */
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"SOME-UUID-STRING-HERE"];
NSString *identifier = @"MyBeacon";
//Construct the region
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:identifier];
self.manager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

This class must implement the CBPeripheralManagerDelegate protocol. The above section can be placed anywhere you are initializing your class (be it a UIViewController or otherwise). Then the following CBPeripheralDelegate callback method should be implemented further down:

#pragma mark - CBPeripheralManagerDelegate Methods

//CBPeripheralManager callback once the manager is ready to accept commands
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
  if (peripheral.state != CBPeripheralManagerStatePoweredOn) {
    return;
  }

  //Passing nil will use the device default power
  NSDictionary *payload = [beaconRegion peripheralDataWithMeasuredPower:nil];

  //Start advertising
  [manager startAdvertising:payload];
}

Tip: You can use the uuidgen command-line tool on your Mac to create a new UUID string.

iBeacon Detection

On the client side, monitoring for iBeacons in range of your application simply requires creating another CLBeaconRegion that matches the filter data of the advertiser and passing it to CLLocationManager via startMonitoringForRegion: in the same way you would for a circular geofence location region. The following snippet illustrates this:

#import <CoreLocation/CoreLocation.h>

/* Initialization */
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"SAME-UUID-STRING-HERE"];
NSString *identifier = @"MyBeacon";
//Construct the region
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:identifier];
//Start monitoring
CLLocationManager *manager = [[CLLocationManager alloc] init];
[manager setDelegate:self];
[manager startMonitoringForRegion:beaconRegion];

This class must implement the CLLocationManagerDelegate protocol. The above section can be placed anywhere you are initializing your class (be it a UIViewController or otherwise). Then the following CLLocationManagerDelegate methods should be implemented further down:

#pragma mark - CLLocationManagerDelegate Methods

//Callback when the iBeacon is in range
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
  if ([region isKindOfClass:[CLBeaconRegion class]]) {
    [manager startRangingBeaconsInRegion:(CLBeaconRegion *)region];
  }
}

//Callback when the iBeacon has left range
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
  if ([region isKindOfClass:[CLBeaconRegion class]]) {
    [manager stopRangingBeaconsInRegion:(CLBeaconRegion *)region];
  }
}

//Callback when ranging is successful
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons
                                                            inRegion:(CLBeaconRegion *)region
{
  //Check if we have moved closer or farther away from the iBeacon…
  CLBeacon *beacon = [beacons objectAtIndex:0];

  switch (beacon.proximity) {
    case CLProximityImmediate:
      NSLog(@"You're Sitting on it!");
      break;
    case CLProximityNear:
      NSLog(@"Getting Warmer!");
      break;
    default:
      NSLog("It's around here somewhere!");
      break;
  }
}

So far, only other iOS 7 devices can officially be set up to act as an iBeacon (though many device manufacturers have already reverse-engineered the basic protocol), but Apple has announced intentions to officially release the protocol documentation soon. When that happens, we will see a wealth of other iBeacon options in the market for developers to play with.

UPDATE: Apple has now officially released the iBeacon protocol specification to developers under an auxiliary license of the MFi hardware program, allowing companies to manufacture officially licensed iBeacon hardware. Existing MFi developers will need to reapply for the secondary license to obtain the documentation. This also comes with the ability to use official Apple iBeacon branding on licensed products.

UPDATE: Multiple sources have reported that, as of iOS 7.1, applications scanning for beacons will now remain registered for ranging events even when the application has been terminated. This removes the need for developers to try and maintain background refresh modes in order to discover new iBeacons.