ReactorKitでFirestoreをListenする方法


概要

ReactorKitを使ったプロジェクトでFirestoreを使うことになったので、Pringを用いて実装しました。

環境

実装

UserというモデルをFirestoreから取得してくるという設定です。

Reactor側

Action, Mutation, State, mutate, reduceはやるだけって感じです。
ポイントはdataSourceを以下のように用意して、

private let dataSource: DataSource<User>?

setupDataSource()内でDataSourcelisten()してやることです。
この時.onの中でself?.action.onNext(.updateUsers(users))することで、Firestoreから取得したデータをReactorのストリームにうまく取り込んでます。

private func setupDataSource() {
    dataSource = User.query.dataSource()
        .on { [weak self] _, _ in
            guard let users = self?.dataSource?.documents else { return }
            self?.action.onNext(.updateUsers(users))
        }
        .listen()
}

全実装は以下のようになります。

import ReactorKit
import RxSwift
import Pring

final class UserListReactor: Reactor {
    enum Action {
        case updateUsers([User])
    }

    enum Mutation {
        case setUsers([User])
    }

    struct State {
        var users: [User] = []
    }

    // MARK: - Variables

    let initialState = State()
    private let dataSource: DataSource<User>?

    // MARK: - Initializer

    init() {
        setupDataSource()
    }

    // MARK: - Setup Methods

    private func setupDataSource() {
        dataSource = User.query.dataSource()
            .on { [weak self] _, _ in
                guard let users = self?.dataSource?.documents else { return }
                self?.action.onNext(.updateUsers(users))
            }
            .listen()
    }

    // MARK: - Reactor Methods

    func mutate(action: Action) -> Observable<Mutation> {
        switch action {
        case let .updateUsers(users):
            return .just(Mutation.setUsers(users))
        }
    }

    func reduce(state: State, mutation: Mutation) -> State {
        var state = state
        switch mutation {
        case let .setUsers(users):
            state.users = users
        }
        return state
    }
}

ViewController側

いつも通りbind(reactor:)内でUserListReactor.State.usersbindしてやれば終わりです。

import UIKit
import RxSwift
import ReactorKit

final class UserListViewController: UIViewController, View {
    // MARK: - Variables

    var disposeBag = DisposeBag()
    ...
    // MARK: - Bind Methods

    func bind(reactor: UserListReactor) {
        // State
        reactor.state.map { $0.users }
            .distinctUntilChanged()
            .bind { users in
                // tableView or collectionViewの更新
            }
            .disposed(by: disposeBag)
    }
}