IGListKitとRxSwiftを掛け合わせてみた🤔

# IGListKitとRxSwiftを掛け合わせてみた🤔

去年のTry Swift New Yorkで発表があったInstagramのFeedの実装に利用されている[IGListKit](https://github.com/Instagram/IGListKit)をRxSwiftで`UICollectionView`や`UITableView`を扱うように実装できるようにしてみました。(Try Swiftでの発表は[こちら](https://realm.io/news/tryswift-ryan-nystrom-refactoring-at-scale-lessons-learned-rewriting-instagram-feed/))

現状で実装できているのはIGListKitの中でも`IGListAdapterDataSource`のみで他のDataSourceやDelegateは実装できていません！良い方法を探しつつ更新していけたらと思います！

このプロジェクトはGithubに公開されているのでクローンして動かしてみてください！
-> https://github.com/yuzushioh/RxIGListKit


## Background

RxSwiftを使っているプロジェクトでIGListKitを使うとどうしても、リストに表示する`Element`の更新と、リストを更新するメソッドを呼ぶタイミングを、`subscribe`する順番などを変えて考慮しないといけません！

もしリストに表示するElementの更新より先にリスト自体を更新してしまうと、バグやクラッシュに繋がります。

またリストに表示する`Element`は`Observable`であってはならないので、ViewModel側にプロパティーが１つ増えてしまい、このプロパティーも毎回更新する必要がでてきます。

👇👇👇👇👇👇

これを解決するために`Observable<Element>`をそのまま`DataSource`に繋ぎこめるようにしたかった🙏🏻

## Example 

今回は実際にIGListKitとRxSwiftを使って以下のUIを実装してみた。
プロジェクトはこの[レポジトリー](https://github.com/yuzushioh/RxIGListKit)に公開しています。


<img src="https://cloud.githubusercontent.com/assets/12425729/24835650/304e1cfe-1d42-11e7-8489-3f8b8c16d4c6.PNG" width="340"> | <img src="https://cloud.githubusercontent.com/assets/12425729/24835665/84d55f44-1d42-11e7-8666-408cda9f536a.PNG" width="340">

## About RxIGListKit

RxSwiftで`UITableView`や`UICollectionView`には`items(dataSource: _)`メソッドが用意されていて、DataSourceを定義し、その中に指定した`Element`のObservableをバインドするだけで更新ができます。

今回はIGListAdapterに`items(dataSource: _)`を実装しました🚀

- Step 1

`UITableView`や`UICollectionView`と同様に`RxIGListAdapterDataSource` protocolを定義する

```swift
protocol RxIGListAdapterDataSource {

    associatedtype Element

    func listAdapter(_ adapter: IGListAdapter, observedEvent: Event<Element>) -> Void
}
```

- Step 2

IGListAdapterにReactive Extensionで`items(dataSource: _)`を追加! (サクッと実装なのでベストかはわかりません)

```swift
func items<DataSource: RxIGListAdapterDataSource & IGListAdapterDataSource,
           O: ObservableType>(dataSource: DataSource)
        -> (_ source: O)
        -> Disposable where DataSource.Element == O.E {
            
        return { source in
            let subscription = source
                .subscribe { dataSource.listAdapter(self.base, observedEvent: $0) }
            
            return Disposables.create {
                subscription.dispose()
            }
        }
}
```

- Step 3

あとはDataSourceを定義して繋ぎこむだけです👍

## How to use

DataSourceを定義し、その中の`Element`と同じ型のストリームを繋ぐだけでIGListKitで実装されたリストを更新することができます。

```swift

lazy privatevar adapter: IGListAdapter = {
    return IGListAdapter(updater: IGListAdapterUpdater(), viewController: self, workingRangeSize: 2)
}()

private let disposeBag = DisposeBag()
private let dataSource = DataSource()

override func viewDidLoad() {
    super.viewDidLoad()
    
    adapter.rx.setDataSource(dataSource)
        .disposed(by: disposeBag)

    viewModel.feeds
        .drive(adapter.rx.items(dataSource: dataSource))
        .disposed(by: disposeBag)
}
```

```swift
final class DataSource: NSObject, IGListAdapterDataSource, RxIGListAdapterDataSource {
    typealias Element = [Foo]
    var elements: Element = []

    func listAdapter(_ adapter: IGListAdapter, observedEvent: Event<Element>) {
        if case .next(let elements) = observedEvent {
            self.elements = elements
            adapter.performUpdates(animated: true)
        }
    }

    func objects(for listAdapter: IGListAdapter) -> [IGListDiffable] {
        return elements
    }

    func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController {
        return SectionController()
    }

    func emptyView(for listAdapter: IGListAdapter) -> UIView? {
        return nil
    }
}
```

## Help

次はIGListSectionTypeをRxSwift化していきたいので何か良い方法があればコメントを頂けると幸いです。`UICollectionView`や`UITableView`のようにModelSelectedなどを追加できたらな〜と思います。

レポジトリ -> https://github.com/yuzushioh/RxIGListKit