ReactiveSwiftを克服する: Property (Part 5)


この記事について

この記事はConquering ReactiveSwift: Property (Part 5)の翻訳です。

以下本文です。

ReactiveSwiftを克服する: Property (Part 5)

ReactiveSwiftを克服するシリーズのパート5へようこそ。前回の記事では、SignalProducerを生成、start、監視する方法を学びました。この記事では、PropertyとMutablePropertyという考え方について議論していきましょう。

定義

Propertyは監視可能なコンテナで、値が変わるたびに値を排出します。PropertyProtocolに準拠していて、PropertyProtocolは次のようなプロパティを持ちます:

  • value: 現在の値を表します。
  • producer: 現在の値と、それに続く値を送るSignalProducerです。Propertyが破棄されたり、これ以上変更がない場合は、SignalProducerも完了し、それ以降値を送りません。
  • signal: 以降に続く値を送るSignalです。現在の値は送りません。Propertyが破棄されたり、これ以上変更がない場合は、SignalProducerも完了し、それ以降値を送りません。

なぜ便利なのか?

値だけを扱い、エラーのことは考えない場合に便利です。
前回の記事の例を考えてみましょう。

50秒の間、5秒に一度の間隔で経過時間を出力しなさい。

これに対して私たちは次のようなSignalProducerを用意しました。

let signalProducer: SignalProducer<Int, NoError> = SignalProducer { (observer, lifetime) in
    let now = DispatchTime.now()
    for index in 0..<10 {
        let timeElapsed = index * 5
        DispatchQueue.main.asyncAfter(deadline: now + Double(timeElapsed)) {
            guard !lifetime.hasEnded else {
                observer.sendInterrupted()
                return
            }
            observer.send(value: timeElapsed)
            if index == 9 {
                observer.sendCompleted()
            }
        }
    }
}

ここのエラー型にあたるNoErrorの意味は、エラーの処理が必要がないことを表していることに注意してください。このような例は、Propertyの良い使いどころです。

それでは、Propertyを定義してみましょう。

let property = Property(initial: 0, then: signalProducer)

ここで、初期値は0でそれに続く値がsignalProducerによって返される値です。

さきほど書いた通り、Propertyにはsignalproducerというプロパティがあり、両方とも監視することができます。しかし、両者の重要な違いは、signalは現在値を排出せず、それに続く値のみを排出することです。

property.producer.startWithValues { value in
    print("[Observing SignalProducer] Time elapsed = \(value)")
}

/* 出力:
[Observing SignalProducer] Time elapsed = 0
[Observing SignalProducer] Time elapsed = 0
[Observing SignalProducer] Time elapsed = 5
[Observing SignalProducer] Time elapsed = 10
[Observing SignalProducer] Time elapsed = 15
[Observing SignalProducer] Time elapsed = 20
[Observing SignalProducer] Time elapsed = 25
[Observing SignalProducer] Time elapsed = 30
[Observing SignalProducer] Time elapsed = 45
[Observing SignalProducer] Time elapsed = 35
[Observing SignalProducer] Time elapsed = 40
*/
property.signal.observeValues { value in
    print("[Observing Signal] Time elapsed = \(value)")
}

/* 出力
[Observing Signal] Time elapsed = 0
[Observing Signal] Time elapsed = 5
[Observing Signal] Time elapsed = 10
[Observing Signal] Time elapsed = 15
[Observing Signal] Time elapsed = 20
[Observing Signal] Time elapsed = 25
[Observing Signal] Time elapsed = 30
[Observing Signal] Time elapsed = 35
[Observing Signal] Time elapsed = 40
[Observing Signal] Time elapsed = 45
*/

この例では、SignalProducerからの値を受け取るPropertyを生成しました。Signalからの値を受け取るPropertyも生成することも可能です。

MutableProperty

MutablePropertyは、Propertyと同じく、監視可能なコンテナです。しかし、値を直接変更することができる点が異なります。PropertyProtcolに準拠しているのはPropertyと同じです。

MutablePropertyは初期値だけで初期化することができます:

let mutableProperty = MutableProperty(1)

値の変更はこうします:

mutableProperty.value = 3

MutablePropertyのsignalproducerはPropertyと同じように監視可能です。

MutablePropertyの便利な使い方はバインディングです。MutablePropertyを使うと以下のように書けます:

mutableProperty <~ signalProducer

このコードだと、mutablePropertyvaluesignalProducerによって決まります。

まとめ

この記事でPropertyとMutablePropertyについてわかってもらえたら嬉しいです。サンプルコードはこちらにあります。次回の記事では、Signalの高度な制御を可能にしてくれるActionについて紹介します。

シリーズ内リンク集

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