代理轉(zhuǎn)換為可觀察序列(DelegateProxy)

  • DelegateProxy

    • DelegateProxy 是代理委托蹋偏,我們可以將它看作是代理的代理
    • DelegateProxy 的作用是做為一個(gè)中間代理,他會(huì)先把系統(tǒng)的 delegate 對象保存一份威始,然后攔截 delegate 的方法像街。也就是說在每次觸發(fā) delegate 方法之前晋渺,會(huì)先調(diào)用 DelegateProxy 這邊對應(yīng)的方法木西,我們可以在這里發(fā)射序列給多個(gè)訂閱者
  • 以獲取地理定位信息為例

    • 創(chuàng)建RxCLLocationManagerDelegateProxy.swift
    import CoreLocation
    import RxSwift
    import RxCocoa
    
    // 必須實(shí)現(xiàn)
    extension CLLocationManager: HasDelegate {
        public typealias Delegate = CLLocationManagerDelegate
    }
    
    // 必須遵守DelegateProxy和DelegateProxyType俭厚,其中DelegateProxy需要遵守對應(yīng)的類和對應(yīng)的代理,用<>,尖括號中宏逗號(,)隔開
    public class RxCLLocationManagerDelegateProxy
        // 注意寫法
        : DelegateProxy<CLLocationManager, CLLocationManagerDelegate>
        , DelegateProxyType , CLLocationManagerDelegate {
      
       // 必須實(shí)現(xiàn)
        public init(locationManager: CLLocationManager) {
            super.init(parentObject: locationManager,
                       delegateProxy: RxCLLocationManagerDelegateProxy.self)
        }
      
        // 必須實(shí)現(xiàn)
        public static func registerKnownImplementations() {
            self.register { RxCLLocationManagerDelegateProxy(locationManager: $0) }
        }
      
        /*****************根據(jù)具體代理具體實(shí)現(xiàn)******************/
        internal lazy var didUpdateLocationsSubject = PublishSubject<[CLLocation]>()
        internal lazy var didFailWithErrorSubject = PublishSubject<Error>()
      
        public func locationManager(_ manager: CLLocationManager,
                                    didUpdateLocations locations: [CLLocation]) {
            _forwardToDelegate?.locationManager?(manager, didUpdateLocations: locations)
            didUpdateLocationsSubject.onNext(locations)
        }
      
        public func locationManager(_ manager: CLLocationManager,
                                    didFailWithError error: Error) {
            _forwardToDelegate?.locationManager?(manager, didFailWithError: error)
            didFailWithErrorSubject.onNext(error)
        }
        /*****************根據(jù)具體代理具體實(shí)現(xiàn)******************/
    
        // 必須實(shí)現(xiàn)扛门,把創(chuàng)建的序列結(jié)束掉
        deinit {
            self.didUpdateLocationsSubject.on(.completed)
            self.didFailWithErrorSubject.on(.completed)
        }
    }
    
    • 創(chuàng)建CLLocationManager+Rx.swift
    import CoreLocation
    import RxSwift
    import RxCocoa
    
    extension Reactive where Base: CLLocationManager {
      
        /**
         Reactive wrapper for `delegate`.
       
         For more information take a look at `DelegateProxyType` protocol documentation.
         */
        // 必須實(shí)現(xiàn)
        public var delegate: DelegateProxy<CLLocationManager, CLLocationManagerDelegate> {
            return RxCLLocationManagerDelegateProxy.proxy(for: base)
        }
      
        // 以下都是根據(jù)具體代理具體實(shí)現(xiàn)
        // MARK: Responding to Location Events
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var didUpdateLocations: Observable<[CLLocation]> {
            return RxCLLocationManagerDelegateProxy.proxy(for: base)
                .didUpdateLocationsSubject.asObservable()
        }
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var didFailWithError: Observable<Error> {
            return RxCLLocationManagerDelegateProxy.proxy(for: base)
                .didFailWithErrorSubject.asObservable()
        }
       
        #if os(iOS) || os(macOS)
        /**
         Reactive wrapper for `delegate` message.
         */
        public var didFinishDeferredUpdatesWithError: Observable<Error?> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManager(_:didFinishDeferredUpdatesWithError:)))
                .map { a in
                    return try castOptionalOrThrow(Error.self, a[1])
            }
        }
        #endif
      
        #if os(iOS)
      
        // MARK: Pausing Location Updates
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var didPauseLocationUpdates: Observable<Void> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManagerDidPauseLocationUpdates(_:)))
                .map { _ in
                    return ()
            }
        }
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var didResumeLocationUpdates: Observable<Void> {
            return delegate.methodInvoked( #selector(CLLocationManagerDelegate
                .locationManagerDidResumeLocationUpdates(_:)))
                .map { _ in
                    return ()
            }
        }
      
        // MARK: Responding to Heading Events
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var didUpdateHeading: Observable<CLHeading> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManager(_:didUpdateHeading:)))
                .map { a in
                    return try castOrThrow(CLHeading.self, a[1])
            }
        }
      
        // MARK: Responding to Region Events
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var didEnterRegion: Observable<CLRegion> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManager(_:didEnterRegion:)))
                .map { a in
                    return try castOrThrow(CLRegion.self, a[1])
            }
        }
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var didExitRegion: Observable<CLRegion> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManager(_:didExitRegion:)))
                .map { a in
                    return try castOrThrow(CLRegion.self, a[1])
            }
        }
      
        #endif
      
        #if os(iOS) || os(macOS)
      
        /**
         Reactive wrapper for `delegate` message.
         */
        @available(OSX 10.10, *)
        public var didDetermineStateForRegion: Observable<(state: CLRegionState,
            region: CLRegion)> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManager(_:didDetermineState:for:)))
                .map { a in
                    let stateNumber = try castOrThrow(NSNumber.self, a[1])
                    let state = CLRegionState(rawValue: stateNumber.intValue)
                        ?? CLRegionState.unknown
                    let region = try castOrThrow(CLRegion.self, a[2])
                    return (state: state, region: region)
            }
        }
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var monitoringDidFailForRegionWithError:
            Observable<(region: CLRegion?, error: Error)> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManager(_:monitoringDidFailFor:withError:)))
                .map { a in
                    let region = try castOptionalOrThrow(CLRegion.self, a[1])
                    let error = try castOrThrow(Error.self, a[2])
                    return (region: region, error: error)
            }
        }
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var didStartMonitoringForRegion: Observable<CLRegion> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManager(_:didStartMonitoringFor:)))
                .map { a in
                    return try castOrThrow(CLRegion.self, a[1])
            }
        }
      
        #endif
      
        #if os(iOS)
      
        // MARK: Responding to Ranging Events
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var didRangeBeaconsInRegion: Observable<(beacons: [CLBeacon],
            region: CLBeaconRegion)> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManager(_:didRangeBeacons:in:)))
                .map { a in
                    let beacons = try castOrThrow([CLBeacon].self, a[1])
                    let region = try castOrThrow(CLBeaconRegion.self, a[2])
                    return (beacons: beacons, region: region)
            }
        }
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var rangingBeaconsDidFailForRegionWithError:
            Observable<(region: CLBeaconRegion, error: Error)> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManager(_:rangingBeaconsDidFailFor:withError:)))
                .map { a in
                    let region = try castOrThrow(CLBeaconRegion.self, a[1])
                    let error = try castOrThrow(Error.self, a[2])
                    return (region: region, error: error)
            }
        }
      
        // MARK: Responding to Visit Events
      
        /**
         Reactive wrapper for `delegate` message.
         */
        @available(iOS 8.0, *)
        public var didVisit: Observable<CLVisit> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManager(_:didVisit:)))
                .map { a in
                    return try castOrThrow(CLVisit.self, a[1])
            }
        }
      
        #endif
      
        // MARK: Responding to Authorization Changes
      
        /**
         Reactive wrapper for `delegate` message.
         */
        public var didChangeAuthorizationStatus: Observable<CLAuthorizationStatus> {
            return delegate.methodInvoked(#selector(CLLocationManagerDelegate
                .locationManager(_:didChangeAuthorization:)))
                .map { a in
                    let number = try castOrThrow(NSNumber.self, a[1])
                    return CLAuthorizationStatus(rawValue: Int32(number.intValue))
                        ?? .notDetermined
            }
        }
    }
    
    /// 自定義的類型裝換方法
    fileprivate 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
    }
    
    /// 自定義的可選類型裝換方法
    fileprivate func castOptionalOrThrow<T>(_ resultType: T.Type,
                                            _ object: Any) throws -> T? {
        if NSNull().isEqual(object) {
            return nil
        }
      
        guard let returnValue = object as? T else {
            throw RxCocoaError.castingError(object: object, targetType: resultType)
        }
      
        return returnValue
    }
    

