我們一路上使用各種Rx版的UIKit,其實都是透過Extension,今天就想來聊聊,到底要如何自訂Rx Extensions。
Binder
是一種Observer,它有幾個特徵
.error
,只處理.next
我們以UIControl為例,打開UIControl+Rx.swift
,第一段就是對isEnable
做封裝
public var isEnabled: Binder<Bool> {
return Binder(self.base) { control, value in
control.isEnabled = value
}
}
Binder
在初始化時會需要給定一個closure,參數包含兩個,第一個是Target,也就是Base,以上範例就是UIControl,第二參數是Value
既然是Observer,只用起來會像這樣
Observable.of(true).bind(to: button.rx.isEnabled).disposed(by: disposeBag)
ControlEvent
是一種Observable的變化型,它有幾個特徵
Observable
.subscribeOn(MainScheduler.instance)
.observeOn(MainScheduler.instance)
就以UIButton作為例子,打開UIButton+Rx.swift
,第一段就是對於.touchUpInside
事件的封裝
extension Reactive where Base: UIButton {
/// Reactive wrapper for `TouchUpInside` control event.
public var tap: ControlEvent<Void> {
return controlEvent(.touchUpInside)
}
}
其中Base就是代表我們要擴展的物件
// Reactive.swift
public struct Reactive<Base> {
...
}
既然ControlEvent
是一種Observable的變化型,使用起來就可以像這樣串
button.rx.tap.bind(to: observer).disposed(by: disposeBag)
ControlProperty
可以是Observable,也可以是Observer,有幾個特徵
.error
shareRelay(1)
就以UIButton作為例子,打開UISwitch+Rx.swift
,第一段就是對isOn
的封裝
// UISwitch+Rx.swift
public var isOn: ControlProperty<Bool> {
return value
}
public var value: ControlProperty<Bool> {
return base.rx.controlPropertyWithDefaultEvents(
getter: { uiSwitch in
uiSwitch.isOn
}, setter: { uiSwitch, value in
uiSwitch.isOn = value
}
)
}
其中controlPropertyWithDefaultEvents
是個重點,它位於『UIControl+Rx.swift』,當.valueChanged
時,執行getter或setter
// UIControl+Rx.swift
internal func controlPropertyWithDefaultEvents<T>(
editingEvents: UIControl.Event = [.allEditingEvents, .valueChanged],
getter: @escaping (Base) -> T,
setter: @escaping (Base, T) -> Void
) -> ControlProperty<T> {
return controlProperty(
editingEvents: editingEvents,
getter: getter,
setter: setter
)
}
因為被定義為internal,所以要擴充自訂的class時,無法直接參照上面程式碼,可以改成下面這樣
extension Reactive where Base: RadioButton {
var isOn: ControlProperty<Bool> {
return value
}
var value: ControlProperty<Bool> {
return controlProperty(
editingEvents: [.allEditingEvents, .valueChanged],
getter: { radioButton in
radioButton.isOn
},
setter: { radioButton, value in
radioButton.isOn = value
}
)
}
}
這部分就是第 4 天 - Observable (下)提過的Observable.create
去實現,在『URLSession+Rx.swift』中可以看到範例
public func response(request: URLRequest) -> Observable<(response: HTTPURLResponse, data: Data)> {
return Observable.create { observer in
...
let task = self.base.dataTask(with: request) { data, response, error in
...
observer.on(.next((httpResponse, data)))
observer.on(.completed)
}
task.resume()
return Disposables.create(with: task.cancel)
}
}
這部分是從RxSwift: Reactive Programming with Swift書中章節所介紹,我覺得之後可能很有用,於是我將其簡化再簡化做個分享。假如在有一全域變數Cache
,我能在任何為Observable<Int>
的Observable使用此storeCache
操作,將其存到Cache
變數中,我可以這樣寫
var Cache: Int = 0
extension ObservableType where Element == Int {
func storeCache() -> Observable<Element> {
return self.do(onNext: {
Cache = $0
})
}
}
用起來就會像這樣
Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
.storeCache()
.subscribe(onNext: { _ in
print("Cache: \(Cache)")
})
.disposed(by: disposeBag)
倒數最後一週~明天是接續Reactive Extensions的延伸討論,明天見