I had a similar problem with CoreBluetooth for connecting to Bluetooth LE devices, in my case connecting to iOS devices (peripherals) from my Mac (central).
If I understand you correctly, the template is quite consistent, the first time I run the Mac application for debugging, it is always detected and connected to any Bluetooth devices (peripherals), most importantly, it also detects their services / features well. The problem starts in the second run (for example, change the code, press cmd-R to restart debug). The center still detects peripheral devices and connects to them, but does not detect any services / features. In other words, the delegate peripheral:didDiscoverServices: and peripheral:didDiscoverCharacteristicsForService:error: will never be called.
The solution after a lot of trial and error is surprisingly simple. It seems that CoreBluetooth caches services and characteristics for peripherals that are still connected, although locally it looks like it was disconnected from the application, the peripheral device still supports Bluetooth connectivity to the system. For these types of connections, there is no need to (re) detect services and characteristics, just access them directly from the peripheral object, check nil to see if they should be detected. In addition, as already mentioned, since the peripheral device is in a state between connections, it is best to call cancelPeripheralConnection: right before trying to connect. Its essence is as follows: suppose that we have already discovered a peripheral connection with:
-(void) centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { [central cancelPeripheralConnection:peripheral]; //IMPORTANT, to clear off any pending connections [central connectPeripheral:peripheral options:nil]; } -(void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { peripheral.delegate = self; if(peripheral.services) [self peripheral:peripheral didDiscoverServices:nil]; //already discovered services, DO NOT re-discover. Just pass along the peripheral. else [peripheral discoverServices:nil]; //yet to discover, normal path. Discover your services needed } -(void) peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { for(CBService* svc in peripheral.services) { if(svc.characteristics) [self peripheral:peripheral didDiscoverCharacteristicsForService:svc error:nil]; //already discovered characteristic before, DO NOT do it again else [peripheral discoverCharacteristics:nil forService:svc]; //need to discover characteristics } } -(void) peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { for(CBCharacteristic* c in service.characteristics) { //Do some work with the characteristic... } }
This works well for me for the CBCentralManager app on Mac. I have never tested it in iOS, but I assume that it should be very similar.
PL
source share