Skip to content

Commit

Permalink
Updated the BLECentralManager implementation (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
Henryforce authored Jul 27, 2024
1 parent bff416f commit b4d82b8
Show file tree
Hide file tree
Showing 16 changed files with 186 additions and 206 deletions.
32 changes: 22 additions & 10 deletions Sources/BLECombineKit/Central/BLECentralManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,22 @@ public protocol BLECentralManager: AnyObject {
var isScanning: Bool { get }

/// The current state as a publisher.
var state: AnyPublisher<ManagerState, Never> { get }
var state: AnyPublisher<CBManagerState, Never> { get }

/// Retrieve peripherals given a set of identifiers.
/// This method will generate events for any matching peripheral or an error.
/// Retrieve a list of known peripherals by their identifiers.
///
/// The returned Publisher can complete without emitting any peripherals if there are no known peripherals. Depending on the usage, `collect(_:)` might be an option for converting the output into an array.
///
/// - Returns: A Publisher that will emit peripherals and then complete.
func retrievePeripherals(withIdentifiers identifiers: [UUID]) -> AnyPublisher<
BLEPeripheral, BLEError
>

/// Retrieve connected peripherals given a set of service identifiers.
/// This method will generate events for any matching peripheral or an error.
/// Returns a list of the peripherals connected to the system whose services match a given set of criteria.
///
/// The returned Publisher can complete without emitting any peripherals if there are no connected peripherals. Depending on the usage, `collect(_:)` might be an option for converting the output into an array.
///
/// - Returns: A Publisher that will emit peripherals and then complete.
func retrieveConnectedPeripherals(withServices serviceUUIDs: [CBUUID]) -> AnyPublisher<
BLEPeripheral, BLEError
>
Expand All @@ -47,19 +53,25 @@ public protocol BLECentralManager: AnyObject {
>

/// Cancel a peripheral connection.
///
/// - Returns: A Publisher that emits a completion when the given peripheral is disconnected.
func cancelPeripheralConnection(_ peripheral: BLEPeripheral) -> AnyPublisher<Never, Never>

/// Register for any connection events.
#if os(iOS) || os(tvOS) || os(watchOS)
/// Register for any connection events.
func registerForConnectionEvents(options: [CBConnectionEventMatchingOption: Any]?)
#endif

/// Observe for any changes to the willRestoreState.
/// This method will generate an event for each update to willRestoreState, if any.
/// Observe for any changes when the system is about to restore the central manager, as part of relaunching the app into the background.
///
/// This method only applies to apps that opt in to the state preservation and restoration feature of Core Bluetooth. The system invokes this method when relaunching your app into the background to complete some Bluetooth-related task. Use this method to synchronize the state of your app with the state of the Bluetooth system.
///
/// - Returns: A Publisher that emits a dictionary that contains information about the central manager preserved by the system when it terminated the app.
func observeWillRestoreState() -> AnyPublisher<[String: Any], Never>

/// Observe any update to the ANCS authorization.
/// This method will trigger an event for any call to the delegate method
/// `didUpdateANCSAuthorizationFor`.
/// This method will trigger an event for any call to the delegate method `centralManager(_:didUpdateANCSAuthorizationFor:)`.
///
/// - Returns: A Publisher that emits a peripheral whose ANCS authorization status changed.
func observeDidUpdateANCSAuthorization() -> AnyPublisher<BLEPeripheral, Never>
}
9 changes: 4 additions & 5 deletions Sources/BLECombineKit/Central/BLECentralManagerDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ typealias DidDiscoverAdvertisementDataResult = (

final class BLECentralManagerDelegate: NSObject, CBCentralManagerDelegate {

let didConnectPeripheral = PassthroughSubject<CBPeripheralWrapper, Never>()
let didConnectPeripheral = PassthroughSubject<CBPeripheralWrapper, BLEError>()
let didDisconnectPeripheral = PassthroughSubject<CBPeripheralWrapper, Never>()
let didFailToConnect = PassthroughSubject<CBPeripheralWrapper, Never>()
let didDiscoverAdvertisementData = PassthroughSubject<
DidDiscoverAdvertisementDataResult, BLEError
>()
let didUpdateState = PassthroughSubject<ManagerState, Never>()
let didUpdateState = PassthroughSubject<CBManagerState, Never>()
let willRestoreState = PassthroughSubject<[String: Any], Never>()
let didUpdateANCSAuthorization = PassthroughSubject<CBPeripheralWrapper, Never>()

Expand Down Expand Up @@ -54,8 +54,7 @@ final class BLECentralManagerDelegate: NSObject, CBCentralManagerDelegate {
}

public func centralManagerDidUpdateState(_ central: CBCentralManager) {
guard let state = ManagerState(rawValue: central.state.rawValue) else { return }
didUpdateState.send(state)
didUpdateState.send(central.state)
}

public func centralManager(
Expand All @@ -65,7 +64,7 @@ final class BLECentralManagerDelegate: NSObject, CBCentralManagerDelegate {
willRestoreState.send(dict)
}

#if !os(macOS)
#if os(iOS) || os(tvOS) || os(watchOS)
public func centralManager(
_ central: CBCentralManager,
didUpdateANCSAuthorizationFor peripheral: CBPeripheral
Expand Down
2 changes: 1 addition & 1 deletion Sources/BLECombineKit/Central/BLEScanResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

public final class BLEScanResult {
public struct BLEScanResult {
public let peripheral: BLEPeripheral
public let advertisementData: [String: Any]
public let rssi: NSNumber
Expand Down
96 changes: 44 additions & 52 deletions Sources/BLECombineKit/Central/CBCentralManagerWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,79 +10,71 @@ import CoreBluetooth
import Foundation

/// Interface for wrapping the CBCentralManager.
/// This interface is critical in order to mock the CBCentralManager calls as Apple has the
/// init restricted.
/// This interface is critical in order to mock the CBCentralManager calls.
public protocol CBCentralManagerWrapper {
var manager: CBCentralManager? { get }
/// The CBCentralManager this interface wraps to.
/// Note that CBCentralManager conforms to CBCentralManagerWrapper and this getter interface is a convenient way to avoid an expensive downcast. That is, if you need a fixed reference to the CBCentralManager object do not run `let validManager = manager as? CBCentralManager`, simply run `let validManager = manager.wrappedManager` which will run significantly faster.
var wrappedManager: CBCentralManager? { get }

/// The delegate object that will receive central events.
var delegate: CBCentralManagerDelegate? { get }

/// The scanning status of this manager.
var isScanning: Bool { get }

func retrievePeripherals(withIdentifiers identifiers: [UUID]) -> [CBPeripheralWrapper]
func retrieveConnectedPeripherals(withServices serviceUUIDs: [CBUUID]) -> [CBPeripheralWrapper]
/// Set up the delegate of the wrapped CBCentralManager.
/// Avoid calling this method unless you explicitly want to listen to delegate events at the cost of breaking the manager's observable events.
func setupDelegate(_ delegate: CBCentralManagerDelegate)

/// Retrieve peripherals.
func retrieveCBPeripherals(withIdentifiers identifiers: [UUID]) -> [CBPeripheralWrapper]

/// Retrieve all the connected peripherals.
func retrieveConnectedCBPeripherals(withServices serviceUUIDs: [CBUUID]) -> [CBPeripheralWrapper]

/// Start scanning for peripherals with the given set of services and options.
func scanForPeripherals(withServices serviceUUIDs: [CBUUID]?, options: [String: Any]?)

/// Stop scanning for peripherals.
func stopScan()

/// Connect to a wrapped peripheral with options.
func connect(_ wrappedPeripheral: CBPeripheralWrapper, options: [String: Any]?)

/// Cancel the connection to a wrapped peripheral.
func cancelPeripheralConnection(_ wrappedPeripheral: CBPeripheralWrapper)
#if !os(macOS)

#if os(iOS) || os(tvOS) || os(watchOS)
/// Register for connection events with options.
func registerForConnectionEvents(options: [CBConnectionEventMatchingOption: Any]?)
#endif
}

final class StandardCBCentralManagerWrapper: CBCentralManagerWrapper {

var manager: CBCentralManager? {
wrappedManager
extension CBCentralManager: CBCentralManagerWrapper {
public var wrappedManager: CBCentralManager? {
self
}

var isScanning: Bool {
wrappedManager.isScanning
public func setupDelegate(_ delegate: CBCentralManagerDelegate) {
self.delegate = delegate
}

let wrappedManager: CBCentralManager

init(with manager: CBCentralManager) {
self.wrappedManager = manager
public func retrieveCBPeripherals(withIdentifiers identifiers: [UUID]) -> [CBPeripheralWrapper] {
return retrievePeripherals(withIdentifiers: identifiers)
}

func retrievePeripherals(withIdentifiers identifiers: [UUID]) -> [CBPeripheralWrapper] {
wrappedManager
.retrievePeripherals(withIdentifiers: identifiers)
public func retrieveConnectedCBPeripherals(withServices serviceUUIDs: [CBUUID])
-> [CBPeripheralWrapper]
{
return retrieveConnectedPeripherals(withServices: serviceUUIDs)
}

func retrieveConnectedPeripherals(
withServices serviceUUIDs: [CBUUID]
) -> [CBPeripheralWrapper] {
wrappedManager
.retrieveConnectedPeripherals(withServices: serviceUUIDs)
public func connect(_ wrappedPeripheral: CBPeripheralWrapper, options: [String: Any]?) {
wrappedPeripheral.connect(manager: self)
}

func scanForPeripherals(withServices serviceUUIDs: [CBUUID]?, options: [String: Any]?) {
wrappedManager.scanForPeripherals(withServices: serviceUUIDs, options: options)
}

func stopScan() {
wrappedManager.stopScan()
}

func connect(_ wrappedPeripheral: CBPeripheralWrapper, options: [String: Any]?) {
guard let manager else { return }
wrappedPeripheral.connect(manager: manager)
}

func cancelPeripheralConnection(_ wrappedPeripheral: CBPeripheralWrapper) {
guard let manager else { return }
wrappedPeripheral.cancelConnection(manager: manager)
}

#if !os(macOS)
func registerForConnectionEvents(options: [CBConnectionEventMatchingOption: Any]?) {

wrappedManager.registerForConnectionEvents(options: options)

}
#endif

func setupDelegate(_ delegate: CBCentralManagerDelegate) {
wrappedManager.delegate = delegate
public func cancelPeripheralConnection(_ wrappedPeripheral: CBPeripheralWrapper) {
wrappedPeripheral.cancelConnection(manager: self)
}

}
18 changes: 0 additions & 18 deletions Sources/BLECombineKit/Central/ManagerState.swift

This file was deleted.

Loading

0 comments on commit b4d82b8

Please sign in to comment.