Performing consecutive GATT operations on iOS

Hello world!

Given some suitable byte sequences b1, b2 and a Bluetooth Low Energy device that provides two GATT characteristics c1 and c2, can I call CBPeripheral.writeValue(b1, c1) and then CBPeripheral.writeValue(b2, b2) without waiting for the callback of the corresponding CBPeripheralDelegate to occur for the first write operation to c1 and without causing one of both operations to fail due to iOS's BLE implementation? Is there any official documentation or specification that states what would or could happen in such scenario?

The background of this question is that on Android, you usually can only perform one GATT operation on a single BluetoothGatt instance at a time. Consecutive operations, even for different characteristics, that do not await the callback invocations of previously requested GATT operations, usually just fail. However, experimenting with CBPeripheral, I can see that such accesses seem to be working on iOS. Now, is that undefined behavior and I am just observing a forgiving implementation or this actually specified behavior?

I would be glad about any hints or comments on this matter that have foundation in the official Apple developer documentation or any official Bluetooth specification. Since I already tried my luck with search engines, I would assume that this is just undefined behavior. Thank you!

Regards, Luis.

Answered by Engineer in 857803022

This is not a matter of iOS or Android's implementation, but how write-with-response works with BLE. As you imply you are receiving a callback after the write, I will assume you are using write-with-response.

When you are using write-with response, the whole operation requires at least 2 connection intervals to finish. Your write will take 1 connection interval. Then in the second interval you will receive the acknowledgement, and the consecutive callback to your app.

A second write to the same peripheral cannot go through until the first one is completed.

Now, this doesn't necessarily mean that you absolutely cannot call writeValue() again before the callback for the first one, but the success of the second write will depend on a lot of factors that are not under your control, or have knowledge of.

iOS has a very limited queue to buffer write requests. The success of the second write will depend on many factors like the connection parameters, response time of the accessory, etc. and there is no way for your app to know these factors.

If you send a second write before the first one is acknowledged to be complete, and it cannot go through, the write will fail without any errors, and the app will have no way to know if the write went through or not.

In any case, writing this way does not provide any benefit to the throughput of your writes, if that's what you are after. And with the risk of your second write failing intermittently without the app being aware will do more harm than good.

So, our recommendation is to wait for the callback before a second write when using write-with-response.


Argun Tekant /  WWDR Engineering / Core Technologies

Accepted Answer

This is not a matter of iOS or Android's implementation, but how write-with-response works with BLE. As you imply you are receiving a callback after the write, I will assume you are using write-with-response.

When you are using write-with response, the whole operation requires at least 2 connection intervals to finish. Your write will take 1 connection interval. Then in the second interval you will receive the acknowledgement, and the consecutive callback to your app.

A second write to the same peripheral cannot go through until the first one is completed.

Now, this doesn't necessarily mean that you absolutely cannot call writeValue() again before the callback for the first one, but the success of the second write will depend on a lot of factors that are not under your control, or have knowledge of.

iOS has a very limited queue to buffer write requests. The success of the second write will depend on many factors like the connection parameters, response time of the accessory, etc. and there is no way for your app to know these factors.

If you send a second write before the first one is acknowledged to be complete, and it cannot go through, the write will fail without any errors, and the app will have no way to know if the write went through or not.

In any case, writing this way does not provide any benefit to the throughput of your writes, if that's what you are after. And with the risk of your second write failing intermittently without the app being aware will do more harm than good.

So, our recommendation is to wait for the callback before a second write when using write-with-response.


Argun Tekant /  WWDR Engineering / Core Technologies

Hey Argun,

thank you very much for clarifying on this matter. I really appreciate it. For the sake of completeness, I just want to provide a few more details to wrap up this thread.

Indeed, I was referring to the 'write with response' case. Also, I interpret your message in such way that you are basically confirming my original suspicion: If a latter write does not await the callback of previous one, then I depend on implementation-specific behavior that might be subject to (code) modification, scheduling/timing, the general system state, etc.

I am aware of that the Bluetooth specification provides corresponding information. However, I feel like Core Bluetooth seems to introduce at least some kind of abstraction in certain situations. For example, when comparing iOS's approach of subscribing to GATT characteristic values, i.e., CBPeripheral.setNotifyValue(...), with Android's approach, the one of Android feels much closer to the underlying GATT specification. Due to such design decisions and the lack of hard statements regarding the absolute necessity of waiting on callbacks inside your Core Bluetooth Programming Guide and other publicly available information, I felt to the urge to ask my original question explicitly. I assumed that it could also be the case that you implemented corresponding queues in your CoreBluetooth implementation. (Please correct me, if a corresponding statement is actually included in your documentation and I just missed it.) Maybe, this thread will be useful for other developers in the future, too.

Regards, Luis.

Performing consecutive GATT operations on iOS
 
 
Q