Releases: vilimets/AsyncBLE-Swift
Release list
AsyncBLE 0.1.1
Housekeeping. No API changes and no behaviour changes — a drop-in update from 0.1.0.
Swift 6 strict concurrency
The library now builds warning-free. Five diagnostics that the compiler flags as errors under the Swift 6 language mode are gone:
- Two "reference to captured var
self". A weak capture is avar, because it can become nil, and the queue hop nested inside a stream's termination handler was reading one from concurrently-executing code. The reference is now promoted to a strong local before the hop — which also means that once termination has established the object is alive, the cleanup is guaranteed to run instead of quietly becoming a no-op partway through. - Two non-Sendable closure captures. The resumption callback behind
Connection's suspension helpers, and the work handed to the connection's scheduler, both cross into@Sendableclosures. Both are now declared@Sendable, which states what was already true rather than changing it. DispatchWorkItemin aSendabletype. It is not Sendable-audited, but cancelling one from another thread is precisely what it is for, and cancelling is all the wrapper does with it. The assumption is now stated with@preconcurrencyat theDispatchimport, matching how the CoreBluetooth imports handle the same problem.
None of this touches the isolation design. Mutable state still lives in queue-confined classes reached through one serial queue that is both CoreBluetooth's queue and the actors' executor, and the deployment floor is still iOS 16.
Documentation
Removed the last references to the development plan, which is not part of the repository. Comments that cited it are rewritten rather than stripped, so the reasoning they carried is still there.
Requirements
Unchanged: iOS 16+, Swift 5.9+, Swift Package Manager.
.package(url: "https://github.com/vilimets/AsyncBLE-Swift.git", from: "0.1.1")AsyncBLE 0.1.0
A Swift concurrency wrapper over CoreBluetooth, central role. No delegates, no CoreBluetooth types in public signatures, and reconnection you configure instead of write.
Why it exists
Three things are painful in every CoreBluetooth codebase, and every codebase solves them again from scratch.
Connect timeouts. CoreBluetooth has none — connect(_:options:) waits forever and tells you nothing. connect(_:timeout:) imposes a deadline and withdraws the pending request when it expires.
Connection state. The truth is normally spread across CBPeripheral.state, a handful of delegate callbacks, and whatever booleans got added under deadline. Here it is one AsyncStream of a four-case enum, driven by a pure state machine that is unit-tested without hardware.
Reconnection. The OS already retries a pending connect indefinitely; what it never does is tell you what is happening, or give up. ReconnectPolicy decides when to stop waiting, and the wait itself is observable as reconnecting(attempt:).
What's in it
connect(_:timeout:)with a configurable deadline, andconnectWhenAvailable(_:)for reconnecting to a known device at launch- Connection state machine observable through
Connection.states, adapter availability throughCentral.adapterStates - Link drop and user-initiated disconnect are different transitions, not the same callback
- Async
read,write(with and without response), andnotifications(for:)— all I/O on a connection ordered through one FIFO queue, because actor reentrancy alone reorders concurrent callers - Notification subscriptions restored automatically across a reconnect; in-flight I/O fails fast while reconnecting rather than hanging
- Lazy service and characteristic discovery, cached per link and flushed on reconnect
- Typed characteristics:
Characteristic<Value>pairs a UUID with the type of its value, with built-in codecs forData,Stringand the fixed-width integers - OSLog-backed logging with a pluggable
LogHandler, six categories, four levels withRaw { peripheral, central in ... }escape hatch, scoped to the library's queueactiveConnections/disconnectAll()for auditing links an app forgot to close
No CoreBluetooth type appears in any public signature. The one exception is CBUUID, spelled CharacteristicID and ServiceID so that consuming code needs no CoreBluetooth import at all — the bundled example app has none.
Requirements
iOS 16+, Swift 5.9+, Swift Package Manager.
.package(url: "https://github.com/vilimets/AsyncBLE-Swift.git", from: "0.1.0")Notes
239 tests, covering the state machine's full transition table, the delegate bridge, the discovery cache, the I/O queue and the reconnect path against hand-written fakes — no hardware required. Verified against real hardware by hand.
Peripheral role, background modes and state restoration, L2CAP, RSSI and descriptors are deliberately out of 0.1.0 and tracked as issues.