代码之家  ›  专栏  ›  技术社区  ›  Alex Stone

iOS RXSwift如何将核心蓝牙连接到RX序列?

  •  1
  • Alex Stone  · 技术社区  · 6 年前

    我正在尝试创建一个可观察的序列来指示设备上蓝牙的状态。我在用 ReplaySubject<CBManagerState> 但是我很好奇是否有更好的东西,因为我听说了关于使用 onNext()

    将回调委托连接到Rxswift可观测域的适当方法是什么?

    class BluetoothStatusMonitor: NSObject, CBPeripheralManagerDelegate {
    let bluetoothStatusSequence = ReplaySubject<CBManagerState>.create(bufferSize: 1)
    
    var bluetoothPeripheralManager: CBPeripheralManager?
    
        func checkBluetoothStatus()
        {
            //silently check permissions, without alert
    
            let options = [CBCentralManagerOptionShowPowerAlertKey:0]
            bluetoothPeripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: options)
    
        }
        func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
    
            bluetoothStatusSequence.onNext(peripheral.state)
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   Daniel T.    6 年前

    这正是受试者擅长做的事情。它们主要用于将非RX代码转换为RX代码。也就是说,Rxcococa有 DelegateProxy 设计用于处理大量必要工作以正确处理委托的类型。很难弄清楚如何实现一个,但是一旦你掌握了它的窍门,它们就非常有用了…

    我不得不承认,大多数代码对我来说都是黑色的魔法,但它确实有效。我尽量在下面的评论中解释。

    import RxSwift
    import RxCocoa
    import CoreBluetooth
    
        // The HasDelegate protocol is an associated type for the DelegateProxyType
    extension CBPeripheralManager: HasDelegate {
        public typealias Delegate = CBPeripheralManagerDelegate
    }
    
    class CBPeripheralManagerDelegateProxy
        : DelegateProxy<CBPeripheralManager, CBPeripheralManagerDelegate>
        , DelegateProxyType
        , CBPeripheralManagerDelegate {
    
        init(parentObject: CBPeripheralManager) {
            super.init(parentObject: parentObject, delegateProxy: CBPeripheralManagerDelegateProxy.self)
        }
    
        deinit {
            _didUpdateState.onCompleted()
        }
    
        static func registerKnownImplementations() {
            register { CBPeripheralManagerDelegateProxy(parentObject: $0) }
        }
    
            // a couple of static functions for getting and setting a delegate on the object.
        static func currentDelegate(for object: CBPeripheralManager) -> CBPeripheralManagerDelegate? {
            return object.delegate
        }
    
        static func setCurrentDelegate(_ delegate: CBPeripheralManagerDelegate?, to object: CBPeripheralManager) {
            object.delegate = delegate
        }
    
            // required delegate functions must be implemented in the class. This is where Subjects come in.
        func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
            _didUpdateState.onNext(peripheral.state)
        }
    
        fileprivate let _didUpdateState = PublishSubject<CBManagerState>()
    }
    
    extension Reactive where Base: CBPeripheralManager {
        var delegate: CBPeripheralManagerDelegateProxy {
            return CBPeripheralManagerDelegateProxy.proxy(for: base)
        }
    
        var state: Observable<CBManagerState> {
            return delegate._didUpdateState
        }
    
        var didUpdateState: Observable<Void> {
            return delegate._didUpdateState.map { _ in }
        }
    
            // optional methods are setup using the `methodInvoked` function on the delegate
        var willRestoreState: Observable<[String: Any]> {
            return delegate.methodInvoked(#selector(CBPeripheralManagerDelegate.peripheralManager(_:willRestoreState:)))
                .map { $0[1] as! [String: Any] }
        }
    
        var didStartAdvertising: Observable<Error?> {
            return delegate.methodInvoked(#selector(CBPeripheralManagerDelegate.peripheralManagerDidStartAdvertising(_:error:)))
                .map { $0[1] as? Error }
        }
    
        // I didn't implement all of the optionals. Use the above as a template to implement the rest.
    }
    

    据我所知, methodInvoked 函数对对象执行一些元编程魔术,以便在运行时安装该方法。之所以这样做,是因为许多具有委托的IOS类实际上行为不同,这取决于该方法是否在委托上定义(不管该方法做什么),因此我们不希望简单地将协议中的每个方法都给代理。

    当然,一旦你有了上面的内容。您可以使用外围设备管理器执行所有标准的Rx操作:

    bluetoothManager.rx.state
        .subscribe(onNext: { state in print("current state:", state) })
        .disposed(by: disposeBag)
    
    bluetoothManager.rx.didStartAdvertising
        .subscribe(onNext: { error in
            if let error = error {
                print("there was an error:", error)
            }
        }
        .disposed(by: disposeBag)