I'm able to connect to a BLE device, but .discoverServices() doesn't work. My logs show an unhelpfully generic error: [CoreBluetooth] XPC connection invalid
Connecting to it does work:
func connectToPeripheral(id: Int) -> Bool {
guard id >= 0 && id <= peripherals.count else {
return false
}
let arrayItem = peripherals[id]
connectedPeripheral = arrayItem.peripheral
connectedPeripheral!.delegate = self
myCentral.connect(connectedPeripheral!, options: nil)
print("connected to \(arrayItem.name)")
myCentral.stopScan()
return true
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected to \(String(describing: peripheral.name)) \(peripheral.description)!")
print(peripheral.services as Any)
peripheral.discoverServices(nil)
}
But this function never gets called. Why????
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("Services:")
print(peripheral.services as Any)
}
Not sure what I'm doing wrong, help?
Here's the full code:
import Foundation
import CoreBluetooth
class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
var myCentral: CBCentralManager!
@Published var isSwitchedOn = false
@Published var peripherals = [Peripheral]()
@Published var connectedPeripheral: CBPeripheral?
override init() {
super.init()
myCentral = CBCentralManager(delegate: self, queue: nil)
myCentral.delegate = self
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
var peripheralName: String!
if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
peripheralName = name
} else {
peripheralName = "Unknown"
}
if peripherals.contains(where: { p in p.name == peripheralName }) {
return
}
let newPeripheral = Peripheral(id: peripherals.count, name: peripheralName, rssi: RSSI.intValue, peripheral: peripheral)
peripherals.append(newPeripheral)
}
func startScanning() {
print("startScanning")
peripherals.removeAll()
myCentral.scanForPeripherals(withServices: nil, options: nil)
}
func stopScanning() {
print("stopScanning")
myCentral.stopScan()
}
func connectToPeripheral(id: Int) -> Bool {
guard id >= 0 && id <= peripherals.count else {
return false
}
let arrayItem = peripherals[id]
connectedPeripheral = arrayItem.peripheral
connectedPeripheral!.delegate = self
myCentral.connect(connectedPeripheral!, options: nil)
print("connected to \(arrayItem.name)")
myCentral.stopScan()
return true
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected to \(String(describing: peripheral.name)) \(peripheral.description)!")
print(peripheral.services as Any)
peripheral.discoverServices(nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("Services:")
print(peripheral.services as Any)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
print("got Characteristics...")
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print("centralManagerDidUpdateState: poweredOn=\(central.state == .poweredOn)")
if central.state == .poweredOn {
//if it wasn't previously on, start scanning
if !isSwitchedOn {
isSwitchedOn = true
startScanning()
}
} else {
isSwitchedOn = false
}
}
}
struct Peripheral: Identifiable {
let id: Int
let name: String
let rssi: Int
let peripheral: CBPeripheral
}
I figured out two bugs in my code.
startScanning() when centralManagerDidUpdateState() was called with central.state == .poweredOn. This causing scanning to start again right after calling myCentral.connect(connectedPeripheral!, options: nil)myCentral.stopScan() after myCentral.connect(connectedPeripheral!, options: nil) was causing disconnectionsI don't understand why either of those were happening, but fixing them allowed me to now connect and get services and characteristics!