Skip to content

fix(ios): Improve reconnection stability (#803)#811

Merged
peitschie merged 5 commits intomainfrom
when-is-a-thread-not-a-thread-when-its-ios
Mar 11, 2026
Merged

fix(ios): Improve reconnection stability (#803)#811
peitschie merged 5 commits intomainfrom
when-is-a-thread-not-a-thread-when-its-ios

Conversation

@peitschie
Copy link
Collaborator

No description provided.

…803)

On rapid reconnect, setTimeout() would overwrite the timeoutMap entry
without cancelling the old DispatchWorkItem. The old work item remained
scheduled and could fire during the new operation, rejecting the wrong
callback. Now explicitly cancel any existing work item for the same key
before creating a replacement.
Plugin.connect() sets up two independent callbacks with timeouts: one at
the Device level (GATT discovery) and one at the DeviceManager level
(physical BLE connection). Both could reject the same CAPPluginCall if
the DeviceManager timeout fired first, leaving the Device timeout as a
zombie. Now when DeviceManager connect fails, the Device-level connect
timeout is explicitly cancelled before rejecting the call.
…ror (#803)

Previously, didDiscoverServices errors were logged but silently dropped,
leaving the connect callback to time out after 10s with a generic
"Connection timeout" message. Now the callback is rejected immediately
with the actual error from CoreBluetooth.
…can + connect (#803)

DeviceManager previously created its own Device objects in
discoveredDevices during scanning. These competed with Plugin.deviceMap's
Device objects for peripheral.delegate ownership, causing service
discovery callbacks to fire on the wrong Device (one with no connect
callback stored), leading to silent connection timeouts on reconnect.

Root cause: Two separate Device objects wrapping the same CBPeripheral
both called peripheral.delegate = self. Whichever touched it last won.

Fix: DeviceManager now stores raw CBPeripheral references instead of
Device objects. Device creation is solely owned by Plugin.deviceMap via
getOrCreateDevice(). This guarantees exactly one Device per peripheral
and eliminates the delegate ownership race entirely.

Closes #803
@peitschie peitschie merged commit bf338ff into main Mar 11, 2026
4 checks passed
@peitschie peitschie deleted the when-is-a-thread-not-a-thread-when-its-ios branch March 11, 2026 03:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant