Core Bluetooth framework is an abstraction layer that provides developers access to BLE hardware. Apple introduced quite a few changes for the better during WWDC 2019. Besides fast transfer and power-efficient connections, much emphasis has been given to user privacy.
Until iOS 12, apps could use Bluetooth services in the background without user’s attention. This could be done for good reasons, such as connecting to Chromecast or wireless headsets.
But this had it’s own pitfalls and created a hole in the user’s privacy since developers could abuse the system and do stuff like location tracking.
Hence with iOS 13, if you’re application accesses any Core Bluetooth APIs it requires user authorization.
The permission dialogs would now be shown on apps build with older SDKs as well.
Privacy Permissions And Usage Descriptions
Starting iOS 13, it’s mandatory for developers to specify the Privacy Usage Description for Bluetooth by including NSBluetoothAlwaysUsageDescription
in their info.plist
file. Otherwise accessing any Core Bluetooth APIs would lead to a crash.
For backward support for older iOS versions, NSBluetoothPeripheralUsageDescription
needs to be defined.
API changes
CBManagerAuthorisation
Property has been added to check whether the user has authorized the CoreBluetooth Permission.
The authorization
property can have any of the following states:
notDetermined
restricted
denied
allowedAlways
In the next section, we’ll be discussing the various steps you need to follow in order to integrate CoreBluetooth in your application.
Implementation
import CoreBluetooth
For using the Core Bluetooth functionalities, we need to conform our ViewController class to CBPeripheralDelegate
and CBCentralManagerDelegate
protocols.
Instantiating the Bluetooth Manager
CBCentralManager is responsible for scanning and connecting to devices. Once the connection is done, CBPeripheral takes in charge of
var centralManager: CBCentralManager? var peripheral: CBPeripheral? override func viewDidLoad() { super.viewDidLoad() centralManager = CBCentralManager(delegate: self, queue: nil) }
Right when the CentralManager is initialised, the centralManagerDidUpdateState(_central: CBCentralManager)
delegate method is called to check the state of bluetooth. We can check the user authorization status from central.state.authorization
property.
func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .unauthorized: switch central.authorization { case .allowedAlways: case .denied: case .restricted: case .notDetermined: } case .unknown: case .unsupported: case .poweredOn: self.centralManager?.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey:true]) case .poweredOff: case .resetting: @unknown default: } }
Scanning of devices is only possible when the state changes to poweredOn
Note: Core Bluetooth scans for BLE devices only.
Connecting To Scanned Devices
Once a BLE device is discovered it shows up in the below method:
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { self.peripheral = peripheral self.peripheral?.delegate = self centralManager?.connect(peripheral, options: nil) centralManager?.stopScan() }
We can then access the Bluetooth Device name from the peripheral.name
property.
From here the CBPeripheralDelegate takes control. We can do various stuff such as passing data using the writeValue
function on the peripheral instance. The positive thing is that the user is aware!
iOS 13 strives to provide transparency and improve user experience through such features.
That’s it for this one. I hope you enjoyed.
1 reply on “Core Bluetooth Permissions in iOS 13”
I appreciate the clarity of the article! I do have a question. I received a notification that “central.authorization ” is depreciated and I’m not sure what they are replacing it with or if I should just ignore it. Thanks!