Skip to content

Commit

Permalink
Queue fixes, advertisement data/rssi, connection respects task cancel…
Browse files Browse the repository at this point in the history
…lation
  • Loading branch information
exPHAT committed Dec 5, 2023
1 parent 4ae2470 commit 06ed317
Show file tree
Hide file tree
Showing 9 changed files with 365 additions and 217 deletions.
35 changes: 25 additions & 10 deletions Sources/SwiftBluetooth/CentralManager/CentralManager+async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,31 @@ public extension CentralManager {
@available(iOS 13, macOS 10.15, watchOS 6.0, tvOS 13.0, *)
@discardableResult
func connect(_ peripheral: Peripheral, timeout: TimeInterval, options: [String: Any]? = nil) async throws -> Peripheral {

Check warning on line 16 in Sources/SwiftBluetooth/CentralManager/CentralManager+async.swift

View workflow job for this annotation

GitHub Actions / lint

Line Length Violation: Line should be 120 characters or less; currently it has 125 characters (line_length)
try await withCheckedThrowingContinuation { cont in
self.connect(peripheral, timeout: timeout, options: options) { result in
cont.resume(with: result)
var cancelled = false
var continuation: CheckedContinuation<Peripheral, Error>?
let cancel = {
cancelled = true
self.cancelPeripheralConnection(peripheral)
continuation?.resume(throwing: CentralError.cancelled)
}

return try await withTaskCancellationHandler {
try await withCheckedThrowingContinuation { cont in
continuation = cont

if cancelled {
cancel()
return
}

self.connect(peripheral, timeout: timeout, options: options) { result in
guard !cancelled else { return }

cont.resume(with: result)
}
}
} onCancel: {
cancel()
}
}

Expand Down Expand Up @@ -63,14 +84,8 @@ public extension CentralManager {
func cancelPeripheralConnection(_ peripheral: Peripheral) async throws {
try await withCheckedThrowingContinuation { cont in
self.cancelPeripheralConnection(peripheral) { result in
switch result {
case .success:
cont.resume()
case .failure(let error):
cont.resume(throwing: error)
}
cont.resume(with: result)
}
}

}
}
30 changes: 17 additions & 13 deletions Sources/SwiftBluetooth/CentralManager/CentralManager+callback.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import CoreBluetooth

public extension CentralManager {
func waitUntilReady(completionHandler: @escaping () -> Void) {
eventQueue.sync {
eventQueue.async { [self] in
guard state != .poweredOn else {
completionHandler()
return
Expand All @@ -20,7 +20,7 @@ public extension CentralManager {
}

func connect(_ peripheral: Peripheral, timeout: TimeInterval, options: [String: Any]? = nil, completionHandler: @escaping (Result<Peripheral, Error>) -> Void) {
eventQueue.sync {
eventQueue.async { [self] in
guard peripheral.state != .connected else {
completionHandler(.success(peripheral))
return
Expand All @@ -46,12 +46,14 @@ public extension CentralManager {
done()
}

let timeoutTimer = Timer(fire: Date() + timeout, interval: 0, repeats: false) { _ in
task.cancel()
completionHandler(.failure(CBError(.connectionTimeout)))
if timeout != .infinity {
let timeoutTimer = Timer(fire: Date() + timeout, interval: 0, repeats: false) { _ in
task.cancel()
completionHandler(.failure(CBError(.connectionTimeout)))
}
timer = timeoutTimer
RunLoop.main.add(timeoutTimer, forMode: .default)
}
timer = timeoutTimer
RunLoop.main.add(timeoutTimer, forMode: .default)

connect(peripheral, options: options)
}
Expand All @@ -75,12 +77,14 @@ public extension CentralManager {
self.centralManager.stopScan()
}

if let timeout = timeout {
let timeoutTimer = Timer(fire: Date() + timeout, interval: 0, repeats: false) { _ in
subscription.cancel()
if timeout != .infinity {
if let timeout = timeout {
let timeoutTimer = Timer(fire: Date() + timeout, interval: 0, repeats: false) { _ in
subscription.cancel()
}
timer = timeoutTimer
RunLoop.main.add(timeoutTimer, forMode: .default)
}
timer = timeoutTimer
RunLoop.main.add(timeoutTimer, forMode: .default)
}

centralManager.scanForPeripherals(withServices: services, options: options)
Expand All @@ -90,7 +94,7 @@ public extension CentralManager {
}

func cancelPeripheralConnection(_ peripheral: Peripheral, completionHandler: @escaping (Result<Void, Error>) -> Void) {
eventQueue.sync {
eventQueue.async { [self] in
guard connectedPeripherals.contains(peripheral) else {
completionHandler(.success(Void()))
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class CentralManagerDelegateWrapper: NSObject, CBCentralManagerDelegate {
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {

Check warning on line 55 in Sources/SwiftBluetooth/CentralManager/CentralManagerDelegateWrapper.swift

View workflow job for this annotation

GitHub Actions / lint

Line Length Violation: Line should be 120 characters or less; currently it has 147 characters (line_length)
guard let parent = parent else { return }
let peripheral = parent.peripheral(peripheral)
peripheral.discovery = .init(RSSI: RSSI, advertisementData: advertisementData)

parent.eventQueue.async {
parent.eventSubscriptions.recieve(.discovered(peripheral, advertisementData, RSSI))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ import Foundation

public enum CentralError: Error {
case unknown
case cancelled
}
58 changes: 58 additions & 0 deletions Sources/SwiftBluetooth/Peripheral/Peripheral+DiscoveryInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Foundation
import CoreBluetooth

extension Peripheral {
public struct DiscoveryInfo {
public var RSSI: Int
public var advertisementData: AdvertisementData

init(RSSI: NSNumber, advertisementData: [String : Any]) {
self.RSSI = Int(truncating: RSSI)
self.advertisementData = .init(data: advertisementData)
}

public struct AdvertisementData {
private(set) var data: [String : Any]

subscript(key: String) -> Any? {
data[key]
}

public var localName: String? {
data[CBAdvertisementDataLocalNameKey] as? String
}

public var manufacturerData: Data? {
data[CBAdvertisementDataManufacturerDataKey] as? Data
}

public var serviceData: [CBUUID : Data]? {
data[CBAdvertisementDataServiceDataKey] as? [CBUUID : Data]
}

public var serviceUUIDs: [CBUUID]? {
data[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID]
}

public var overflowServiceUUIDs: [CBUUID]? {
data[CBAdvertisementDataOverflowServiceUUIDsKey] as? [CBUUID]
}

public var txPowerLevel: Int? {
guard let value = data[CBAdvertisementDataTxPowerLevelKey] as? NSNumber else { return nil }

return Int(truncating: value)
}

public var isConnectable: Bool? {
guard let value = data[CBAdvertisementDataIsConnectable] as? NSNumber else { return nil }

return Bool(truncating: value)
}

public var solicitedServiceUUIDs: [CBUUID]? {
data[CBAdvertisementDataSolicitedServiceUUIDsKey] as? [CBUUID]
}
}
}
}
Loading

0 comments on commit 06ed317

Please sign in to comment.