前言
其實兽赁,這一篇的題目屿良,我覺得應該是RxSwift對代理的封裝色解,最后還是沿用官方Example的命名吧。
效果說明
圖一
圖二
圖一是當App可以使用定位信息時钥勋,顯示當前的經(jīng)緯度炬转。
圖二是當App被禁止使用定位信息時,顯示的提示信息
代碼解釋
比起上兩個Example算灸,這個Example復雜的多了。主要復雜在對Delegate的封裝。
如何使用RxSwift對Delegate的封裝稍后再說贰逾,先看看封裝后的使用错英。
let service = GeolocationService.instance
// 將是否允許使用定位的“Bool”綁定noGeolocationView.rx.isHidden
service.authorized
.drive(noGeolocationView.rx.isHidden)
.addDisposableTo(disposeBag)
// 將定位信息綁定在showLocationLabel.rx.coordinates
service.location
.drive(showLocationLabel.rx.coordinates)
.addDisposableTo(disposeBag)
1、對UILabel的擴展
可以看到上面的例子,將CLLocationCoordinate2D的經(jīng)緯度信息綁定在label上了先煎。當想綁定的在視圖信息越多贼涩,我們就需要對UILabel進行擴展。
擴展方法如下:
/*
意思就是當Reactive的Base對象是UILabel時薯蝎,增加一個類型為UIBindingObserver<Base, CLLocationCoordinate2D>的coordinates屬性遥倦。
*/
private extension Reactive where Base: UILabel {
var coordinates: UIBindingObserver<Base, CLLocationCoordinate2D> {
return UIBindingObserver(UIElement: base, binding: { (label, location) in
label.text = "Lat: \(location.latitude)\nLon: \(location.longitude)"
})
}
}
在后面的block參數(shù)列表中,label的類型是泛型中的Base類型(例子是UILabel)良风,location是泛型中的CLLocationCoordinate2D對象谊迄。因此,我們只要在block中對label和location對象做操作即可烟央。
2统诺、封裝CLLocationManagerDelegate
2.1、創(chuàng)建CLLocationManagerDelegate的代理Proxy
為了創(chuàng)建Proxy對象疑俭,我們需要繼承DelegateProxy粮呢,并且遵守DelegateProxyType協(xié)議,該協(xié)議必須實現(xiàn)以下兩個方法钞艇。
/// 返回object的代理對象
class func currentDelegateFor(_ object: AnyObject) -> AnyObject? {
let locationManager: CLLocationManager = object as! CLLocationManager
return locationManager.delegate
}
/// 設置objct代理對象
class func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
let locationManager: CLLocationManager = object as! CLLocationManager
if let delegate = delegate {
locationManager.delegate = (delegate as! CLLocationManagerDelegate)
} else {
locationManager.delegate = nil
}
}
2.2啄寡、創(chuàng)建PublishSubject對象
先來簡單回顧一下概念:
subject的概念
Subject可以看做是一種代理和橋梁。它既是訂閱者又是訂閱源哩照,這意味著它既可以訂閱其他Observable對象挺物,同時又可以對它的訂閱者們發(fā)送事件。
PublishSubject的概念
當你訂閱PublishSubject的時候飘弧,你只能接收到訂閱他之后發(fā)生的事件
因此為了能夠成為代理的代理识藤,我們需要監(jiān)聽代理的事件,并且能夠讓外部進行監(jiān)聽次伶,所以我們創(chuàng)建了以下兩個publishSubject對象
internal lazy var didUpdateLocationsSubject = PublishSubject<[CLLocation]>()
internal lazy var didFailWithErrorSubject = PublishSubject<Error>()
將代理事件通過subject傳遞出去痴昧,記得調(diào)用_forwardToDelagate?.method方法,因為我們這里只是做了一層監(jiān)聽中轉
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)
}
代理和proxy之間的層級關系圖:
+-------------------------------------------+
| |
| UIView subclass (UIScrollView) |
| |
+-----------+-------------------------------+
|
| Delegate
|
|
+-----------v-------------------------------+
| |
| Delegate proxy : DelegateProxyType +-----+----> Observable<T1>
| , UIScrollViewDelegate | |
+-----------+-------------------------------+ +----> Observable<T2>
| |
| +----> Observable<T3>
| |
| forwards events |
| to custom delegate |
| v
+-----------v-------------------------------+
| |
| Custom delegate (UIScrollViewDelegate) |
| |
+-------------------------------------------+
2.3冠王、對CLLocationManager做擴展
將想被監(jiān)聽的屬性赶撰,通過剛才創(chuàng)建Proxy傳遞出來,于是我們只需要創(chuàng)建對應的Observable柱彻。
代碼如下:
extension Reactive where Base: CLLocationManager {
/**
Reactive wrapper for `delegate`.
For more information take a look at `DelegateProxyType` protocol documentation.
*/
public var delegate: DelegateProxy {
return RxCLLocationManagerDelegateProxy.proxyForObject(base)
}
// 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
}
}
// MARK: Responding to Location Events
/**
Reactive wrapper for `delegate` message.
*/
public var didUpdateLocations: Observable<[CLLocation]> {
return (delegate as! RxCLLocationManagerDelegateProxy).didUpdateLocationsSubject.asObservable()
}
/**
Reactive wrapper for `delegate` message.
*/
public var didFailWithError: Observable<Error> {
return (delegate as! RxCLLocationManagerDelegateProxy).didFailWithErrorSubject.asObservable()
}
}
這里值得一提的是調(diào)后delegate.methodInvoked豪娜,會返回Observable<[Any]>,其中數(shù)組裝的就是傳遞給selector的參數(shù)哟楷,所以后面的map的block中侵歇,a[1]代表的就是CLAuthorizationStatus枚舉類型。
此外吓蘑,還定義了一個轉類型的函數(shù)惕虑,轉失敗后坟冲,會發(fā)出Error
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
}
2.4、定義service層
class GeolocationService {
static let instance = GeolocationService()
private (set) var authorized: Driver<Bool>
private (set) var location: Driver<CLLocationCoordinate2D>
private let locationManager = CLLocationManager()
private init() {
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
// deferred會為每一位訂閱者observer創(chuàng)建一個新的可觀察序列
authorized = Observable.deferred { [weak locationManager] in
let status = CLLocationManager.authorizationStatus()
guard let locationManager = locationManager else {
return Observable.just(status)
}
return locationManager
.rx.didChangeAuthorizationStatus
.startWith(status)
}
.asDriver(onErrorJustReturn: CLAuthorizationStatus.notDetermined)
.map {
switch $0 {
case .authorizedAlways:
return true
default:
return false
}
}
location = locationManager.rx.didUpdateLocations
.asDriver(onErrorJustReturn: [])
.flatMap {
return $0.last.map(Driver.just) ?? Driver.empty()
}
.map { $0.coordinate }
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
這里的service層就是將之前擴展的LocationManager再次封裝溃蔫。值得一提的是健提,這里的authorized是使用deferred創(chuàng)建的。
deferred
deferred會等到有訂閱者的時候再通過工廠方法創(chuàng)建Observable對象伟叛,每個訂閱者訂閱的對象都是內(nèi)容相同而完全獨立的序列私痹。
因此,每次訂閱authorized信息時统刮,都會發(fā)送獨立的序列紊遵,確保每次都會響應。
總結
該Example可以當作封裝Delegate的介紹侥蒙,因此可以總結一下封裝流程如下:
- 創(chuàng)建代理Proxy暗膜,需要繼承繼承DelegateProxy,并且遵守DelegateProxyType協(xié)議
- 定義subject對象鞭衩,即訂閱者(訂閱代理)又是訂閱源(被外部訂閱)
之后的什么擴展学搜,service層就看大家的需要而定制了,但是以上的兩步是必須的论衍。最后瑞佩,有不正確的地方,歡迎指正~