In this entry, we will walk through the code that we needed to add to the SDK/Central Mode to discover and register devices.
WALKTHROUGH
The Starting Repo Tag
The Ending Repo Tag
The Comparison Between the Two Tags
The ITCB_SDK_Central_internal.swift
File
- I started the “discovery chain reaction” going. This entailed:
- Instantiating a new instance of
ITCB_SDK_Device_Peripheral
, and adding it to our Array of Peripherals in the Central SDK.
This will keep a strong reference to the discovered Peripheral (See note below). - Calling the
CBCentralManager.connect(_:, options:)
method.
This initiates the connection to the discovered Peripheral (remember that we already discovered the Peripheral in the last walkthrough). - Adding the
CBCentralManagerDelegate.centralManager(_:, didConnect:)
method to the Central SDK.
This is the first callback in our “chain of discovery.” It is called after Core Bluetooth has successfully initiated a connection with the discovered Peripheral. - Registering the
ITCB_SDK_Device_Peripheral
class as aCBPeripheralDelegate
.
This is so that we can get the Service and Characteristic discovery events. - Created a new
ITCB_SDK_Device_PeripheralDelegate
protocol.
This is how we can “backtalk” to our Central SDK instance.
This protocol just has one method: theperipheralServicesUpdated(_:)
method. This tells the SDK that it needs to reexamine the Peripheral, because the Peripheral Service changed.
Even though, internally, I am not registering as observers, I think that keeping this kind of stuff very clear and unambiguous is important. It’s very easy to get terribly difficult bugs if we screw this up. - Added the
CBPeripheralDelegate.peripheral(_:, didDiscoverServices:)
method to theITCB_SDK_Device_Peripheral
class.
This is called when the Service is discovered, and is the second link in our discovery chain. At this point, we ask all Services (there should only be one), toCBPeripheral.discoverCharacteristics(_:, for:)
. This will start the last link in the chain. - Added the
CBPeripheralDelegate.peripheral(_:, didDiscoverCharacteristicsFor:, error:)
method to theITCB_SDK_Device_Peripheral
class.
This the final callback. It is made after the Service has discovered the three Characteristics that the Peripheral is publishing.
In this callback, we “walk back up the chain,” until we send the observerdeviceDiscovered(:)
message. - We now fetch the device name and UUID from the actual
CBPeripheral
instance.
This will allow us to use whatever data the other end sends us, and it uses the persistent UUID that every Bluetooth device generates.
- Instantiating a new instance of
NOTE: We need to make sure that we keep a strong reference to instances of
CBPeripheral
that are returned in the discovery callback. This does not need to be done for instances ofCBService
orCBCharacteristic
, as they are aggregated byCBPeripheral
andCBService
, respectively.If we do not keep a strong reference, then the
CBPeripheral
instance will be deallocated when the context leaves the callback.This is accomplished when we create an instance of
ITCB_SDK_Device_Peripheral
, and add it to thedevices
array in the SDK instance.
The ITCB_SDK_Peripheral_internal.swift
File
- Added a signal strength check to the Peripheral Discovery callback.
We should be checking signal strength. I had left it out of the initial implementation to keep things simpler. - Extend the native Swift
Array
type to look for instances that have already been created.
This searches our aggregated instances for aCBPeripheral
, and returnstrue
, if it is found. This allows us to avoid repeatedly adding Peripherals.
This is a classic “coolness” of Swift. We can create custom extensions that can be specialized to the needs of the moment.
extension Array where Element == ITCB_Device_Peripheral_Protocol { func contains(_ inCBPeripheral: CBPeripheral) -> Bool { let peripheralStringID = inCBPeripheral.identifier.uuidString guard let myself = self as? [ITCB_SDK_Device_Peripheral], !peripheralStringID.isEmpty else { return false } return myself.reduce(false) { (current, nextItem) in return current || nextItem.uuid == peripheralStringID } } }
WHERE WE ARE NOW
We now actually have the Central discovering Peripherals in the real world (not just in the mock).
If we run the app on the Mac, in Central Mode, we will get this:
Followed by this screen (once we start another device in Peripheral Mode):
It should be noted that it could take up to 30 seconds or so to see the Peripheral (or it could happen instantly).
Clicking on the device now gives us this screen:
Unfortunately, you can ask a question, and it won’t do anything. We need to establish the Peripheral capabilities to handle questions, and the Central’s capabilities to receive the answer.