【SwiftChaining】イベントを送受信するプロトコル

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

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

今までの記事では`SwiftChaining`で用意されたクラスを使用して、バインディングする方法を紹介してきました。

これまでそれら用意されたクラスに関して「監視対象」とか「値を返せる・返せない」といった表現で説明をしていたのですが、実際のところは何のプロトコルに適合しているかという話になります。今回はそのプロトコルについて解説します。

# プロトコル

`SwiftChaining`でイベントの送受信してバインディングできるようにするために用意しているプロトコルがあります。

* Chainable
* Sendable
* Fetchable
* Syncable (Sendable & Fetchable)
* Receivable

独自に作ったクラスでも、これらのプロトコルに適合させることで`SwiftChaining`でバインディング対象として扱うことができます。

# Chainable

クラスを監視対象とするための基本となるプロトコルです。定義は以下のようになっています。

```swift
public protocol Chainable: class {
    // 送信するイベントの型
    associatedtype ChainValue
    // chain()で返す型（あまり気にしなくて良い）
    typealias FirstChain = Chain<ChainValue, Self>
}

extension Chainable {
    // バインディング処理の構築を開始する（実装済。そのまま使う）
    public func chain() -> FirstChain { ... }
    // Observerに保持させるためのオブジェクトを返す（実装済み。そのまま使う）
    public func retain() -> Retainer<Self> { ... }
}
```

`Chainable`だけに適合させても`chain()`が呼べてバインディング処理が書けるだけの状態です。イベントを送信できるようにするためには、以下の`Sendable`や`Fetchable`に適合させる必要があります。

# Sendable

`Sendable`は`Chainable`を継承しており、適合させるとイベントの送信ができます。

定義は以下のような感じです。

```swift
public protocol Sendable: Chainable {
}

extension Sendable {
    public func broadcast(value: ChainValue) { ... }
}
```

実装例として、`Int`を送ることに限定した`Notifier`を書いてみると、このようになります。

```swift
class IntNotifier: Sendable {
    typealias ChainValue = Int
    
    func notify(value: Int) {
        self.broadcast(value: value)
    }
}
```
イベントとして送る値を`ChainValue`で定義し、イベントを送信するときは`broadcast(value:)`を呼びます。必要な実装はこれだけです。

`IntNotifier`は、以下のように使うことができます。

```swift
let notifier = IntNotifier()

// chain()を呼べるので、イベントが送信された時に実行される処理を構築できる
let observer = notifier.chain().do { print($0) }.end()

// 値が送信されてdoが実行され、1がログに出力される
notifier.notify(value: 1)
```
# Fetchable

`Fetchable`も`Chainable`を継承しており、適合させると`sync()`が呼ばれた時に値を引き出して送信することができます。

定義は以下のようになっています。

```swift
public protocol Fetchable: Chainable {
    // 送信する値を返す（要実装）
    func fetchedValue() -> ChainValue
    // 値が返せるかをBoolで返す（必要に応じて実装）
    func canFetch() -> Bool
}

extension Fetchable {
    public func canFetch() -> Bool {
        // デフォルトではtrueを返す
        return true
    }
}
```

`fetchedValue()`を必ず実装する必要があります。返り値に返すのは`sync()`を呼んだ時に送信される値です。

`fetchedValue()`で値を引き出せないことがある場合に`canFetch()`で`false`を返すと`fetchedValue()`は呼ばれません。デフォルトは`true`なので、必要があれば実装をすると良いでしょう。

こちらも実装例として、`Int`を返す`IntFetcher`を書いてみるとこのようになります。

```swift
class IntFetcher: Fetchable {
    typealias ChainValue = Int
    
    var value: Int = 0
    
    func fetchedValue() -> Int {
        return self.value
    }
}
```

以下のように使うことができます。

```swift
let fetcher = IntFetcher()

// sync()で送信され0がログに出力される
let observer = fetcher.chain().do { print($0) }.sync()
```

`sync()`を呼んだタイミングで`fetchedValue()`で返した値がイベントとして送信されます。

ただ、今の所`sync()`を呼んだ時にしか動作しないので、`Fetchable`だけに適合してもあまり使い所がないと思います。実際には`Sendable`と組み合わせて使うことになるでしょう。

# Syncable

`Syncable`は、`Sendable`と`Fetchable`を組み合わせて`typealias`で定義した型です。

```swift
public typealias Syncable = Fetchable & Sendable
```

`Fetchable & Sendable`と直接書いても良いのですが、よく使うので組み合わせたものを作っておいている感じです。

値が変更された時の送信と、監視を開始した時の値の取得を組み合わせていて、値の同期に使えるということで`Syncable`という名前にしています。

例として、`Int`を保持して監視対象とするクラスを作ると以下のようになります。

```swift
class IntHolder: Syncable {
    typealias ChainValue = Int
    
    var value: Int = 0 {
        didSet {
            if self.value != oldValue {
                self.broadcast(value: self.value)
            }
        }
    }
    
    func fetchedValue() -> Int {
        return self.value
    }
}
```

使い方は以下のようになります。

```swift
let holder = IntHolder()

// sync()で送信され0がログに出力される
let observer = holder.chain().do { print($0) }.sync()

// 値が変更されてイベントが送信されたので1が出力される
holder.value = 1
```

# Receivable

`Receivable`に適合したクラスは、`sendTo()`でイベントを受信することができます。

定義は以下の通りです。

```swift
public typealias Receivable = ValueReceivable & ReceiveReferencable

public protocol ValueReceivable: class {
    // 受信するイベントの型
    associatedtype ReceiveValue
    // 受信した時の処理（要実装）
    func receive(value: ReceiveValue)
}

// 内部的な処理に使う
public protocol ReceiveReferencable {
    associatedtype ReceiveObject: ValueReceivable = Self
    
    func reference() -> Reference<ReceiveObject>
}
```

受け取るイベントの型を`ReceiveValue`で定義し、`receive(value:)`を実装する必要があります。イベントで送信されてきた値が`value`に入ってきますので、受け取った時の処理を書いてください。

例として、`Syncable`の例の`IntHolder`をさらに拡張してみたいと思います。

```swift
class IntHolder: Syncable, Receivable {
    typealias ChainValue = Int
    typealias ReceiveValue = Int
    
    var value: Int {
        didSet {
            if self.value != oldValue {
                self.broadcast(value: self.value)
            }
        }
    }
    
    init(_ initial: Int) {
        self.value = initial
    }
    
    func fetchedValue() -> Int {
        return self.value
    }
    
    func receive(value: Int) {
        self.value = value
    }
}
```

以下のように使うことができます。

```swift
let sender = IntHolder(0)
let receiver = IntHolder(1)

// syncで値が同期され、receiverは0になる
let observer = sender.chain().sendTo(receiver).sync()

// 0がログに出力される
print(receiver.value)

// イベントが送信され、receiverは2になる
sender.value = 2

// 2がログに出力される
print(receiver.value)
```