It looks like CoreBluetooth couples GAP and GATT roles, despite many combinations being valid. So even though your iOS app will advertise itself as a BLE peripheral, you can use CBCentralManager to connect back to the central device. Instead of scanning like you would do in a central app, you can use retrieveConnectedPeripherals(withServices:) or retrievePeripherals(withIdentifiers:) to find the central device.
Unfortunately there isn't a didConnect
method in CBPeripheralManagerDelegate. If you're going to use retrievePeripherals(withIdentifiers:)
you'll need to add a dummy service and characteristic and have your central device access that characteristic after connecting. When you receive a didReceiveRead
or didSubscribeTo
event, you can connect back to request.central.identifier
. Using retrieveConnectedPeripherals(withServices:)
is simpler, but it didn't consistently return the central device in my testing.
retrieveConnectedPeripherals(withServices:) example
import CoreBluetooth
// A GATT client for an iOS App that operates as a BLE Peripheral. CoreBluetooth requires using a CBCentralManager
// to use remote GATT services, but we don't have to actually scan for the remote device.
class GattClient: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
// Replace with your service UUIDs
let remoteServiceUUIDs = [CBUUID(string: "FACE")]
var centralManager: CBCentralManager!
var remoteDevice: CBPeripheral? = nil
var timer: Timer? = nil
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if (central.state == .poweredOn) {
// There's no `didConnect` event we can listen for, so poll for connected devices.
timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true, block: findConnectedDevice)
}
}
func findConnectedDevice(timer: Timer) {
// Don't scan, just connect to the device already connected to us offering the right services.
print("Searching for connected devices...")
let connectedPeripherals = centralManager.retrieveConnectedPeripherals(withServices: remoteServiceUUIDs)
print("Devices found: \(connectedPeripherals.count)")
if (connectedPeripherals.count == 1) {
remoteDevice = connectedPeripherals[0]
print("Connecting...")
centralManager.connect(remoteDevice!, options: nil)
timer.invalidate()
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Discovering services...")
peripheral.delegate = self
peripheral.discoverServices(remoteServiceUUIDs)
}
...
}