ReactorKit と apollo-ios を少し効率的に併用する方法

## はじめに

[iOS Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ios)、13日目です。

個人開発のアプリを改修しようと思い、最近気に入ってる設計のFluxを取り入れつつ、GraphQL API を使用したいことから、 [ReactorKit](https://github.com/ReactorKit/ReactorKit) と [apollo-ios](https://github.com/apollographql/apollo-ios) を使って開発をしています。

少し、ニッチな話になってしまいますが、`ReactorKit` と `apollo-ios` を少し効率的に併用する方法について考えたことを紹介します。

## ReactorKit と `distinctUntilChanged()`

ReactorKitでは、`state`の一部が変更されただけでも、`state`全部が流れてくるので、適切に`distinctUntilChanged()`を呼ぶ必要があります。

```swift
reactor.state.map { $0.elements }
    .distinctUntilChanged()
    .subscribe(onNext: { _ in
         //
    })
    .disposed(by: disposeBag)
```

`distinctUntilChanged()`を呼ぶためには流れてくる要素が`Equatable`に準拠している必要があります。

流れてくる要素が自分で用意した構造体なのであれば、

```swift
struct Element: Equatable {
   //
}
```
↑のようにするだけで, Swift4.1からは暗黙的に`==`が実装されます。

## apollo-ios と Equatable

しかし、今回は、`apollo-ios`によって自動で生成された構造体が流れてくる場合がほとんどでした。

```swift
extension GeneratedObject: Equatable {
    public static func == (lhs: Element, rhs: Element) -> Bool {
        //
    }
}
```

`extension`で`Equatable`を適応させるときには、暗黙的に`==`が実装されないので、毎回自分で実装を書かなくてはなりません。
毎回、`Equatable`を意識しなければいけないのは、少し面倒くさいのでもうちょっと効率よくしていきたいところです。

`apollo-ios`によって生成された構造体は、`GraphQLSelectionSet`に準拠しています。
`GraphQLSelectionSet`は 通信で取得した情報を格納したDictionary型の`resultMap` を持っているので、これを比較すれば値に変更があったかどうか判断することができそうです。

つまり、次のように書くことができれば、解決できそうです。

```swift
extension GraphQLSelectionSet: Equatable {
    public static func == (lhs: Element, rhs: Element) -> Bool {
        return lhs.resultMap == rhs.resultMap
    }
}
```
しかし、`GraphQLSelectionSet` はProtocolなので、このように書くことはできません。

## `GraphQLSelectionSet` を `distinctUntilChanged()` に対応させる

そこで`ObservableType`の中身が`GraphQLSelectionSet`のときにのみ適応される`distinctUntilChanged()`を作ってしまう手があります。

```swift
extension ObservableType where E: GraphQLSelectionSet {
    func distinctUntilChanged() -> Observable<Self.E> {
        return distinctUntilChanged { $0.resultMap == $1.resultMap }
    }
}
```
これによって、中身が何であっても`apollo-ios`によって自動生成された構造体であれば`distinctUntilChanged()`を呼ぶことが可能になりました。

## `[GraphQLSelectionSet]` の対応

しかし、これですべて解決かと思いきや、これだけで終わると、`[GraphQLSelectionSet]`に対応できていません。

先ほどと同じように、`[GraphQLSelectionSet]` 用の `distinctUntilChanged()` を作成すれば一見解決するように思えますが、結局`[[GraphQLSelectionSet]]` のような場合に、また同じ問題にぶつかってしまいます。

`Equatable`の場合は、Swift4.2から、要素が`Equatble`に準拠しているときは`[Equtable]`も`Equatble`に準拠するようになりました。([Conditional conformances](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md))

少し強引な気もしますが、同じように要素が`GraphQLSelectionSet`の場合、`[GraphQLSelectionSet]`も`GraphQLSelectionSet`に準拠するようにすれば、配列であっても対応することができそうです。

```swift
extension Array: GraphQLSelectionSet where Element: GraphQLSelectionSet {
    public init(unsafeResultMap: ResultMap) {
        let resultMaps = unsafeResultMap["resultMaps"] as! [ResultMap]
        let sets = resultMaps.map(Element.init)
        self = sets
    }
    
    public static var selections: [GraphQLSelection] {
        return []
    }
    
    public var resultMap: ResultMap {
        return ["resultMaps": self.map { $0.resultMap }]
    }
}
```

これによって、`apollo-ios`によって生成された構造体を`distinctUntilChanged()`したいときに、毎回`Equatable`を気にする必要がなくなりました！

# 最後に
相性が微妙なのであれば最初から`ReactorKit` を使わなければいいのでは.. という思いもありましたが、自分で`Flux`の機構を作ろうとすると色々と大変なことがありました。

もっと良さそうな解決方法がありましたら、ぜひ教えてください。