Closed jason-gabriele closed 2 years ago
I can simply refresh my cached objects and issue read/writes without needing to call discoverServices on each connection
How do you obtain the new services? Are they available using from CBPeripheral
?
Could you paste some code snippet?
I had run an experiment where I saw the peripheral.services coming from onConnect was prepopulated, but now using the same code, I cannot get it consistently. I tried looking at packet logger and it seems that most discoverServices() calls are using the system cache anyway (though I cannot get rid of the 2803 device info check). So, I guess I don't need any changes.
Thanks
My App chokes on this issue, since I am not getting all data.
Here is the class which accesses the characteristics:
class DeviceService {
var device: Device
var service: CBService
init(device: Device, service: CBService) {
self.device = device
self.service = service
}
var characteristics: [CBUUID: CBCharacteristic] {
get {
var characteristics = [CBUUID: CBCharacteristic]()
if let services = service.characteristics {
for characteristic in services as [CBCharacteristic] {
characteristics[characteristic.uuid] = characteristic
}
}
return characteristics
}
}
}
I am getting back:
(lldb) print myDeviceServices?.services?[UUIDS.deviceInfoServiceUUID]?.characteristics
([xxx.CBUUID : xxx.CBCharacteristic]?) $R11 = 0 key/value pairs
Running on the native stack the result is quite different:
(lldb) print myDeviceServices?.services?[UUIDS.deviceInfoServiceUUID]?.characteristics
([CBUUID : CBCharacteristic]?) $R0 = 5 key/value pairs {
[0] = {
key = 0x000000028022aa80 {
baseNSObject@0 = {
isa = CBUUID
}
_bytes = ""
_type = '\0'
}
value = 0x00000002826447e0 {
baseCBAttribute@0 = {
baseNSObject@0 = {
isa = CBCharacteristic
}
_UUID = 0x000000028022aa80
}
_isBroadcasted = false
_isNotifying = false
_service = 0x00000002817f04c0
_properties = 2
_value = 0x0000000000000000
_descriptors = 0x0000000000000000
_valueTimestamp = 0
_peripheral = 0x0000000283355180
_handle = 0xbdb2ab539a9ef274 Int64(19)
_valueHandle = 0xbdb2ab539a9ef1f4 Int64(20)
}
}
[1] = {
key = 0x000000028022aa00 {
baseNSObject@0 = {
isa = CBUUID
}
_bytes = ""
_type = '\0'
}
value = 0x0000000282644ba0 {
baseCBAttribute@0 = {
baseNSObject@0 = {
isa = CBCharacteristic
}
_UUID = 0x000000028022aa00
}
_isBroadcasted = false
_isNotifying = false
_service = 0x00000002817f04c0
_properties = 2
_value = 0x0000000000000000
_descriptors = 0x0000000000000000
_valueTimestamp = 0
_peripheral = 0x0000000283355180
_handle = 0xbdb2ab539a9ef374 Int64(17)
_valueHandle = 0xbdb2ab539a9ef2f4 Int64(18)
}
}
[2] = {
key = 0x000000028022a680 {
baseNSObject@0 = {
isa = CBUUID
}
_bytes = ""
_type = '\0'
}
value = 0x0000000282644360 {
baseCBAttribute@0 = {
baseNSObject@0 = {
isa = CBCharacteristic
}
_UUID = 0x000000028022a680
}
_isBroadcasted = false
_isNotifying = false
_service = 0x00000002817f04c0
_properties = 2
_value = 0x0000000000000000
_descriptors = 0x0000000000000000
_valueTimestamp = 0
_peripheral = 0x0000000283355180
_handle = 0xbdb2ab539a9ef074 Int64(23)
_valueHandle = 0xbdb2ab539a9ef7f4 Int64(24)
}
}
[3] = {
key = 0x000000028022aa20 {
baseNSObject@0 = {
isa = CBUUID
}
_bytes = ""
_type = '\0'
}
value = 0x0000000282644300 {
baseCBAttribute@0 = {
baseNSObject@0 = {
isa = CBCharacteristic
}
_UUID = 0x000000028022aa20
}
_isBroadcasted = false
_isNotifying = false
_service = 0x00000002817f04c0
_properties = 2
_value = 0x0000000000000000
_descriptors = 0x0000000000000000
_valueTimestamp = 0
_peripheral = 0x0000000283355180
_handle = 0xbdb2ab539a9ef174 Int64(21)
_valueHandle = 0xbdb2ab539a9ef0f4 Int64(22)
}
}
[4] = {
key = 0x000000028022a400 {
baseNSObject@0 = {
isa = CBUUID
}
_bytes = ""
_type = '\0'
}
value = 0x0000000282644720 {
baseCBAttribute@0 = {
baseNSObject@0 = {
isa = CBCharacteristic
}
_UUID = 0x000000028022a400
}
_isBroadcasted = false
_isNotifying = false
_service = 0x00000002817f04c0
_properties = 2
_value = 0x0000000000000000
_descriptors = 0x0000000000000000
_valueTimestamp = 0
_peripheral = 0x0000000283355180
_handle = 0xbdb2ab539a9ef774 Int64(25)
_valueHandle = 0xbdb2ab539a9ef6f4 Int64(26)
}
}
}
This access method is already some 5 years in service and proven, I don't want to change the service discovery.
OK, tracked down the issue:
the native peripheral didDiscoverServices
callback delivers under peripheral.services
all services including all characteristics on the very first discovery call.
My code was then walking through all services and called for each service .discoverCharacteristics
:
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
characteristics = 0
logger.debug("discovered \(peripheral.services!.count) services")
for service in peripheral.services! {
let thisService = service as CBService
serviceList[service.uuid] = DeviceService(device: self, service: thisService)
peripheral.discoverCharacteristics([], for: thisService)
characteristics += 1
logger.debug("discover characteristics for #\(characteristics), \(service.uuid)")
}
}
Then it collects each service:
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
logger.debug("discovered charactersitic #\(characteristics), \(service.uuid) -> \(service.characteristics.map {$0.description + "/"} ) ")
// --> saving service on my list was missing
serviceList[service.uuid] = DeviceService(device: self, service: service)
// <--
characteristics -= 1
if serviceList[service.uuid] != nil && characteristics == 0 {
isPopulated = true
logger.debug("discovering finished... calling back")
serviceCallbacks.call(self.serviceList)
}
}
My original code was missing to save this in the device serviceList
, so it got discarded before. This code portion was untouched the last years, so I am sure that the native stack delivers the characteristics and manages them under the hood. A "smarter copy" inside mock code would not help in such case. One solution would be to call .discoverCharacteristics
automatically when .discoverServices
is called and then return a combined list much like what the native stack delivers.
Hello.
I'm trying to implement a smarter way of using iOS's service cache so I don't need to call discoverServices on every connection. I've noticed that iOS seems to recreate the CBService and CBCharacteristic objects on each connection, even if the underlying details haven't changed. If I use the native CoreBluetooth objects, I can simply refresh my cached objects and issue read/writes without needing to call discoverServices on each connection. However, CoreBluetoothMock doesn't update the objects unless you specifically call discoverServices. Would it be possible to call smartCopy on connect to update the object references?
Thanks
Here's an example of my testing just using the native CoreBluetooth:
You can see the pointer is changing on each connect