美文网首页
RxSwift 给代理添加Rx扩展

RxSwift 给代理添加Rx扩展

作者: 天外丶飞仙 | 来源:发表于2019-04-16 14:56 被阅读0次

    在RxCocoa里面封装了大量系统UI组件的扩展,我们可以仿照RxCocoa里面的封装方式,给自己的代理也添加Rx的扩展。

    大概原理就是通过DelegateProxy把delegate的方法调用转换为一个observable sequence。

    Av4gtx.png

    这里我们用CLLocationManager代理的封装作为例子。

    1.创建一个RXCLLocationManagerDelegateProxy类
    open class RXCLLocationManagerDelegateProxy:
        DelegateProxy<CLLocationManager, CLLocationManagerDelegate>,
        DelegateProxyType,
        CLLocationManagerDelegate  {
        
        public weak private(set) var locationManager: CLLocationManager?
        
        public init(locationManager: ParentObject) {
            self.locationManager = locationManager
            super.init(parentObject: locationManager, delegateProxy: RXCLLocationManagerDelegateProxy.self)
        }
        
        public static func registerKnownImplementations() {
            self.register { RXCLLocationManagerDelegateProxy(locationManager: $0) }
        }
        
        public class func currentDelegate(for object: ParentObject) -> CLLocationManagerDelegate? {
            return object.delegate
        }
        
        public class func setCurrentDelegate(_ delegate: CLLocationManagerDelegate?, to object: ParentObject) {
            object.delegate = delegate
        }
    }
    
    2.让CLLocationManager遵循HasDelegate
    extension CLLocationManager: HasDelegate {
        public typealias Delegate = CLLocationManagerDelegate
    }
    
    3.给CLLocationManager创建Reactive扩展
    extension Reactive where Base: CLLocationManager {
        
        public var delegate: RXCLLocationManagerDelegateProxy {
            return RXCLLocationManagerDelegateProxy.proxy(for: base)
        }
        
        public var didUpdateLocations: ControlEvent<(manager: CLLocationManager, locations: [CLLocation])> {
            let sel = #selector(CLLocationManagerDelegate.locationManager(_:didUpdateLocations:))
        
            let source = delegate.methodInvoked(sel).map{ (args) -> (manager: CLLocationManager, locations: [CLLocation]) in
                let manager = try castOrThrow(CLLocationManager.self, args[0])
                let locations = try castOrThrow(Array<CLLocation>.self, args[1])
                return (manager, locations)
            }
            return ControlEvent(events: source)
        }
        
        public var didError: ControlEvent<(manager: CLLocationManager, error: Error)> {
            let didFailWithErrorSel = #selector(CLLocationManagerDelegate.locationManager(_:didFailWithError:))
            let didFinishDeferredUpdatesWithErrorSel = #selector(CLLocationManagerDelegate.locationManager(_:didFinishDeferredUpdatesWithError:))
            let generalError = delegate
                .methodInvoked(didFailWithErrorSel)
                .map(clErrorEvent)
            let updatesError = delegate
                .methodInvoked(didFinishDeferredUpdatesWithErrorSel)
                .map(clErrorEvent)
            let source = Observable.of(generalError, updatesError).merge()
            return ControlEvent(events: source)
        }
        
        private func clErrorEvent(_ args: [Any]) throws -> (manager: CLLocationManager, error: Error) {
            let manager = try castOrThrow(CLLocationManager.self, args[0])
            let error = try castOrThrow(Error.self, args[1])
            return (manager, error)
        }
        
        public var didErrorRangingBeacons: ControlEvent<(manager: CLLocationManager, region: CLBeaconRegion, error: Error)> {
            let rangingBeaconsDidFailForRegionSel = #selector(CLLocationManagerDelegate.locationManager(_:rangingBeaconsDidFailFor:withError:))
            let source = delegate
                .methodInvoked(rangingBeaconsDidFailForRegionSel)
                .map { (args) -> (manager: CLLocationManager, region: CLBeaconRegion, error: Error) in
                    let manager = try castOrThrow(CLLocationManager.self, args[0])
                    let beaconRegion = try castOrThrow(CLBeaconRegion.self, args[1])
                    let error = try castOrThrow(Error.self, args[2])
                    return (manager, beaconRegion, error)
                }
            return ControlEvent(events: source)
        }
        
    }
    
    注意

    RxCocoa的 castOrThrow 这个方法没有加public修饰,所以我们是没法调用,我们需要Copy出来.

    func castOrThrow<T>(_ resultType: T.Type, _ object: Any) throws -> T {
        guard let returnValue = object as? T else {
            throw RxCocoaError.castingError(object: object, targetType: resultType)
        }
        return returnValue
    }
    
    
    然后我们就可以使用了
    llocationManager.rx
        .didUpdateLocations
        .subscribe(onNext: { (_, locations) in
            print(locations)
        })
        .disposed(by: disposeBag)
    
    locationManager.rx
        .didError
        .subscribe(onNext: { (_, error) in
            print(error)
        })
        .disposed(by: disposeBag)
    
    locationManager.rx
        .didErrorRangingBeacons
        .subscribe(onNext: { (_, beacons, _) in
            print(beacons)
        })
        .disposed(by: disposeBag)
    

    相关文章

      网友评论

          本文标题:RxSwift 给代理添加Rx扩展

          本文链接:https://www.haomeiwen.com/subject/euebwqtx.html