【SwiftChaining】 バインディング処理を構築する関数

[SwiftChaining](https://github.com/objective-audio/SwiftChaining)の解説記事その4です。

[SwiftChaining](https://github.com/objective-audio/SwiftChaining)とは何か？というのは、こちらの[最初の記事](https://qiita.com/yasos/items/4d8dd0d1aa63605dc63b)を参考にしてください。

今までの記事で、`SwiftChaining`で用意しているオブジェクトの`chain()`という関数を呼ぶと、そのあとにオブジェクトの値が変更された時などに実行されるバインディング処理を書ける、ということを紹介しました。

今回はその`chain()`を呼んだ後に書く部分について少し詳しく解説しようと思います。

# Chainとは

監視対象となるオブジェクトの`chain()`関数を呼ぶと`Chain`という型のインスタンスを返します。`Chain`は監視対象のオブジェクトがイベントを送信する時に実行されるバインディング処理を構築するクラスです。

（監視対象となるオブジェクトというのは、`SwiftChaining`的には[`Chainable`](https://qiita.com/yasos/items/bacc276397606bc7dca1#chainable)というプロトコルに適合したオブジェクトということになるのですが、プロトコルに関しては[別の記事](https://qiita.com/yasos/items/bacc276397606bc7dca1)で解説しています。）

`Chain`の持つ関数を呼ぶとまたその返り値に`Chain`を返すという感じで、処理をメソッドチェーンでどんどん追加して繋げて書くことができるようになっています。

`Chain`の関数はいろいろあるのですが、大きく分けると「バインディング処理を構築する」関数と「バインディング処理を確定する」関数があります。例えば以下のようなものです。

### バインディング処理を構築する関数

* do
* map
* guard
* merge
* tuple
* combine
* sendTo

### バインディング処理を確定する関数

* end
* sync

メソッドチェーンで処理をいくつもつなげて書いていけるのは「処理を構築する関数」の方です。今回はこの「処理を構築する関数」について解説します。

# バインディング処理を構築する関数
## do

`do`は、イベントを受け取った時に登録したクロージャの処理を単に実行するものです。クロージャの引数には、受け取ったイベントの値が入ってきます。イベントの値はそのまま次の処理に渡されます。

```swift
let notifier = Notifier<Int>()

let observer = notifier
    .chain()
    .do({ (value: Int) in
        // 何か処理をする
    })
    .end()

// "1"が送信されるので、doのクロージャの引数valueに1が渡され実行される
notifier.notify(value: 1)
```

真面目に書くとこのような感じになるのですが、クロージャの記述は普通に省略して書く事が出来るので、通常は以下のコードくらい省略する方が書きやすいでしょう。

```swift

.do { value in
    // 何か処理をする
}
```

ちなみに、これまでのサンプルコードに出てきた`sendTo`でオブジェクトに値を反映させるというのも、内部的には`do`を使って値をセットしているだけです。何かしらイベントが来た時に、最終的にバインドするような処理を書くのは、基本的に`do`を使うということになります。

また、開発中に「この時点ではどんな値が来ているのか」と調べたい時も、`do`を挟み込んで確認するという使い方もできると思います。

## map

`map`は、受け取ったイベントの値を変化させて次の処理に渡すことができます。Arrayの`map`と同じようなものと思っていただいて良いと思います。

例として`Int`を`String`に変換する場合は、以下のようになります。

```swift
let notifier = Notifier<Int>()

let observer = notifier
    .chain()
    .map({ (intValue: Int) -> String in
        return String(intValue)
    })
    .do({ (strValue: String) in
        // 変換されたStringの値を受け取る
    })
    .end()

notifier.notify(value: 1)
```

こちらもクロージャの記述を省略する方が書きやすいでしょう。最大限まで省略すると以下のようになります。

```swift
.map { String($0) }
```

`map`も`do`と同じくクロージャを実行するものではあるので、何でも書きたい処理は書けるのですが、`map`では単に値を変換をする処理を書くだけに留めておくほうが良いと思います。

## guard

`guard`は、受け取ったイベントをそこで止めることができます。登録したクロージャで`true`を返すとそのまま値を次の処理へ渡しますが、`false`を返すと処理を打ち切ります。

例として、`Int`が奇数の場合だけイベントを通すコードは以下のようになります。

```swift
let notifier = Notifier<Int>()

let observer = notifier
    .chain()
    .guard({ (value: Int) -> Bool in
        return value % 2 != 0
    })
    .do({ value in
        // valueが奇数の時だけ実行される
    })
    .end()
```

`guard`も`map`と同じく関係のない処理を書かずに、単にイベントを通すか通さないかという条件だけを書いたほうが良いと思います。

## merge

`merge`は、2つの同じ型の`Chain`の処理の流れを同じ型のまま1つにまとめます。

```swift
let notifier1 = Notifier<Int>()
let notifier2 = Notifier<Int>()

let chain1 = notifier1.chain()
let chain2 = notifier2.chain()

let observer = chain1
    .merge(chain2)
    .do { value in
        // 両方の値を受け取る
    }
    .end()

// どちらから送ってもdoが実行される
notifier1.notify(value: 1)
notifier2.notify(value: 2)
```

`merge`が使えるのは同じ型の`Chain`同士の場合のみです。型が違う場合は以下に紹介する`tuple`や`combine`を使うことになります。

## tuple

`tuple`は、2つの`Chain`の処理の流れをひとつのタプルにまとめます。

こちらは`merge`と違い別の型でもまとめることができます。その代わり2つのOptionalの要素を持ったタプルに変換されます。

```swift
let chain1 = notifier1.chain()
let chain2 = notifier2.chain()

let observer = chain1
    .tuple(chain2)
    .do { (value1: Int?, value2: String?) in
        // value1かvalue2のどちらかだけ値が入って来る
    }.end()

// doが実行されvalue1に値が渡される
notifier1.notify(value: 1)
// doが実行されvalue2に値が渡される
notifier2.notify(value: "2")
```

`tuple`から出力されるタプルは、2つの要素両方に値が入ることはありません、どちらか一方に値があればもう一方は必ず`nil`になります。2つの処理の流れが混ざらずに並走しているだけとイメージしてもらうと良いかもしれません。

ただ、2つの要素がバラバラに送られてきてもそのままでは扱いにくいと思うので、実際には次に紹介する`combine`を使うことが多くなるかもしれません。

## combine

`combine`は、2つの`Chain`の流れをひとつのタプルにまとめます。`tuple`と違うのは、受け取った値を内部で保存しており、必ず2つの値が揃った状態でタプルのイベントが送信されることです。

ただ、タプルの中身はオプショナルではなく、どちらか一方がイベントを一度も受け取っていなければ`combine`からイベントが送信されることはありません。

```swift
let holder1 = ValueHolder(1)
let holder2 = ValueHolder("2")

let chain1 = holder1.chain()
let chain2 = holder2.chain()

let observer = chain1
    .combine(chain2)
    .do { (value1: Int, value2: String) in
        // 両方の値が揃って実行される
    }
    .sync()
```

上記のコード例のように、`combine`でまとめる送信元が`ValueHolder`のように値を常に送信できる種類のものだと、`sync()`を呼んだ時点で2つともから1度値が送信されるので、以後どちらかだけが変わったら`combine`の後に値が送信されます。

これが`Notifier`のように送信元が値を持たない種類のものだと、敢えて明示的に値を送信しておかないと`combine`での値が揃わないので、注意が必要です。

```swift
let notifier1 = Notifier<Int>()
let notifier2 = Notifier<String>()

let observer = notifier1.chain()
    .combine(notifier2.chain())
    .do { (value1: Int, value2: String) in
        // 両方の値が揃ってから実行される
    }
    .end()

// この時点ではdoは実行されない
notifier1.notify(value: 0)

// この時点でも片方しか値がないのでdoは実行されない
notifier1.notify(value: 1)

// 2つとも値が揃ったのでdoが実行される
notifier2.notify(value: "2")
```

## sendTo

`sendTo`は、引数に渡したオブジェクトにイベントの値を送る関数です。

```swift
let notifier = Notifier<Int>()
let holder = ValueHolder<Int>(0)

// notifierから値を送信したらholderが値を受け取るようにする
let observer = notifier.chain().sendTo(holder).end()

// notifierから値が送信されholderに1がセットされる
notifier.notify(value: 1)
```

具体的にどのようなオブジェクトが受け取れるのかというと、`SwiftChaining`的には[`Receivable`](https://qiita.com/yasos/items/bacc276397606bc7dca1#receivable)というプロトコルに適合したクラスということになります。プロトコルの具体的な説明はまた[別の記事]()でしますが、今まで出てきたクラスでは以下のものが適合しています。

* ValueHolder
* KVOAdapter
* Notifier

`ValueHolder`や`KVOAdapter`は値を保持しているものなので、イベントを受け取ったら値をセットします。`Notifier`はそれ自身が値を保持するものではないので、受け取った値をそのまま送信する感じの動作になります。

また、[前回の記事](https://qiita.com/yasos/items/40852a6674618d4fb3c9)にも書きましたが、`sendTo`で送るイベントの型と受け取るオブジェクトの型がオプショナルの有無の違いだけであれば、`map`などで変換しなくてもそのままつなげることができます。