Skip to content

Instantly share code, notes, and snippets.

@jhays
Created June 4, 2020 15:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jhays/632dc56fc4684d88406a382bda2a8860 to your computer and use it in GitHub Desktop.
Save jhays/632dc56fc4684d88406a382bda2a8860 to your computer and use it in GitHub Desktop.
A comparison of reading from a BLE peripheral using the CoreBluetooth standard delegation pattern, and RxBluetoothKit's reactive pattern. Notice the difference in total lines of code to accomplish a single read...
//
// BluetoothManager.swift
// BluetoothExample
//
// Created by Julian Hays on 5/26/20.
// Copyright © 2020 PunchThrough. All rights reserved.
//
import Foundation
import CoreBluetooth
import os.log
@objc class BluetoothManager: NSObject {
public static let shared = BluetoothManager()
static let ble_log = OSLog(subsystem: "com.punchthrough.BluetoothExample.BluetoothManager", category: "BLE")
private var centralManager: CBCentralManager? = nil
private var connectingPeripheral: CBPeripheral?
private var service: CBService? = nil
private var characteristic: CBCharacteristic? = nil
private let serviceUUID = CBUUID(string: "3296991d-cc2b-4b8b-a45d-a87ae695d2f6")
private let characteristicUUID = CBUUID(string: "4021c51f-fad4-4e54-84cf-cfaf46fc93c5")
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
// For this example, we are assuming the bluetooth state is .poweredOn
startScan()
}
func startScan() {
os_log(.info, log: BluetoothManager.ble_log, "start scan")
centralManager?.scanForPeripherals(withServices: [serviceUUID], options: nil)
}
}
// MARK: - CBCentralManagerDelegate
extension BluetoothManager: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
let bluetoothState = "\(central.state)"
os_log(.info, log: BluetoothManager.ble_log, "bluetoothState: %@", bluetoothState)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
os_log(.info, log: BluetoothManager.ble_log, "peripheral: %@ \nadvertisementData: %@ \nrssi: %@", peripheral, advertisementData, RSSI)
central.stopScan()
connectingPeripheral = peripheral
central.connect(peripheral, options: nil)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
os_log(.info, log: BluetoothManager.ble_log, "didConnectPeripheral: %@", peripheral)
peripheral.delegate = self
peripheral.discoverServices([serviceUUID])
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
os_log(.error, log: BluetoothManager.ble_log, "didFailToConnectPeripheral: %@ \nerror: %@", peripheral, error?.localizedDescription ?? "")
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
os_log(.info, log: BluetoothManager.ble_log, "didDisconnectPeripheral: %@ \nerror: %@", peripheral, error?.localizedDescription ?? "")
connectingPeripheral = nil
}
}
// MARK: - CBPeripheralDelegate
extension BluetoothManager: CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
os_log(.info, log: BluetoothManager.ble_log, "didDiscoverServices: %@ \nerror: %@", peripheral, error?.localizedDescription ?? "")
guard let discoveredService = peripheral.services?.first(where: { (discovered) -> Bool in
return discovered.uuid.uuidString == serviceUUID.uuidString
}) else {
os_log(.error, log: BluetoothManager.ble_log, "service not found on peripheral.")
return
}
service = discoveredService
peripheral.discoverCharacteristics([characteristicUUID], for: discoveredService)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
os_log(.info, log: BluetoothManager.ble_log, "didDiscoverCharacteristicsFor: %@ \nerror: %@", service, error?.localizedDescription ?? "")
guard let discoveredCharacteristic = service.characteristics?.first(where: { (discovered) -> Bool in
return discovered.uuid.uuidString == characteristicUUID.uuidString
}) else {
os_log(.error, log: BluetoothManager.ble_log, "characteristic not found on service.")
return
}
characteristic = discoveredCharacteristic
peripheral.readValue(for: discoveredCharacteristic)
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
os_log(.info, log: BluetoothManager.ble_log, "didUpdateValueFor characteristic")
if let value = characteristic.value {
var number: UInt8 = 0
value.copyBytes(to:&number, count: MemoryLayout<UInt8>.size)
os_log(.info, log: BluetoothManager.ble_log, "Value read: %d", number)
} else {
os_log(.error, log: BluetoothManager.ble_log, "Error reading value")
}
}
}
//
// RxBluetoothManager.swift
// BluetoothExample
//
// Created by Julian Hays on 5/27/20.
// Copyright © 2020 PunchThrough. All rights reserved.
//
import Foundation
import CoreBluetooth
import RxBluetoothKit
import RxSwift
import os.log
class RxBluetoothManager {
public static let shared = RxBluetoothManager()
static let rxble_log = OSLog(subsystem: "com.punchthrough.BluetoothExample.RxBluetoothManager", category: "RxBLE")
private let centralManager = CentralManager(queue: .main)
private let serviceUUID = CBUUID(string: "3296991d-cc2b-4b8b-a45d-a87ae695d2f6")
private let characteristicUUID = CBUUID(string: "4021c51f-fad4-4e54-84cf-cfaf46fc93c5")
private let disposeBag = DisposeBag()
func scanAndConnect() {
// For this example, we are assuming the bluetooth state is .poweredOn
centralManager.scanForPeripherals(withServices: [serviceUUID])
.take(1)
.flatMap { $0.peripheral.establishConnection() }
.flatMap { $0.discoverServices([self.serviceUUID]) }
.flatMap { Observable.from($0) }
.flatMap { $0.discoverCharacteristics([self.characteristicUUID]) }
.flatMap { Observable.from($0) }
.flatMap { $0.readValue() }
.subscribe(onNext: {
if let value = $0.value {
var number: UInt8 = 0
value.copyBytes(to:&number, count: MemoryLayout<UInt8>.size)
os_log(.info, log: RxBluetoothManager.rxble_log, "Value read: %d", number)
} else {
os_log(.error, log: RxBluetoothManager.rxble_log, "Error reading value")
}
})
.disposed(by: disposeBag)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment