为什么用ReactiveSwift?
先看个例子,假设我们想要实现一个用户界面,其中包含一个UILabel (让我们称之为label )和一个UITextView (让我们称之为textView ),其中UILabel需要实时地显示出在UITextView中输入的文本。 为了实现这种需求,我们一般会监听textView的代理方法:
func textViewDidChange(_ textView: UITextView) {
label.text = textView.text
}
而在ReactiveSwift,我们可以很优雅地让label的text跟textView的text实时保持一致:
label.reactive.text <~ textView.reactive.continuousTextValues
简简单单地通过操作符<〜
,就将label的text跟textView的text进行了绑定,label的文本会在label的整个生命周期中与textView的文本等同。
<〜
称为操作符(binding operator),操作符的左侧是绑定目标(binding target) ,右侧是绑定源(binding source)。
再来看一个例子:
usernameTextField.reactive.continuousTextValues.observeValues {
text in
print(text ?? "")
}
随着username text field输入,你在Xcode控制台会看到以下输出:
e
ee
eef
eeff
eefff
eeffff
eefffff
eeffffff
每次你在username text field输入,控制台都会输出,而不用设置target-action,也不用设置委托(delegate)。
ReactiveCocoa信号会发送一系列的事件给订阅者(观察者)。ReactiveCocoa有以下事件:
- Value事件:Value事件提供了新值
- Failed事件:Failed事件表明在信号完成之前发生了错误
- Completed事件:Completed事件信号完成了,之后不会再有新的值发送
- Interrupted事件:Interrupted事件表明由于被取消,信号被终止了
usernameTextField.reactive就是把usernameTextField变成可响应的,而continuousTextValues就是text值的信号。通过observeValues,我们可以观察到continuousTextValues这个信号传来的Value事件,每次在usernameTextField输入的时候,我们就会接收到text的值。
如何创建一个Signal并通过Observer观察它呢?
- 信号Signal
信号被定义为事件流,其中每个事件表示在指定时间点上的一种状态。如我们在文本框输入的时候,它会不断地产生值类型的事件。事件event是信号的基本单位,它是一个枚举:
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发出的事件。Observer封装了一个闭包(Event) -> Void,我们可以在闭包中根据event类型进行相应的处理:
let observer = Signal.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")
}
}
现在,我们清楚如何创建一个Observer,接下来通过一个例子看看如何创建信号及观察信号。
要求:每隔五秒钟打印一次,持续五十秒。
信号的创建可以看作是流过管道的水流,Observer在源端不断地发送数据,将事件注入到输出端Signal,然后我们在通过跟Signal绑定一起的Observer实例观察到Signal中的value,其实整个过程就这么简单,so easy!
首先,创建信号:
let (output, input) = Signal.pipe()
通过pipe()
方法,创建一个‘管道’,这个‘管道’其实是个元组,元素output代表输出端,类型为Signal
现在我们向信号发送值:
for i in 0..<10 {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0 * Double(i)) {
input.send(value: i)
}
}
好了,信号有了,我们还需要一个Observer实例来观察信号中的事件,上面我说过,Observer封装了event的处理逻辑,现在我们需要把前面发送的值打印出来,所以我们创建了这样的一个Observer:
let signalObserver = Signal.Observer (
value: { value in
print("Time elapsed = \(value)")
},
completed: { print("completed") },
interrupted: { print("interrupted") })
output.observe(signalObserver)
完整的代码如下:
//Create an observer
let signalObserver = Signal.Observer(
value: { value in
print("Time elapsed = \(value)")
}, completed: {
print("completed")
}, interrupted: {
print("interrupted")
})
//Create an a signal
let (output, input) = Signal.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)
再来看个例子:
当文本框输入字符超过10个时,启用按钮。
第1步:创建信号
let signal = textField.reactive.continuousTextValues
这里,在文本textfield键入的文本通过信号textField.reactive.continuousTextValues表示。
第2步:转换信号
在第一步中创建的信号会发出可选字符串。 我们可以通过运算符map
将其转换成发出布尔值的信号。
let transformedSignal = signal
.map {text in
text ?? “”}
.map {text in
text.characters.count> 10
}
第3步:观察信号
接下来,我们将观察信号,并将设置按钮的isEnabled的操作封装到Observer中。
let observer = Signal.Observer(value: { value in
button.isEnabled = value
})
let disposable = transformedSignal.observe(observer)
第4步:停止观察信号
disposable?.dispose()
当然,最好的做法是在类的deinit中释放所有观察者。
以上整个过程是:
//Defining consumer
let observer = Signal.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()
小结:
让我们回顾一下,我们需要三个简单的步骤来创建一个信号并观察它:
- 创建一个pipe管道,同时创建了一个输入端Observer,一个输出端Signal
- 创建观察者,订阅Signal。
- 通过输入端Observer发送值给Signal,这将触发所有订阅了该信号的观察者执行与观察者关联的闭包。