Core Bluetooth is an iOS Framework to build Bluetooth Low Energy (BLE) applications that communicate with hardware devices.
A Bluetooth device can be either a central or peripheral
CBCentralManager
). The object that scan, connect, disconnect from a Bluetooth device.CBPeripheral
). The Bluetooth device that publishes data to be consumed by other devices. Bluetooth peripherals broadcast some of the data they have in the form of advertising packets. The peripheral’s data is organized into services and characteristics.Here, we presume that the iOS device will be the central, receiving some data from some peripheral.
Everything to do with Bluetooth is event based. Let's go through main classes and protocols that we'll use in this tutorial
CBCentralManager
, CBCentralManagerDelegate
. CBCentralManager
is the first object you’ll need to instantiate to set up a Bluetooth connection. It handles monitoring the Bluetooth state of the device, scanning for Bluetooth peripherals and manage peripherals.CBPeripheral
, CBPeripheralDelegate
. Represents physical BLE devices which were discovered by CBCentralManager
. They are identified by UUID which contains one or more services.CBService
. There are list of service that BLE device has, also provide data associated behaviors and characteristics.CBCharacteristics
. Represent the data of the device’s service and contains a single value. Here we can read, write, and subscribe to the data from the device.Let's start with setuping XCode. After creating a project first you’ll need to configure certain permissions to allow your app to use Bluetooth. In Info.plist file we are going to add following keys
<key>NSBluetoothAlwaysUsageDescription</key> <string>We use bluetooth to connect to nearby bluetooth Devices</string> <key>NSBluetoothPeripheralUsageDescription</key> <string>Bluetooth is used to connect to user devices</string>
Now we can open a ViewController and import CoreBluetooth
class.
First, we have to initiate instance of CBCentralManager
and implement centralManagerDidUpdateState
delegates methods in order to get updates when the Bluetooth peripheral is switched on or off, and after that we can start scanning peripherals, by calling scanForPeripherals
method. You may pass in an array of CBUUIDs that represent specific services you want to filter for.
import UIKit import CoreBluetooth class BluetoothViewController: UIViewController { var centralManager: CBCentralManager! var peripheral: CBPeripheral? = nil var someCharacteristic: CBCharacteristic? = nil var scanningTimer = Timer() override func viewDidLoad() { super.viewDidLoad() // Start manager centralManager = CBCentralManager(delegate: self, queue: nil)//, options: [CBCentralManagerOptionShowPowerAlertKey: true]) } } extension BluetoothViewController: CBCentralManagerDelegate { // MARK: - Scanning func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .unauthorized: print("State is unauthorized") case .poweredOn: central.scanForPeripherals(withServices: nil, options: nil) print("Scanning...") scanningTimer = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(stopBluetoothScanning), userInfo: nil, repeats: false) default: print("\(central.state)") } } @objc func stopBluetoothScanning() { centralManager.stopScan() print("Stopped...") } func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { guard let peripheralName = peripheral.name else { return } print(peripheralName) if peripheralName == "iPhone 16 Ultra Pro" { print("Device found!") // Stop scan //centralManager.stopScan() // Connect //centralManager.connect(peripheral, options: nil) //self.peripheral = peripheral } } // MARK: - Connected func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { // Discover all service peripheral.discoverServices(nil) peripheral.delegate = self } } extension BluetoothViewController : CBPeripheralDelegate { // MARK: - Discover services func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { if let services = peripheral.services { for service in services { peripheral.discoverCharacteristics(nil, for: service) } } } // MARK: - Discover characteristics for the service func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { guard let characteristics = service.characteristics else { return } let characteristicId = CBUUID(string: "0x1234") for characteristic in characteristics { if characteristic.uuid == characteristicId { self.someCharacteristic = characteristic print("Found characteristic - \(characteristic)") } } } }
Second, we need another delegate method didDiscover
to receive scan results. After it we can connect and store a copy of BLE-device. To obtain data from a peripheral you’ll need to connect to it.
Third, once you’ve obtained a reference to the desired CBPeripheral
object, you may attempt to connect to it by simply calling the connect
method on your central manager and passing in the peripheral. On a successful connection, you’ll receive a didConnect
delegate call, or on connection failure.
Fourth, we call discoverServices
and implement CBPeripheralDelegate
methods to discover its services and then its characteristics. In discoverServices
if you pass nil
it’s going to discover all services but if you know specific service you can discover only that.
Fifth, we need didDiscoverServices
delegate method to discover characteristics by calling discoverCharacteristics
. If you pass nil
it's going to discover all characteristics.
Sixth, we need to implement didDiscoverCharacteristicsFor
method coming from the CBPeripheral
delegate for getting characteristics.
That's all for beginning.
Here is some useful snippets. We can write a value to the CBPeripheral
let data = Data(bytes: [bytes]) peripheral?.writeValue(data, for: someCharacteristic, type: .withResponse)
Read a value from the CBPeripheral
peripheral?.readValue(for: someCharacteristic)
To disconnect or cancel an active local connection from a peripheral, use the cancelPeripheralConnection
method.
if let peripheral { centralManager.cancelPeripheralConnection(blePeripheral) }