【SwiftChaining】 Swiftでデータバインディングするライブラリを作った

[SwiftChaining](https://github.com/objective-audio/SwiftChaining)というデータバインディングのライブラリを作ってみました。それなりに使い物になってきた気がするので、どのようなものか紹介しようと思います。

まだ破壊的な変更は加わる可能性はありますが、その点はご了承ください。Qiitaの記事はライブラリの変更に合わせて随時更新をしていく予定です。

# SwiftChainingとは？
https://github.com/objective-audio/SwiftChaining

Swiftで書かれたデータバインディングをするライブラリです。監視対象となるオブジェクトからメソッドチェーンで処理をつなげて書いて、別のオブジェクトに値に反映させるということができます。

CocoaPodsでインストールできます。モジュール名はSwiftを含まず`Chaining`です。

```ruby
pod 'Chaining'
```

# なぜ作ったのか？

iOS開発でデータバインディングできるライブラリは何かないかな？と探してみると、だいたいRxSwiftのようなReactive Extensionsをルーツとしたライブラリに行き当たります。でも、個人的には非同期処理を簡単に書きたいわけではなく、単にバインディングを便利にしたいだけなので、それにしてはちょっと複雑すぎる印象を受けます。

とはいえ、ライブラリを使わないと何かしらの通知を受け取るのにNotificationCenterやKVO、デリゲートなどいろいろ書き方も違うこともありますし、完璧に値を同期させるのも大変です。

そこで、統一的な書き方でメソッドチェーンを使って値を伝播させることできるものがあれば十分だろうと考え、自分でライブラリを作りました。

# 簡単な使い方
どのような感じで使えるものなのか簡単なコードをお見せします。

## 値を同期してみる
2箇所にある値をバインディングして同期してみたいと思います。以下のようなコードになります。

```swift
import UIKit
import Chaining

let a = ValueHolder<Int>(0)
let b = ValueHolder<Int>(1)

// aは0、bは1

let observer: AnyObserver = a.chain().sendTo(b).sync()

// aとbは両方0

a.value = 2

// aとbは両方2

observer.invalidate()
a.value = 3

// aは3で、bは2のまま
```

### 解説

バインディングするための値を扱うのに`ValueHolder`というクラスを用意しています。`ValueHolder`は、値を保持していて、監視する方にも監視される方にもなるクラスです。ジェネリクスで保持する型を指定できます。

```swift
let a = ValueHolder<Int>(0)
let b = ValueHolder<Int>(1) // <Int>は省略してValueHolder(1)という書き方もできる

// aは0、bは1を保持している
print("\(a.value) / \(b.value)") // -> "0 / 1"
```
`Int`を保持する`ValueHolder`を2つの生成しました。この時点ではそれぞれ`0`と`1`という別の値を持っています。

`chain()`という関数を呼ぶと、`ValueHolder`の持つ値が変更された時に呼ばれるバインディング処理をメソッドチェーンで記述していくことができます。

```swift
let observer: AnyObserver = a.chain().sendTo(b).sync()

// aからbに値が同期され両方0になっている
print("\(a.value) / \(b.value)") // -> "0 / 0"
```

今回は`a`の持つ値を何もせずそのまま`b`に送りたいので、`sendTo()`という関数に`b`を渡します。最後に`sync()`を呼ぶと処理の流れが確定し、1度`a`から値が送信された上で`AnyObserver`という型のObserverオブジェクトを返します。

Observerは`chain()`の後に書かれたバインディング処理の一連の流れを保持していて、生存している間は`a`の変更を監視し続けます。`a`の値を変更すれば`b`に値が反映されます。

```swift
a.value = 2

// holder1の変更がholder2に反映され両方2になっている
print("\(a.value) / \(b.value)") // -> "2 / 2"
```

Observerを破棄するか、あるいは`invalidate()`関数を呼ぶことで監視が止まります。

```swift
observer.invalidate()
a.value = 3

// 監視は終わったのでbは2のまま
print("\(a.value) / \(b.value)") // -> "3 / 2"
```

## 単に値を送ってみる

値と値をバインディングをするという感じではなく、`NotificationCenter`のように単に値を一方的に送ることもできるので書いてみます。以下のようなコードになります。

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

let observer = notifier.chain().do { value in print(value) }.end()

notifier.notify(value: 1)
```

### 解説

単に値を送信するクラスとして`Notifier`を用意しています。送る値の型をジェネリクスで指定できます。

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

こちらも同じく`chain()`メソッドでバインディング処理の記述を始めます。

```swift
let observer = notifier.chain().do { value in print(value) }.end()
```

`Notifier`は`ValueHolder`と違って値を保持しているわけではないので、`sync()`ではなく`end()`という関数で処理の流れを確定させます。`end`を呼んだ時点では何も送る値はないので、何も実行されません。

`chain()`の次に`do`関数がありますが、これは単にクロージャで処理を差し込むものです。

`Notifier`は`notify(value:)`関数で値を送信することができます。

```swift
notifier.notify(value: 1) // -> "1"
```

値が送信されると`do`の処理が実行され、この場合`1`がログに出力されます。

この`Notifier`の例では`do`関数で値を受け取るようにしましたが、`sendTo()`で`ValueHolder`が受け取ることもできます。それらを繋げて書いて、2つ以上の処理を続けて行うこともできます。

以上が簡単な使い方です。

この記事に書いたもの以外にも色々とクラスとか機能はあるのですが、それは以下の記事を参照してください。

# 関連記事一覧

[【SwiftChaining】 UIとバインディングする](https://qiita.com/yasos/items/f2d8fc774d39214ebd71)
[【SwiftChaining】 NotificationCenterからの通知を受け取る](https://qiita.com/yasos/items/40852a6674618d4fb3c9)
[【SwiftChaining】 処理を構築する関数](https://qiita.com/yasos/items/ff4486908684038bdc5c)
[【SwiftChaining】 処理を確定する関数](https://qiita.com/yasos/items/4fe4a4f01499c7ec9665)
[【SwiftChaining】 イベントを送受信するプロトコル](https://qiita.com/yasos/items/bacc276397606bc7dca1)
[【SwiftChaining】 オブジェクトの保持](https://qiita.com/yasos/items/ed03468fa6c1510b6d38)