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

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

去年のTry Swift New Yorkで発表があったInstagramのFeedの実装に利用されているIGListKitをRxSwiftでUICollectionViewUITableViewを扱うように実装できるようにしてみました。(Try Swiftでの発表はこちら)

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

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

Background

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

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

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

👇👇👇👇👇👇

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

Example

今回は実際にIGListKitとRxSwiftを使って以下のUIを実装してみた。
プロジェクトはこのレポジトリーに公開しています。

|

About RxIGListKit

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

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

  • Step 1

UITableViewUICollectionViewと同様にRxIGListAdapterDataSource protocolを定義する

protocol RxIGListAdapterDataSource {

    associatedtype Element

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

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

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で実装されたリストを更新することができます。


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)
}
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化していきたいので何か良い方法があればコメントを頂けると幸いです。UICollectionViewUITableViewのようにModelSelectedなどを追加できたらな〜と思います。

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


『 Swift 』Article List