この記事について
この記事はConquering ReactiveSwift: Signal and Observer (Part 3)の翻訳です。
以下本文です。
ReactiveSwiftを克服する: SignalとObserver (Part 3)
みなさんこんにちは! ReactiveSwiftを克服するシリーズのPart 3へようこそ。前回の記事では、ReactiveSwiftのいろいろな基本要素について議論しました。今回はSignalについて議論します。SignalはSourceカテゴリに属する重要な基本要素です。この記事でSignalを生成してObserverでそれを監視するまでの過程を見ていきましょう。
Signal
関数型リアクティブプログラミング(FRP)では、システムを時間変化する関数群として設計します。したがって、私たちの仕事は時間の経過にしたがってシステムがどう振る舞うかを定義するということになります。その時々の状態に対してシステムの状態を管理する命令型プログラミングとは違い、FRPでは時間軸にそった状態の変化を扱います。この「時間軸にそった変化」という概念をとてもよく表しているのがSignalです。SignalはEventのストリームと定義され、それぞれのEventがその時々の状態を表します。Signalの基本単位がEventです。また、Eventは次の4種類に分類されます:
- Value: 何らかの型を持つ有効な値です。
- Failed: ストリームがエラーで終了したことを表します。
- Completed: ストリームが無地に終了したことを表します。後には他のEventは続きません。
- Interrupted: Eventの生産が中断されたことを表します。
Signalは "Failed/Completed/Interrupted" のいずれかのEventが起こるまではValue型のEventを排出します。一度"Failed/Completed/Interrupted" のいずれかのEventが排出されると、そのあとはValue型のEventは排出されません。
Observer
Signalから排出されるEventを監視するためのものがObserverです。Observerは(Event) -> Void
型のクロージャの薄いラッパーであり、Signalから排出されたEventに対するシステムの振る舞いをカプセル化します。たとえば、Int型の値を排出するSignalを監視したいときは、以下のようにObserverを定義します。
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")
}
}
Observerの生成方法がわかったところで、次はSignalを生成してそれを監視する方法を考えていきましょう。
次の課題を見てください。
50秒の間、5秒に一度の間隔で経過時間を出力しなさい。
FRPでは、システムを時間変化する関数群という観点から設計します。つまり、Signalの観点から設計を考える必要があります。この課題においては、5秒ごとにInt型の値を排出するSignalがまず必要になります。
そのために、パイプを持ち出してきましょう。
パイプを通る水の流れを図に描いてみるとSignalの生成方法がわかりやすいです。パイプの出口にいる受け取り側に対して、入口側からデータを送ります。
ReactiveSwiftでは、Signalは以下のように生成します:
let (output, input) = Signal<Int, NoError>.pipe()
output
の型はSignal<Int, NoError>
で、input
の型はObserver<Int, NoError>
です。input
からoutput
にEventを流し込むことができます。
では、実際にEventをSignalに送ってみましょう。
for i in 0..<10 {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0 * Double(i)) {
input.send(value: i)
}
}
Signalの準備はできたのでそれを監視しましょう。そのためにはObserverのインスタンスが必要です。ドキュメントによると
Observerは(典型的にはSignalからの)Eventを引数にとる関数の薄いラッパーです。
SignalからEventを受け取るにはクロージャを定義する必要があります。クロージャにはEventが届いた時に行うべきタスクを書いておきます。ここでは、さきほど生成したSignalから届くtimeElapsed
を出力するクロージャを定義します。
let signalObserver = Observer<Int, NoError> (
value: { vale in
print("Time elapsed = \(value)")
},
completed: { print("completed") },
interrupted: { print("interrupted") })
output.observe(signalObserver)
まとめるとこうなります。
// Observerを生成する
let signalObserver = Signal<Int, NoError>.Observer(
value: { value in
print("Time elapsed = \(value)")
}, completed: {
print("completed")
}, interrupted: {
print("interrupted")
})
// 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)
}
}
// Signalを監視する
output.observe(signalObserver)
どうやって動いているのか
pipe()
からSignalを生成すると、(output: Signal, input: Observer)
型のタプルが返ってきます。input(型はObserver)はSignalの購読者の代表です。SignalにEventを送りたいときは、send(value: Value)
を呼び出します。これで購読している全てのObserverのsend
が呼ばれ、それぞれのObserverのクロージャが実行されます。
Signalの有効期限
Signalのインスタンスが消えるのは:
- 終了Eventを受け取ったとき
- 監視するObserverがなく、強参照もされていないとき
Observerの有効期間
ObserverがどれだけのあいだSignalを監視するのかをDisposableとLifetimeで定義することができます。
他の監視開始方法について
ReactiveSwiftには他にも監視開始のためのメソッドがあります。特定の型のEventを監視したい時に便利です。
- observeValues
- observeFailed
- observeInterrupted
- observeCompleted
まとめ
おさらいしましょう。Signalを生成してそれを監視するためには3つの簡単なステップを踏みます。
- pipeをつくる
- inputから値を送る
- outputから値を受け取る
サンプルコードはこちらです。次の記事ではSignalProducerを紹介します。
読んでいただきありがとうございます