我正在尝试创建一个可观察的序列来指示设备上蓝牙的状态。我正在使用 ReplaySubject<CBManagerState>
, 但很好奇是否有更好的东西,因为我听说使用 onNext()
不好
将回调委托(delegate)连接到 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
类型,旨在处理正确执行委托(delegate)所需的大量工作。仍然很难弄清楚具体如何实现,但一旦掌握了窍门,它们就会非常有用...
我不得不承认大部分代码对我来说都是黑魔法,但它确实有效。我尽量在下面的评论中解释。
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
函数对对象执行了一些元编程魔术,以便在运行时安装该方法。这样做是因为许多具有委托(delegate)的 iOS 类实际上根据是否在委托(delegate)上定义了方法(不管方法做什么)而表现不同,所以我们不想简单地给代理中的每个方法协议(protocol)。
当然,一旦您具备上述条件。您可以使用外设管理器执行所有标准 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)
关于iOS RxSwift 如何将核心蓝牙连接到 Rx 序列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52264303/