Add auto-connect support (#206)

* Add autoConnect support

* Enhance autoConnect functionality for Bluetooth devices

- Added support for managing a set of auto-connect devices in both Android and iOS implementations.
- Updated connection and disconnection logic to handle auto-reconnect scenarios appropriately.
- Improved cleanup processes to prevent unwanted reconnections when devices are manually disconnected.

* Refactor connection button and add auto-reconnect toggle

- Updated the connection button layout for better UI consistency.
- Introduced an auto-reconnect toggle to manage automatic reconnections when devices become available.
- Enhanced connection and disconnection logic to respect the auto-connect setting.

* Update version to 1.2.0 and add CHANGELOG for new features

- Bumped version to 1.2.0.
- Added CHANGELOG.md detailing new features including support for `autoConnect` and UI updates for automatic reconnection.

* Improve readme

* Refactor variable naming for clarity in Bluetooth connection logic

- Changed variable name from `isAutoConnect` to `shouldAutoConnect` for better readability and understanding of its purpose in the connection state handling.

* Refactor connection handling in UniversalBlePlugin

- Simplified connection logic by always cleaning up internal state before sending connection change callbacks.
- Ensured GATT resources are only closed when autoConnect is disabled, allowing for better management of Bluetooth connections.

* Refactor disconnection handling in UniversalBlePlugin

- Introduced a new method `handlePeripheralDisconnection` to encapsulate disconnection logic, improving code readability and maintainability.
- Updated the `centralManager` methods to utilize the new disconnection handler, ensuring consistent behavior across connection state changes.

* Update darwin/Classes/UniversalBlePlugin.swift

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Foti Dim <fotios.dimanidis@a2zebra.de>
Co-authored-by: Navideck Labs <130186950+navidecklabs@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Foti Dim
2026-01-16 07:06:24 +01:00
committed by GitHub
parent a30e84a70c
commit 8557a46d05
28 changed files with 323 additions and 106 deletions
+3 -2
View File
@@ -536,7 +536,7 @@ protocol UniversalBlePlatformChannel {
func startScan(filter: UniversalScanFilter?) throws
func stopScan() throws
func isScanning() throws -> Bool
func connect(deviceId: String) throws
func connect(deviceId: String, autoConnect: Bool?) throws
func disconnect(deviceId: String) throws
func setNotifiable(deviceId: String, service: String, characteristic: String, bleInputProperty: Int64, completion: @escaping (Result<Void, Error>) -> Void)
func discoverServices(deviceId: String, withDescriptors: Bool, completion: @escaping (Result<[UniversalBleService], Error>) -> Void)
@@ -681,8 +681,9 @@ class UniversalBlePlatformChannelSetup {
connectChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let deviceIdArg = args[0] as! String
let autoConnectArg: Bool? = nilOrValue(args[1])
do {
try api.connect(deviceId: deviceIdArg)
try api.connect(deviceId: deviceIdArg, autoConnect: autoConnectArg)
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
+56 -5
View File
@@ -42,6 +42,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
private var discoverServicesFutures = [DiscoverServicesFuture]()
private var rssiReadFutures = [RssiReadFuture]()
private var isManageScanning = false
private var autoConnectDevices = Set<String>()
init(callbackChannel: UniversalBleCallbackChannel) {
self.callbackChannel = callbackChannel
@@ -129,15 +130,41 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
UniversalBleLogger.shared.setLogLevel(logLevel)
}
func connect(deviceId: String) throws {
func connect(deviceId: String, autoConnect: Bool?) throws {
let peripheral = try deviceId.getPeripheral(manager: manager)
peripheral.delegate = self
manager.connect(peripheral)
let shouldAutoConnect = autoConnect ?? false
if shouldAutoConnect {
autoConnectDevices.insert(deviceId)
if #available(iOS 17.0, macOS 14.0, watchOS 10.0, tvOS 17.0, *) {
let options: [String: Any] = [CBConnectPeripheralOptionEnableAutoReconnect: true]
manager.connect(peripheral, options: options)
} else {
// Auto-reconnect via CBConnectPeripheralOptionEnableAutoReconnect is only
// available on iOS 17.0 / macOS 14.0 / watchOS 10.0 / tvOS 17.0 and later.
// On earlier OS versions, enabling `autoConnect` will NOT provide automatic
// reconnection behavior. Any desired reconnection must be handled manually
// (e.g., in central manager delegate callbacks).
UniversalBleLogger.shared.logInfo(
"autoConnect requested for device \(deviceId), " +
"but automatic reconnection via CBConnectPeripheralOptionEnableAutoReconnect " +
"is only available on iOS 17+/macOS 14+/watchOS 10+/tvOS 17+. " +
"On this OS version, reconnections must be handled manually."
)
manager.connect(peripheral)
}
} else {
autoConnectDevices.remove(deviceId)
manager.connect(peripheral)
}
}
func disconnect(deviceId: String) throws {
autoConnectDevices.remove(deviceId)
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
callbackChannel.onConnectionChanged(deviceId: deviceId, connected: false, error: nil) { _ in }
cleanUpConnection(deviceId: deviceId)
return
}
if peripheral.state != CBPeripheralState.disconnected {
@@ -445,9 +472,33 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, connected: true, error: nil) { _ in }
}
public func centralManager(_: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error _: Error?) {
callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, connected: false, error: nil) { _ in }
cleanUpConnection(deviceId: peripheral.uuid.uuidString)
private func handlePeripheralDisconnection(deviceId: String, error: Error?) {
autoConnectDevices.remove(deviceId)
callbackChannel.onConnectionChanged(deviceId: deviceId, connected: false, error: error?.localizedDescription) { _ in }
cleanUpConnection(deviceId: deviceId)
}
public func centralManager(
_: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
timestamp: CFAbsoluteTime,
isReconnecting: Bool,
error: Error?
) {
let deviceId = peripheral.uuid.uuidString
if #available(iOS 17.0, macOS 14.0, watchOS 10.0, tvOS 17.0, *) {
if isReconnecting {
return
}
}
handlePeripheralDisconnection(deviceId: deviceId, error: error)
}
public func centralManager(_: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
let deviceId = peripheral.uuid.uuidString
handlePeripheralDisconnection(deviceId: deviceId, error: error)
}
public func centralManager(_: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {