Introduction
I had the chance to work on a project where communication via BLE was at the heart of the project.
Before adding any code to application, I always asked myself about two scenarios:
- The first scenario is when the device acts as a central device while searching for and connecting to peripheral devices.
- The second scenario is when the device acts as a peripheral device by using
CBCharacteristic
and changes its value.
In this article, I will focus on the first scenario and will show how to scan for peripheral devices.
First Step
The first step is to add permission for Bluetooth: Privacy - Bluetooth Always Usage Description
Second Step
The second step is to initialize centralManager
:
private var centralManager: CBCentralManager!
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey: true])
}
CBCentralManager
- CBCentralManager objects manage discovered or connected remote peripheral devices (represented by CBPeripheral objects), including scanning for, discovering, and connecting to advertising peripherals.
CBCentralManagerDelegate
- The single requiredcentralManagerDidUpdateState
method indicates the availability of the central manager, while the optional methods allow for the discovery and connection of peripherals.
CBCentralManagerOptionShowPowerAlertKey
- An NSNumber (Boolean) indicating that the system should, if Bluetooth is powered off whenCBCentralManager
is instantiated, display a warning dialog to the user.
Third Step
The third step is to scanForPeripherals
after centralManager
changes its state to poweredOn
.
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
print("CBManager is powered on")
centralManager.scanForPeripherals(withServices: nil)
case .poweredOff:
print("CBManager is not powered on")
return
case .resetting:
print("CBManager is resetting")
return
case .unauthorized:
print("CBManager is unauthorized")
return
case .unknown:
print("CBManager state is unknown")
return
case .unsupported:
print("Bluetooth is not supported on this device")
return
@unknown default:
print("A previously unknown central manager state occurred")
return
}
}
Fourth Step
The fourth step is to implement the didDiscover
method that is called when the central manager discovers a peripheral.
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
// Reject if the signal strength is too low.
// Change the minimum RSSI value depending on your app’s use case.
guard RSSI.intValue >= -50 else {
print("Discovered peripheral not in expected range, at \(RSSI.intValue)")
return
}
print("Discovered \(String(describing: peripheral.name)) at \(RSSI.intValue)")
}
The sample implementation of this method uses the RSSI (Received Signal Strength Indicator) parameter to determine whether the signal is strong enough. RSSI values are provided as negative numbers, with a theoretical maximum of 0.
Complete Example
import Foundation
import CoreBluetooth
final class CentralManager: NSObject {
private var centralManager: CBCentralManager!
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey: true])
}
}
// MARK: - CBCentralManagerDelegate
extension CentralManager: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
print("CBManager is powered on")
centralManager.scanForPeripherals(withServices: nil)
case .poweredOff:
print("CBManager is not powered on")
return
case .resetting:
print("CBManager is resetting")
return
case .unauthorized:
print("CBManager is unauthorized")
return
case .unknown:
print("CBManager state is unknown")
return
case .unsupported:
print("Bluetooth is not supported on this device")
return
@unknown default:
print("A previously unknown central manager state occurred")
return
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
// Reject if the signal strength is too low.
// Change the minimum RSSI value depending on your app’s use case.
guard RSSI.intValue >= -50
else {
print("Discovered perhiperal not in expected range, at %d", RSSI.intValue)
print("Discovered %s at %d", String(describing: peripheral.name), RSSI.intValue)
return
}
print("Discovered %s at %d", String(describing: peripheral.name), RSSI.intValue)
}
}