Hi there, I'm having an issue hoping someone could help. We have an iOS app that uses CoreBluetooth to connect to peripherals using the central manager. The app works great - However, when using the same exact central manager for our watchos app, it will attempt to connect, but I never get a callback for either didConnect or didFailToConnect.
The watch can connect successfully to other BLE devices, so the watch itself is capable of BLE connectivity.
Here's a list of thing's I've tried (unsuccessfully):
1) Added every bluetooth-related entitlement to info.plist
Privacy - Bluetooth Always Usage Description
Privacy - Bluetooth Peripheral Usage Description
Background Modes: App communicates using CoreBluetooth, App shares data using CoreBluetooth
2) Checked for Single-Connection Limits
Verified that the iPhone was fully disconnected from the peripheral to ensure the device wasn’t limited to one connection.
Attempted to connect on watchOS alone (with iPhone turned off)
3) Tried various options for CBCentralManager, scanForPeripherals, and connect
I went through all the keys for various options and tried just setting them, they had no effect
CBCentralManagerOptionShowPowerAlertKey, CBConnectPeripheralOptionEnableTransportBridgingKey
Item 2
4) Tried .registerForConnectionEvents()
5) Set peripheral's delegate to the central in the didDiscover, stored it in a variable to ensure a strong reference to it
I get no warnings either. The last time I ran into something like this, I found out the watchOS blocks TCP sockets. If I print out the CBPeripheralState a few seconds after trying to connect, it shows its stuck on CBPeripheralStateConnecting.
Any advice or direction is greatly appreciated
Below is the code and various print outs (day 2 into debugging, so it's not pretty)
class WatchBLEManager:NSObject,CBCentralManagerDelegate, ObservableObject{
var centralManager: CBCentralManager?
@Published var devices : [String:AtomBLEDevice] = [:]
private var scanningDevice:AtomBLEDevice?
var bleStatus:WatchBLEStatus = .blePoweredOff
func startBLE() {
centralManager = CBCentralManager(delegate: self, queue: nil,options: [CBCentralManagerOptionShowPowerAlertKey: true])
self.centralManager?.delegate = self
}
func startScan() {
self.centralManager?.scanForPeripherals(withServices: [],options: [CBCentralManagerScanOptionAllowDuplicatesKey : true])
self.centralManager?.delegate = self
}
func stopScan() {
print("stopping scan")
self.centralManager?.stopScan()
filterName = ""
scanningDevice = nil
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch (central.state) {
//... other states omitted
case .poweredOff:
bleStatus = .blePoweredOff
// bleStateDelegate?.didBlePoweredOff()
for device in devices.values{
device.isConnected = false
}
print("BLE is Powered Off")
case .poweredOn:
bleStatus = .blePoweredOn
// bleStateDelegate?.didBlePoweredOn()
startScan()
centralManager?.registerForConnectionEvents()
print("Central supports extended scan and connect: ", CBCentralManager.supports(.extendedScanAndConnect))
print("powered on")
@unknown default:
print("BLE is Unknown")
}
}
private let connectionQueue = DispatchQueue(label: "com.atom.connectionQueue")
var connectingTo: String? = nil
var peripheral: CBPeripheral? = nil
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard let localName = advertisementData[CBAdvertisementDataLocalNameKey] as? String else { return}
if localName.contains("Atom") {
print("\nConnecting to \(localName)")
print("\tAdvertising data: \(advertisementData)")
print("\tANCS Authorized: ",peripheral.ancsAuthorized)
print("\tServices", peripheral.services, "\n")
self.peripheral = peripheral
self.peripheral?.delegate = self
// central.registerForConnectionEvents()
// central.delegate = self
peripheral.delegate = self
DispatchQueue.main.async {
// central.connect(peripheral)
self.centralManager?.connect(peripheral, options: [ CBConnectPeripheralOptionEnableTransportBridgingKey: true])
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
print("\tState", String(describing: peripheral.state))
print("Connected Peripherals: \(self.centralManager?.retrieveConnectedPeripherals(withServices: []))")
}
}
}
// Never gets called for watchos
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected to peripheral: \(peripheral.identifier)")
if let atomDevice = getAtomBLEDevice(peripheral: peripheral) {
//atomDevice.setPeripheral(perpipheral: <#T##CBPeripheral?#>)
atomDevice.isConnected = true
atomDevice.isConnecting = false
//delegate?.didConnected(atomBLE: atomDevice!)
atomDevice.startDiscoveringService()
//atomDevice?.delegate?.didConnected(atomBLE: atomDevice!)
print("Connected: \(peripheral.name)")
} else {
print("no matching atom device found for didConnect")
print("connected peripheral :",peripheral.identifier.uuidString)
}
}
func centralManager(_ central: CBCentralManager, connectionEventDidOccur event: CBConnectionEvent, for peripheral: CBPeripheral) {
print("Connection event: \(event)")
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: (any Error)?) {
print("Failed to connect: \(error?.localizedDescription)")
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
let atomDevice = getAtomBLEDevice(peripheral: peripheral)
atomDevice?.isConnected = false
print("Peripheral disconnected:\(peripheral.name)")
}
func clearData() {
filterName = ""
for device in devices.values{
disconnect(atomBLEDevice: device)
device.perpipheral?.delegate = nil
}
devices = [:]
scanningDevice = nil
// delegate = nil
centralManager = nil
}
}
extension WatchBLEManager: CBPeripheralDelegate {
}```