Home Articles

如何正确地将第三方库委托转换为RxSwift Observable

Asked
Viewed 1233 times
2

我有一个案例,我正在使用第三方库,我想把它变成一个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
        .flatMapLatest{self._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/

我试过这种方法,但看起来自2015年以来API发生了变化 . 也就是说,在扩展中添加 rx_delegate 方法时,在上面的链接中找不到 proxyForObject .

此外,这种方法似乎有利于纯 Objective-C [UIKit / AppKit] API . 在我尝试关注链接的示例时,我正在编辑第三方lib的源代码,以使委托方法 optional 并将其公开给 @objc . lib的委托是 required ,我宁愿不用fork来进行修改 .

这个SO答案为上面的2个链接提供了更新的API:

Can not use proxyForObject function in DelegateProxyType (rxSwift)

1 Answer

  • 3

    因此,在挖掘了一些之后,看起来这将使用所需的委托方法,为 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个半打?

    在我的特殊情况下,我只需要担心1个委托方法 . 如果您正在使用具有多个委托的某些功能,我认为 DelegateProxy extension 方法将更加实用,并且在这种情况下是更好的选择 .

    关于我使用 Void observable的原始试用包装物,看起来用 flatMapLatest 改变流是完全可以接受的,如下所示:在按下按钮时发送连续事件:

    https://stackoverflow.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()
                })
                .flatMapLatest{self._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()
        }
    }
    

Related