iOS RxSwift如何将Core蓝牙连接到Rx序列? [英] iOS RxSwift how to connect Core bluetooth to Rx sequences?

查看:141
本文介绍了iOS RxSwift如何将Core蓝牙连接到Rx序列?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to create an observable sequence to indicate the status of Bluetooth on device. I'm using ReplaySubject<CBManagerState>, but am curious if there is something better, as I hear bad things about using 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)
    }
}

推荐答案

这正是Subjects擅长的事情.它们主要用于将非Rx代码转换为Rx代码.也就是说,RxCocoa具有DelegateProxy类型,该类型旨在处理正确完成委托所需的许多工作.仍然很难弄清楚究竟该如何实现,但是一旦掌握了它们,它们就会非常有用...

This is exactly the kind of things that Subjects are good for. They exist primarily to convert non-Rx code into Rx code. That said, RxCocoa has the DelegateProxy type that is designed to handle a lot of the work necessary to do delegates right. It's still hard to figure out exactly how to implement one, but once you get the hang of it they are quite useful...

我必须承认,大多数代码对我来说都是不可思议的,但确实有效.我会在下面的评论中尽我所能解释.

I have to admit that most of the code is black magic to me, but it does work. I try to explain as much as I can in comments below.

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类实际上会根据方法是否在委托上定义而行为不同(无论该方法做什么),因此我们不想简单地将代理中的每个方法都赋给代理协议.

As far as I can tell, the methodInvoked function performs some meta-programming magic on the object to install the method at runtime. This is done because many of the iOS classes that have delegates actually behave differently depending on whether or not the method was defined on the delegate (regardless of what the method does,) so we don't want to simply give the proxy every method in the protocol.

当然,一旦具备以上条件,就可以了.您可以使用外围设备管理器执行所有标准的RX任务:

Of course, once you have the above in place. You can do all the standard RX stuff with your peripheral manager:

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)

这篇关于iOS RxSwift如何将Core蓝牙连接到Rx序列?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