如何正确地将 3rd 方库委托转换为 RxSwift Observable
Posted
技术标签:
【中文标题】如何正确地将 3rd 方库委托转换为 RxSwift Observable【英文标题】:How to properly convert a 3rd party library delegate into a RxSwift Observable 【发布时间】:2017-03-23 13:38:45 【问题描述】:我有一个案例,我正在使用 3rd 方库,我想把它变成一个 Observable。适当地,该库是围绕代表设计的,正如人们所期望的那样,所以我将它包装起来。库执行异步操作并在完成时调用它的委托并使用结果。
我绝对想利用 observable 的 cold
特性,并且仅在有人订阅时才开始操作。我有一个可行的解决方案,我只是不知道它是否存在严重缺陷,并且我缺少对RxSwift
的一些重要理解,或者也许有更简单的方法可以实现相同的目标。
public final class RxLibBridge: LibDelegate
let lib = Lib()
let _source = PublishSubject<[LibResult]>()
public init()
lib.delegate = self
public func asObservable() -> Observable<[LibResult]>
// create a cold observable to start
// the Lib's async operation on subscribe.
return Observable<Void>.create
observer in
self.lib.startOperation()
// emit and complete
observer.onNext(())
observer.onCompleted()
return Disposables.create()
// convert the `Void` observable into an observable from the
// PublishSubject
.flatMapLatestself._source
// the lib's completion delegate method
public func lib(_ lib: Lib, didFinishWithResult results: [LibResult])
// grab the PublishSubject, emit the result and complete
let observer = _source.asObserver()
observer.onNext(results)
observer.onCompleted()
所以我的问题是:这在 Rx 中是否合适?再次,它有效:
RxLibBridge()
.asObservable()
.subscribe(...)
仅仅因为它有效并不意味着我没有从根本上误解处理这种情况的正确方法。
我知道在 RxSwift 中有一种方法可以处理这样的事情:
https://medium.com/@maxofeden/rxswift-migrate-delegates-to-beautiful-observables-3e606a863048#.rksg2ckpj
https://samritchie.net/2016/05/12/rxswift-delegateproxy-with-required-methods/
我尝试了这种方法,但似乎 API 自 2015 年以来发生了变化。也就是说,在上面的示例链接中,在扩展中添加 rx_delegate
方法时找不到 proxyForObject
。
此外,这种方法似乎更倾向于纯 Objective-C
[UIKit/AppKit] API。在我尝试遵循链接示例时,我正在编辑 3rd 方库的源代码以制作委托方法 optional
并将其公开给 @objc
。该库的代表是required
,我宁愿不必分叉该库来进行修改。
这个 SO 答案为上面的 2 个链接提供了更新的 API:
Can not use proxyForObject function in DelegateProxyType (rxSwift)
【问题讨论】:
【参考方案1】:所以在深入挖掘之后,看起来这将使用所需的委托方法来解决问题,更新为RxSwift 3.3.1
。这是使用他们的DelegateProxy
系统。
import RxSwift
import RxCocoa
import Lib
public final class RxLibDelegate: DelegateProxy, LibDelegate, DelegateProxyType
let _subject = PublishSubject<[LibResult]>()
public static func currentDelegateFor(_ object: AnyObject) -> AnyObject?
let target = object as! Lib
return target.delegate
public static func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject)
let target = object as! Lib
target.delegate = delegate as? LibDelegate
public func lib(_ lib: Lib, didFinishWithResult results: [LibResult])
_subject.onNext(results)
_subject.onCompleted()
extension Lib
public var rx_delegate: DelegateProxy
// `proxyForDelegate` moved as compared to examples at:
// https://samritchie.net/2016/05/12/rxswift-delegateproxy-with-required-methods/
// https://medium.com/@maxofeden/rxswift-migrate-delegates-to-beautiful-observables-3e606a863048#.rksg2ckpj
return RxLibDelegate.proxyForObject(self)
public var rx_libResults: Observable<[LibResult]>
// `proxyForDelegate` moved as compared to examples at:
// https://samritchie.net/2016/05/12/rxswift-delegateproxy-with-required-methods/
// https://medium.com/@maxofeden/rxswift-migrate-delegates-to-beautiful-observables-3e606a863048#.rksg2ckpj
let proxy = RxLibDelegate.proxyForObject(self)
return proxy._subject
大约是 28 LOC。我原来的“包装器”(见下面的更新版本)但我不知道它是否最好是 21 LOC; 6 of 1 半打其他?
在我的特殊情况下,我只需要担心 1 个委托方法。如果您正在使用具有多个委托的功能,我认为 DelegateProxy
+ extension
方法会更实用,并且在这种情况下是更好的选择。
关于我使用 Void
observable 的原始试用包装内容,使用 flatMapLatest
更改流似乎是完全可以接受的,如下所示:按下按钮时发送连续事件:
https://***.com/a/39123102/1060314
import RxSwift
import RxCocoa
let button = submitButton.rx_controlEvent([.TouchDown])
button
.flatMapLatest _ in
Observable<Int64>.interval(0.1, scheduler: MainScheduler.instance)
.takeUntil(self.submitButton.rx_controlEvent([.TouchUpInside]))
.subscribeNext x in print("BOOM \(x)")
.addDisposableTo(disposeBag)
//prints BOOM 0 BOOM 1 BOOM 2 BOOM 3 BOOM 4 BOOM 5 for every 0.1 seconds
请注意,从flatMapLatest
返回一个新的Observable
。作者引用了RxSwift slack channel
,所以我认为至少可以接受。
这是我的包装器版本的更新版本,我认为它可能更简洁:
import RxSwift
public final class RxLibBridge: LibDelegate
let lib = Lib()
let _source = PublishSubject<[LibResult]>()
public init()
lib.delegate = self
public func asObservable() -> Observable<[LibResult]>
// create a cold observable to start
// the Lib's async operation on subscribe.
return Observable.just(())
.do(onNext:
self.lib.startOperation()
)
.flatMapLatestself._source
// the lib's completion delegate method
public func lib(_ lib: Lib, didFinishWithResult results: [LibResult])
// grab the PublishSubject, emit the result and complete
_source.onNext(results)
_source.onCompleted()
【讨论】:
以上是关于如何正确地将 3rd 方库委托转换为 RxSwift Observable的主要内容,如果未能解决你的问题,请参考以下文章