diff --git a/Sources/SwiftBluetooth/CentralManager/CentralManager+async.swift b/Sources/SwiftBluetooth/CentralManager/CentralManager+async.swift index d9b8c21..6a7adea 100644 --- a/Sources/SwiftBluetooth/CentralManager/CentralManager+async.swift +++ b/Sources/SwiftBluetooth/CentralManager/CentralManager+async.swift @@ -3,9 +3,9 @@ import CoreBluetooth public extension CentralManager { @available(iOS 13, macOS 10.15, watchOS 6.0, tvOS 13.0, *) - func waitUntilReady() async throws { + func waitUntilReady(timeout: TimeInterval = Double.infinity) async throws { try await withCheckedThrowingContinuation { cont in - self.waitUntilReady { result in + self.waitUntilReady(timeout: timeout) { result in cont.resume(with: result) } } @@ -44,13 +44,13 @@ public extension CentralManager { // This method doesn't need to be marked async, but it prevents a signature collision @available(iOS 13, macOS 10.15, watchOS 6.0, tvOS 13.0, *) - func scanForPeripherals(withServices services: [CBUUID]? = nil, timeout: TimeInterval? = nil, options: [String: Any]? = nil) async -> AsyncStream { + func scanForPeripherals(withServices services: [CBUUID]? = nil, timeout: TimeInterval? = nil, options: [String: Any]? = nil) async -> AsyncStream { .init { cont in var timer: Timer? let subscription = eventSubscriptions.queue { event, done in switch event { - case .discovered(let peripheral, _, _): - cont.yield(peripheral) + case .discovered(let peripheral, let advData, let rssi): + cont.yield(PeripheralScanResult(peripheral: peripheral, advertisementData: advData, rssi: rssi)) case .stopScan: done() cont.finish() diff --git a/Sources/SwiftBluetooth/CentralManager/CentralManager+callback.swift b/Sources/SwiftBluetooth/CentralManager/CentralManager+callback.swift index 7fd8c0a..6b6796b 100644 --- a/Sources/SwiftBluetooth/CentralManager/CentralManager+callback.swift +++ b/Sources/SwiftBluetooth/CentralManager/CentralManager+callback.swift @@ -1,8 +1,14 @@ import Foundation import CoreBluetooth +public struct PeripheralScanResult { + public let peripheral: Peripheral + public let advertisementData: [String: Any] + public let rssi: NSNumber +} + public extension CentralManager { - func waitUntilReady(completionHandler: @escaping (Result) -> Void) { + func waitUntilReady(timeout: TimeInterval = Double.infinity, completionHandler: @escaping (Result) -> Void) { eventQueue.async { [self] in guard state != .poweredOn else { completionHandler(.success(Void())) @@ -18,8 +24,9 @@ public extension CentralManager { completionHandler(.failure(CentralError.unavailable)) return } - - eventSubscriptions.queue { event, done in + + var timer: Timer? + let task = eventSubscriptions.queue { event, done in guard case .stateUpdated(let state) = event else { return } switch state { @@ -32,9 +39,19 @@ public extension CentralManager { default: return } - + + timer?.invalidate() done() } + + if timeout != .infinity { + let timeoutTimer = Timer(fire: Date() + timeout, interval: 0, repeats: false) { _ in + task.cancel() + completionHandler(.failure(CentralError.poweredOff)) + } + timer = timeoutTimer + RunLoop.main.add(timeoutTimer, forMode: .default) + } } } @@ -78,13 +95,13 @@ public extension CentralManager { } } - func scanForPeripherals(withServices services: [CBUUID]? = nil, timeout: TimeInterval? = nil, options: [String: Any]? = nil, onPeripheralFound: @escaping (Peripheral) -> Void) -> CancellableTask { + func scanForPeripherals(withServices services: [CBUUID]? = nil, timeout: TimeInterval? = nil, options: [String: Any]? = nil, onPeripheralFound: @escaping (PeripheralScanResult) -> Void) -> CancellableTask { eventQueue.sync { var timer: Timer? let subscription = eventSubscriptions.queue { event, done in switch event { - case .discovered(let peripheral, _, _): - onPeripheralFound(peripheral) + case .discovered(let peripheral, let advData, let rssi): + onPeripheralFound(PeripheralScanResult(peripheral: peripheral, advertisementData: advData, rssi: rssi)) case .stopScan: done() default: diff --git a/Sources/SwiftBluetooth/CentralManager/CentralManagerError.swift b/Sources/SwiftBluetooth/CentralManager/CentralManagerError.swift index 6caff7f..ce795e1 100644 --- a/Sources/SwiftBluetooth/CentralManager/CentralManagerError.swift +++ b/Sources/SwiftBluetooth/CentralManager/CentralManagerError.swift @@ -4,4 +4,5 @@ public enum CentralError: Error { case unknown case unauthorized case unavailable + case poweredOff }