為什么用ReactiveSwift?
先看個(gè)例子呈础,假設(shè)我們想要實(shí)現(xiàn)一個(gè)用戶界面舆驶,其中包含一個(gè)UILabel (讓我們稱之為label )和一個(gè)UITextView (讓我們稱之為textView )橱健,其中UILabel需要實(shí)時(shí)地顯示出在UITextView中輸入的文本。 為了實(shí)現(xiàn)這種需求贞远,我們一般會監(jiān)聽textView的代理方法:
func textViewDidChange(_ textView: UITextView) {
label.text = textView.text
}
而在ReactiveSwift畴博,我們可以很優(yōu)雅地讓label的text跟textView的text實(shí)時(shí)保持一致:
label.reactive.text <~ textView.reactive.continuousTextValues
簡簡單單地通過操作符<?
,就將label的text跟textView的text進(jìn)行了綁定蓝仲,label的文本會在label的整個(gè)生命周期中與textView的文本等同。
<?
稱為操作符(binding operator)官疲,操作符的左側(cè)是綁定目標(biāo)(binding target) 袱结,右側(cè)是綁定源(binding source)。
再來看一個(gè)例子:
usernameTextField.reactive.continuousTextValues.observeValues {
text in
print(text ?? "")
}
隨著username text field輸入途凫,你在Xcode控制臺會看到以下輸出:
e
ee
eef
eeff
eefff
eeffff
eefffff
eeffffff
每次你在username text field輸入垢夹,控制臺都會輸出,而不用設(shè)置target-action维费,也不用設(shè)置委托(delegate)果元。
ReactiveCocoa信號會發(fā)送一系列的事件給訂閱者(觀察者)。ReactiveCocoa有以下事件:
- Value事件:Value事件提供了新值
- Failed事件:Failed事件表明在信號完成之前發(fā)生了錯誤
- Completed事件:Completed事件信號完成了犀盟,之后不會再有新的值發(fā)送
- Interrupted事件:Interrupted事件表明由于被取消而晒,信號被終止了
usernameTextField.reactive就是把usernameTextField變成可響應(yīng)的,而continuousTextValues就是text值的信號阅畴。通過observeValues倡怎,我們可以觀察到continuousTextValues這個(gè)信號傳來的Value事件,每次在usernameTextField輸入的時(shí)候贱枣,我們就會接收到text的值监署。
如何創(chuàng)建一個(gè)Signal并通過Observer觀察它呢?
- 信號Signal
信號被定義為事件流纽哥,其中每個(gè)事件表示在指定時(shí)間點(diǎn)上的一種狀態(tài)钠乏。如我們在文本框輸入的時(shí)候,它會不斷地產(chǎn)生值類型的事件春塌。事件event是信號的基本單位晓避,它是一個(gè)枚舉:
public enum Event {
/// A value provided by the signal.
case value(Value)
/// The signal terminated because of an error. No further events will be
/// received.
case failed(Error)
/// The signal successfully terminated. No further events will be received.
case completed
/// Event production on the signal has been interrupted. No further events
/// will be received.
///
/// - important: This event does not signify the successful or failed
/// completion of the signal.
case interrupted
}
- 觀察者Observer
在ReactiveSwift中我們可以通過Observer觀察Signal發(fā)出的事件。Observer封裝了一個(gè)閉包(Event) -> Void摔笤,我們可以在閉包中根據(jù)event類型進(jìn)行相應(yīng)的處理:
let observer = Signal<Int, NoError>.Observer { (event) in
switch event {
case let .value(v):
print("value = \(v)")
case let .failed(error):
print("error = \(error)")
case .completed:
print("completed")
case .interrupted:
print("interrupted")
}
}
現(xiàn)在够滑,我們清楚如何創(chuàng)建一個(gè)Observer,接下來通過一個(gè)例子看看如何創(chuàng)建信號及觀察信號吕世。
要求:每隔五秒鐘打印一次彰触,持續(xù)五十秒。
信號的創(chuàng)建可以看作是流過管道的水流命辖,Observer在源端不斷地發(fā)送數(shù)據(jù)况毅,將事件注入到輸出端Signal分蓖,然后我們在通過跟Signal綁定一起的Observer實(shí)例觀察到Signal中的value,其實(shí)整個(gè)過程就這么簡單尔许,so easy么鹤!
首先,創(chuàng)建信號:
let (output, input) = Signal<Int, NoError>.pipe()
通過pipe()
方法味廊,創(chuàng)建一個(gè)‘管道’蒸甜,這個(gè)‘管道’其實(shí)是個(gè)元組,元素output代表輸出端余佛,類型為Signal <Int柠新,NoError> , 元素input代表輸入端辉巡,類型為Observer <Int恨憎,NoError>,作為input的Observer發(fā)送數(shù)據(jù)郊楣,將事件注入output的Signal憔恳。
現(xiàn)在我們向信號發(fā)送值:
for i in 0..<10 {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0 * Double(i)) {
input.send(value: i)
}
}
好了,信號有了净蚤,我們還需要一個(gè)Observer實(shí)例來觀察信號中的事件钥组,上面我說過,Observer封裝了event的處理邏輯塞栅,現(xiàn)在我們需要把前面發(fā)送的值打印出來者铜,所以我們創(chuàng)建了這樣的一個(gè)Observer:
let signalObserver = Signal<Int, NoError>.Observer (
value: { value in
print("Time elapsed = \(value)")
},
completed: { print("completed") },
interrupted: { print("interrupted") })
output.observe(signalObserver)
完整的代碼如下:
//Create an observer
let signalObserver = Signal<Int, NoError>.Observer(
value: { value in
print("Time elapsed = \(value)")
}, completed: {
print("completed")
}, interrupted: {
print("interrupted")
})
//Create an a signal
let (output, input) = Signal<Int, NoError>.pipe()
//Send value to signal
for i in 0..<10 {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0 * Double(i)) {
input.send(value: i)
}
}
//Observe the signal
output.observe(signalObserver)
再來看個(gè)例子:
當(dāng)文本框輸入字符超過10個(gè)時(shí),啟用按鈕放椰。
第1步:創(chuàng)建信號
let signal = textField.reactive.continuousTextValues
這里作烟,在文本textfield鍵入的文本通過信號textField.reactive.continuousTextValues表示。
第2步:轉(zhuǎn)換信號
在第一步中創(chuàng)建的信號會發(fā)出可選字符串砾医。 我們可以通過運(yùn)算符map
將其轉(zhuǎn)換成發(fā)出布爾值的信號拿撩。
let transformedSignal = signal
.map {text in
text ?? “”}
.map {text in
text.characters.count> 10
}
第3步:觀察信號
接下來,我們將觀察信號如蚜,并將設(shè)置按鈕的isEnabled的操作封裝到Observer中压恒。
let observer = Signal<Bool, NoError>.Observer(value: { value in
button.isEnabled = value
})
let disposable = transformedSignal.observe(observer)
第4步:停止觀察信號
disposable?.dispose()
當(dāng)然,最好的做法是在類的deinit中釋放所有觀察者错邦。
以上整個(gè)過程是:
//Defining consumer
let observer = Signal<Bool, NoError>.Observer(value: { value in
button.isEnabled = value
})
//Defining source
let signal = textField.reactive.continuousTextValues
let transformedSignal = signal
.map { text in
text ?? "" }
.map { text in
text.characters.count > 10
}
//Consuming signal
let disposable = transformedSignal.observe(observer)
//Limit Scope
disposable?.dispose()
小結(jié):
讓我們回顧一下探赫,我們需要三個(gè)簡單的步驟來創(chuàng)建一個(gè)信號并觀察它:
- 創(chuàng)建一個(gè)pipe管道,同時(shí)創(chuàng)建了一個(gè)輸入端Observer撬呢,一個(gè)輸出端Signal
- 創(chuàng)建觀察者伦吠,訂閱Signal。
- 通過輸入端Observer發(fā)送值給Signal,這將觸發(fā)所有訂閱了該信號的觀察者執(zhí)行與觀察者關(guān)聯(lián)的閉包毛仪。