在RxCocoa里面封裝了大量系統(tǒng)UI組件的擴(kuò)展霹抛,我們可以仿照RxCocoa里面的封裝方式搓逾,給自己的代理也添加Rx的擴(kuò)展。
大概原理就是通過DelegateProxy把delegate的方法調(diào)用轉(zhuǎn)換為一個observable sequence杯拐。
這里我們用CLLocationManager代理的封裝作為例子霞篡。
1.創(chuàng)建一個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創(chuàng)建Reactive擴(kuò)展
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修飾,所以我們是沒法調(diào)用端逼,我們需要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)