Guides and Sample Code

Developer

Energy Efficiency Guide for iOS Apps

On This Page

Bluetooth Best Practices

The Core Bluetooth framework provides classes for communicating with devices that support Bluetooth low energy wireless technology. When developing an app that interacts with a Bluetooth device, remember that Bluetooth shares the device’s radio with other forms of wireless communication, such as Wi-Fi, to transmit signals over the air. Also, interacting with a Bluetooth device doesn’t just use energy on the iOS device. It uses energy on the Bluetooth device too. If you make your iOS app energy efficient, the Bluetooth device will also benefit.

In general, minimize use of the radio whenever possible to reduce impact on other resources and the device’s battery. This can be done by buffering data instead of streaming it, and by batching transactions. The following additional guidelines will help you reduce unnecessary radio use.

Scan for Devices Only When Needed

When you call the scanForPeripheralsWithServices:options: method of the CBCentralManager class to discover remote peripherals that are advertising services, the device uses its radio to listen for advertising devices until explicitly told to stop. Unless you need to discover more devices, stop scanning for other devices once you have found one you want to connect to. Use the stopScan method of the CBCentralManager class to stop scanning for other devices. See Listing 17-1.

Listing 17-1Scanning for a Bluetooth device and then stopping

Objective-C

  1. -(void)beginScanningForDevice {
  2. // Create a Core Bluetooth Central Manager object
  3. self.myCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
  4. // Scan for peripherals
  5. [self.myCentralManager scanForPeripheralsWithServices:nil options:nil];
  6. }
  7. - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
  8. // Connect to the newly discovered device
  9. // Stop scanning for devices
  10. [self.myCentralManager stopScan];
  11. }

Swift

  1. func beginScanningForDevice() {
  2. // Create a Core Bluetooth Central Manager object
  3. self.myCentralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
  4. // Scan for peripherals
  5. self.myCentralManager.scanForPeripheralsWithServices(nil, options: nil)
  6. }
  7. func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [NSObject: AnyObject]!, RSSI: NSNumber!) {
  8. // Connect to the newly discovered device
  9. // Stop scanning for devices
  10. self.myCentralManager.stopScan()
  11. }

Minimize Processing of Duplicate Device Discoveries

Remote peripheral devices may send out multiple advertising packets per second to announce their presence to listening apps. By default, these packets are combined into a single event and delivered to your app once per peripheral. You should avoid changing this behavior—don’t specify the CBCentralManagerScanOptionAllowDuplicatesKey constant as a scan option when calling the scanForPeripheralsWithServices:options: method. Doing so results in excess events that can drain battery life.

Only Discover Services and Characteristics You Need

Peripheral devices provide services related to performing specific functions—such as a Bluetooth heart monitor that offers a service for retrieving heart rate information. Services include characteristics, or attributes. For example, the heart rate service may have characteristics that provide specific measurements or data, such as the position of the sensor when a reading was obtained.

A peripheral may have many more services and characteristics than are needed to fulfill a specific use case with your app. Therefore, look for and discover the specific services and characteristics your app needs. You can do this by providing specific UUIDs (represented by CBUUID objects) to the discoverServices: and discoverCharacteristics:forService: methods of the CBPeripheral class, as shown in Listing 17-2 and Listing 17-3.

Listing 17-2Discovering specific services

Objective-C

  1. // Look for services matching a specific set of UUIDs
  2. [peripheral discoverServices:@[firstServiceUUID, secondServiceUUID]];

Swift

  1. // Look for services matching a specific set of UUIDs
  2. peripheral.discoverServices([firstServiceUUID, secondServiceUUID])
Listing 17-3Discovering specific service characteristics

Objective-C

  1. // Look for characterstics matching a specific set of UUIDs for a given service
  2. [[peripheral discoverCharacteristics:@[firstCharacteristicUUID, secondCharacteristicUUID]
  3. forService:interestingService]];

Swift

  1. // Look for characterstics matching a specific set of UUIDs for a given service
  2. peripheral.discoverCharacteristics([firstCharacteristicUUID, secondCharacteristicUUID], forService: interestingService)

Request Notifications Rather than Polling for Characteristic Value Changes

In most cases, your app has no way of knowing when a service’s characteristic value will change on a connected device. You could repeatedly query the device (polling) to detect changes, but a more efficient way is to register to receive notifications when changes occur.

Subscribe to the value of a characteristic by passing a value of YEStrue to the setNotifyValue:forCharacteristic: method of the CBPeripheral class, as shown in Listing 17-4. Whenever the characteristic’s value changes, the peripheral calls the peripheral:didUpdateValueForCharacteristic:error: method of its delegate object.

Listing 17-4Subscribing and responding to characteristic value change notifications

Objective-C

  1. -(void)subscribeToCharacteristic {
  2. // Subscribe to a characteristic value
  3. [self.peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];
  4. }
  5. - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
  6. // Process the characteristic value update
  7. }

Swift

  1. func subscribeToCharacteristic() {
  2. // Subscribe to a characteristic value
  3. self.peripheral.setNotifyValue(true, forCharacteristic: interestingCharacteristic)
  4. }
  5. func peripheral(peripheral: CBPeripheral, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic, error: NSError! {
  6. // Process the characteristic value update
  7. }

Disconnect from a Device When You No Longer Need It

To prevent your app from needlessly using the device’s radio, disconnect from a peripheral device if a characteristic has stopped providing notifications or if additional data is no longer required. Cancel any notification subscriptions by passing a value of NOfalse to the setNotifyValue:forCharacteristic: method of the CBPeripheral class. Then disconnect from the device by calling the cancelPeripheralConnection: method of the CBCentralManager class. See Listing 17-5.

Listing 17-5Stopping characteristic value change notifications and disconnecting from a peripheral device

Objective-C

  1. // Unsubscribe from a characteristic value
  2. [self.peripheral setNotifyValue:NO forCharacteristic:interestingCharacteristic];
  3. // Disconnect from the device
  4. [self.myCentralManager cancelPeripheralConnection:peripheral];

Swift

  1. // Unsubscribe from a characteristic value
  2. self.peripheral.notifyValue(false, forCharacteristic: interestingCharacteristic)
  3. // Disconnect from the device
  4. self.myCentralManager.cancelPeripheralConnection(peripheral)