參考文章:Swift - RxSwift的使用詳解58(DelegateProxy樣例1:獲取地理定位信息 )
Swift - RxSwift的使用詳解59(DelegateProxy樣例2:圖片選擇功能 )
Swift - RxSwift的使用詳解60(DelegateProxy樣例3:應(yīng)用生命周期的狀態(tài)變化)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末论寨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子爽茴,更是在濱河造成了極大的恐慌葬凳,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件室奏,死亡現(xiàn)場離奇詭異火焰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)胧沫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門昌简,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绒怨,“玉大人,你說我怎么就攤上這事犬金×” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵音同,是天一觀的道長权均。 經(jīng)常有香客問我,道長叽赊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任囊咏,我火速辦了婚禮梅割,結(jié)果婚禮上葛家,老公的妹妹穿的比我還像新娘。我一直安慰自己底燎,他們只是感情好弹砚,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布朱沃。 她就那樣靜靜地躺著,像睡著了一般为流。 火紅的嫁衣襯著肌膚如雪敬察。 梳的紋絲不亂的頭發(fā)上尔当,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天椭迎,我揣著相機(jī)與錄音,去河邊找鬼缴阎。 笑死简软,一個(gè)胖子當(dāng)著我的面吹牛述暂,可吹牛的內(nèi)容都是我干的畦韭。 我是一名探鬼主播肛跌,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼衍慎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酝掩?” 一聲冷哼從身側(cè)響起眷柔,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤驯嘱,失蹤者是張志新(化名)和其女友劉穎鞠评,沒想到半個(gè)月后壕鹉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡负乡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年抖棘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了切省。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帕胆。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖芙盘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晨汹,我是刑警寧澤贷盲,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布巩剖,位于F島的核電站,受9級特大地震影響曙聂,放射性物質(zhì)發(fā)生泄漏鞠鲜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一榆苞、第九天 我趴在偏房一處隱蔽的房頂上張望坐漏。 院中可真熱鬧碧信,春花似錦、人聲如沸砰碴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弱卡。三九已至住册,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凡人,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工传睹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岸晦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓邢隧,卻偏偏與公主長得像倒慧,于是被迫代替她去往敵國和親包券。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容