Add indication-confirmation callback to PeripheralManager#48
Merged
Conversation
Adds a didConfirm(Central, UInt16) callback so callers can be notified when a central acknowledges an indication, distinct from didWrite which only covers writes initiated by the central. Backends document what, if anything, actually triggers it, since not every transport can observe ATT-level indication confirmations with central/characteristic granularity.
Bluetooth.GATTServer already receives ATT indication confirmations internally (see send(_:response:) in GATTServer.swift) but only logs them; its public Callback struct exposes willRead, willWrite, and didWrite only, with no hook for confirmations. This property exists solely to satisfy PeripheralManager conformance and is documented as never invoked until GATTServer.Callback gains a corresponding hook upstream.
CoreBluetooth does not report ATT indication confirmations directly; the closest signal is peripheralManagerIsReady(toUpdateSubscribers:), which fires once the shared transmit queue has space again after updateValue(_:for:onSubscribedCentrals:) returns false. Since CoreBluetooth withholds further updates to a central while an indication to it is unconfirmed, use that as a heuristic: invoke didConfirm from write(_:forCharacteristic:for:) when a targeted update had to wait on the queue before succeeding. This also fixes that method to target the specified central instead of silently broadcasting to all subscribers.
colemancda
force-pushed
the
feature/indication-confirmation
branch
from
July 21, 2026 01:14
e1b22a1 to
cf1acc7
Compare
Code Coverage OverviewLanguages: Swift Swift / code-coverage/llvm-covThe overall coverage in commit c8f1847 in the Show a code coverage summary of the most impacted files.
Updated |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
write(_:forCharacteristic:)/write(_:forCharacteristic:for:)onPeripheralManageronly document that they "optionally emit notifications if configured" — there is no way for a caller to learn that a central actually confirmed an indication. Protocols that need HAP-BLE-style confirmed delivery (indications, not notifications) currently have no signal for this.What this adds
A new protocol requirement in
Sources/GATT/PeripheralProtocol.swift:alongside the existing
willRead/willWrite/didWritecallbacks.Upstream dependency
PureSwift/Bluetoothis now required at8.0.0, which adds adidConfirmhook toGATTServer.Callback(Sources/BluetoothGATT/GATTServer.swift) and forwards the ATT indication confirmation it already receives indidWriteAttribute'ssend(indication) { ... }completion handler, previously only logged and discarded.With that in place:
GATTPeripheral(the generic,GATTServer-backed implementation used on Linux/embedded/etc.) now wiresGATTServer.Callback.didConfirmstraight through to its owndidConfirmproperty — real, per-central, per-characteristic confirmation delivery, not a heuristic.DarwinPeripheral(CoreBluetooth-backed) does not go throughGATTServer, so it isn't affected by that change either way. CoreBluetooth itself has no confirmation-delivery delegate method — the only related signal isperipheralManagerIsReady(toUpdateSubscribers:), which fires when the shared transmit queue (used for all subscribers and both notifications and indications) has space again afterupdateValue(_:for:onSubscribedCentrals:)returnedfalse. Since CoreBluetooth withholds further updates to a central while an indication to it is outstanding,write(_:forCharacteristic:for:)uses that as a best-effort heuristic: it targets the single specified central (previously it silently broadcast to all subscribers, ignoring thecentralparameter) and invokesdidConfirmwhen that update had to wait on the queue before it could be sent. This remains a heuristic, not a verified per-characteristic confirmation, since the queue can also free up for unrelated reasons — documented as such on the property.Changes
Sources/GATT/PeripheralProtocol.swift: adddidConfirmprotocol requirement.Sources/GATT/GATTPeripheral.swift: adddidConfirmstorage/property, wired to the underlyingGATTServer.Callback.didConfirm.Sources/DarwinGATT/DarwinPeripheral.swift: implement best-effortdidConfirmviaperipheralManagerIsReady(toUpdateSubscribers:); fixwrite(_:forCharacteristic:for:)to actually target the specified central.Package.swift: bumpBluetoothdependency to8.0.0.Test plan
swift build --traits BluetoothGATTsucceedsswift test --traits BluetoothGATTpasses (14/14)