LoginSignup
9
4

More than 5 years have passed since last update.

ReactiveSwiftを克服する: SignalとObserver (Part 3)

Last updated at Posted at 2018-10-30

この記事について

この記事は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の生成方法がわかりやすいです。パイプの出口にいる受け取り側に対して、入口側からデータを送ります。

reactivePipe.png
訳注: 画像は翻訳元の記事からの引用です

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のインスタンスが消えるのは:

  1. 終了Eventを受け取ったとき
  2. 監視するObserverがなく、強参照もされていないとき

Observerの有効期間

ObserverがどれだけのあいだSignalを監視するのかをDisposableとLifetimeで定義することができます。

他の監視開始方法について

ReactiveSwiftには他にも監視開始のためのメソッドがあります。特定の型のEventを監視したい時に便利です。

  1. observeValues
  2. observeFailed
  3. observeInterrupted
  4. observeCompleted

まとめ

おさらいしましょう。Signalを生成してそれを監視するためには3つの簡単なステップを踏みます。

  1. pipeをつくる
  2. inputから値を送る
  3. outputから値を受け取る

サンプルコードはこちらです。次の記事ではSignalProducerを紹介します。

読んでいただきありがとうございます :smile:

シリーズ内リンク集

  1. ReactiveSwiftを克服する: 導入 (Part 1)
  2. ReactiveSwiftを克服する: 基本要素 (Part 2)
  3. この記事です
  4. ReactiveSwiftを克服する: SignalProducer (Part 4)
  5. ReactiveSwiftを克服する: Property (Part 5)
  6. ReactiveSwiftを克服する: Action (Part 6)
9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4