CoreBluetooth: How to design code for many charact

2019-03-25 12:10发布

问题:

I searched around a bit and just found this as a possible duplicate question:

Multiple CBPeripheral's for same device

My problem is:

I have multiple services which all together have about 30-40 characteristics (Yes, I need all of them...). As starting point for dealing with CoreBluetooth I always used the Apple Sample Code (CoreBluetooth Temperature Sensor).

Discovery and Service/Characteristic handling is divided into two classes and this works fine for just a few characteristics. But handling this huge amount of characteristics in one class is not what I understand under "good software-design".

The first idea that come into ones mind is to create one class for every service. But unfortunately a CBPeripheral just can have one CBPeripheralDelegate at the same time. This means I can't divide it up into several classes.

(We don't have to start the discussion if BLE is the right technology for getting this amount of data - it isn't. But there are manufacturers that use BLE so they don't have to hassle with the MFi program...)

I also read the finally provided CoreBluetooth Programming Guide but it just describes basic workflows - nothing about the right design.

I'm looking for a nice design approach. You may have any suggestions, hints or links to sample code? Many thanks in advance!

回答1:

Breaking out logic into several self contained classes is always good design. You should definitely try to group your code according to services or other categories. Even though the peripheral has only a single delegate, you can easily implement the dispatcher pattern where you register the various service implementations and selection keys (practically the service objects) and dispatch the call to the designated service handler. If the service classes implement the CPPeripheralDelegate protocol, then this design will allow you to test/reuse each service separately if you need to with minimal changes in the code.

In pseudo obj-c code the dispatcher peripheral delegate would look like as follows:

// The ivar/property serving as the registry
NSMutableDictionary *registeredHandlers = [[NSMutableDictionary alloc] init];

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
  // for each service create an instance of its handler class and
  // add them to the registered handlers
  for (CBService *service : peripheral.services) {
    if (!registeredHandlers[service]) { // don't reinitialize if not needed
      ExtendedCBPeripheralDelegate *serviceHandler = [self instantiateHandlerForService:service];
      [registeredHandlers setObject:serviceHandler forKey:service];
      [serviceHandler discoverCharacteristics]; // make this functionality self contained for the service
    }
  }
}

In service or characteristic related callbacks the dispatching should be implemented. An example:

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
  ExtendedCBPeripheralDelegate *serviceHandler = registeredHandlers[service];
  [serviceHandler peripheral:peripheral didDiscoverCharacteristicsForService:service error:error];
}

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
  ExtendedCBPeripheralDelegate *serviceHandler = registeredHandlers[characteristic.service];
  [serviceHandler peripheral:peripheral didWriteValueForCharacteristic:characteristic error:error];
}

If the central manager is powered off, then the best solution is to drop the whole peripheral delegate. Don't bother with reinitialization, rather plan for disposal. Of course, if needed, you can notify the service handlers of the imminent destruction.