Characteristics of a service are lost after successful discovery

My code makes an iPhone use the CBCentralManager to talk to devices peripherals over core bluetooth.

After attempting a connect to a peripheral device, I get a didConnect callback on CBCentralManagerDelegate.

After this I initiate discovery of services using:

peripheral.discoverServices([CBUUID(nsuuid: serviceUUID)])

Since I am only interested in discovering my service of interest and not the others to speed up time to the actual sending of data. This also gives me the didDiscoverServices callback without error prints in which I do the following:

guard let services = peripheral.services, !services.isEmpty else {
                print("Empty services")
                centralManager.cancelPeripheralConnection(peripheral)
                return
            }

And for next steps

if let serviceOfInterest = services.first(where: {$0.uuid == CBUUID(nsuuid: serviceUUID)}) {  //double check for service we want
initiateDiscoverCharacteristics(peripheral: peripheral, service: serviceOfInterest)
}

Below is what initiateDiscoverCharacteristics() does. I basically only tries to discover certain characteristics of the selected service:

peripheral.discoverCharacteristics(
            [CBUUID(nsuuid: readUUID),
             CBUUID(nsuuid: writeUUID)],
            for: serviceOfInterest)

For this also we get the didDiscoverCharacteristicsFor callback without error prints. Here in this callback however we were not doing the serviceOfInterest check to see that we are getting the callback for the service we expect, since our understanding was that we will get didDiscoverCharacteristicsFor callback for the characteristics on the serviceOfInterest because that is what peripheral.discoverCharacteristics() was initiated for.

When we go ahead to write some data/subscribe for notify/read data we have 2 guard statements for services and characteristics of a particular service. The first guard below passes:

if(peripheral.services == nil) {
            print("services yet to be discovered \(peripheral.identifier.uuidString)")
            return
        }

However the second guard below fails:

let serviceOfInterest = peripheral.services?.first(where: {$0.uuid == CBUUID(nsuuid: serviceUUID})
        if((serviceOfInterest?.characteristics == nil) || (serviceOfInterest?.characteristics == [])) {
            print("characteristics yet to be discovered \(peripheral.identifier.uuidString)")
            return
        }

First of all, does the iPhone go ahead and discover other characteristics and services separately even when we explicitly mention the service and the characteristics it should discover?

Now if you say yes and that it maybe the reason of our bug because we didn't do a check for serviceOfInterest in didDiscoverCharacteristicsFor callback, then I have another question. Why don't we get a second/third print in didDiscoverCharacteristicsFor callback signifying that more characteristics were discovered? The peripheral device just disconnects after a set timeout (peripheral device used in our testing does this if we are not communicating with it for a certain amount of time).

This issue is extremely rare. We have seen it only twice in our customer base. Both the instances were on the same iPhone 15 Pro. Once a few months back and once recently. Currently, this iPhone is having iOS version 18.1.1 running on it.

While iOS will go ahead and and discover other services and characteristics for its own use, these will not (should not) trigger callbacks in your code. The didDiscover___ callbacks are triggered by calling the discover___ functions.

And these callbacks should only happen for the services you are inquiring about.

So, the simplest question to ask is, is serviceOfInterest or anywhere where you use a list of service UUIDs populated correctly? If it happens to be nil then you would get the callbacks for all services. Perhaps that is what's happening.

Also, as this seems to be happening with only a single iPhone, is it also happening with a single peripheral? Perhaps the configuration of the peripheral is off for some reason?

In any case, I cannot think of any reason that you would be getting wrong callbacks, or characteristics would be missing. This obviously requires some debugging, but I understand that is going to be difficult with only one occurrence, and not being able to reproduce it yourself.


Argun Tekant /  DTS Engineer / Core Technologies

Characteristics of a service are lost after successful discovery
 
 
Q