LoginSignup
5
2

More than 3 years have passed since last update.

【Swift】RxDataSource(MVVM)の実装手順

Last updated at Posted at 2021-03-05

1.Entityを用意する

struct Data {
    var id
    var name
}

もちろんここはAPIの受け口であるCodableなEntityでも良いですし、
そういう使い方の方が多いかもしれません。

2.DataSourceを実装

import RxDataSources

struct DataSource {
    var items: [Data]
}
extension DataSource: SectionModelType {
    init(original: DataSource, items: [Data]) {
        self = original
        self.items = items
    }
}

セクションモデルなので、セクションごとのモデル定義が前提だが、
もちろんセクションが1つの場合でもok

3. register

tableView.register(UINib(nibName: identifier, bundle: nil), forCellReuseIdentifier: identifier)

collectionviewの場合はここをcollectionviewで

4. Cellのsetup関数自作(ここは自由)

//cellファイル
func setupCell(item: Data) {
    hogeId = item.id
    hogeName = item.name
}

5. datasource (6が先でも良い)

//grobal変数>>
//CollectionViewならRxCollectionViewSectionedReloadDataSource
 var datasource: RxTableViewSectionedReloadDataSource<[DataSource]>?
//<<
...
//CollectionViewならRxCollectionViewSectionedReloadDataSource
datasource = RxTableViewSectionedReloadDataSource<DataSource>(configureCell: { _, tableView, indexPath, items in
            let cell = TableViewUtil.createCell(tableView, identifier: cellのidentifier,  indexPath) as! hogehogeTableViewCell
            //cellのセットアップ関数を作りそこにdatasourceを流す
            //itemsのtypeはData
            cell.setupCell(item: items)
            return cell
        })

6. setDelegate

//grobal変数>>
private var disposeBag = DisposeBag()
//<<

//普通のTableViewDelegateが使える
tableView.rx.setDelegate(self).disposed(by: disposeBag)
//セル選択時にindexPathが送られてくるのでViewModelに流す
tableView.rx.itemSelected
            .subscribe(onNext: { [weak self] indexPath in
                guard let self = self else {return}
                hogeViewModel.input.tableviewSelected.onNext(indexPath)
            }).disposed(by: disposeBag)

7.ViewModel(KickstarterのVMインターフェース)

protocol ViewModelInput {
    var tableviewSelected: AnyObserver<Void> {get}
    var fetchData: AnyObserver<Void> {get}
}
protocol ViewModelOutput {
    var dataSource: Observable<[DataSource]> {get}
}
protocol ViewModelType {
    var inputs: ViewModelInput {get}
    var outputs: ViewModelOutput {get}
}
class ViewModel: ViewModelInput, ViewModelOutput {
    //input
    var tableviewSelected: AnyObserver<Void>
    var fetchData: AnyObserver<Void>
    //output
    var dataSource: Observable<[DataSource]>
    private var localDataSource:[DataSource]?
    private var disposeBag = DisposeBag()

    //VCヘの遷移でViewModelのインスタンスを作るときに
    //データを渡してあげたい場合はinitに投げる
    init(dataSource: [DataSource]) {
        localDataSource = dataSource

        let _tableviewSelected = PublishRelay<IndexPath>()
        tableviewSelected = AnyObserver<IndexPath>() { indexPath in
            guard let indexPath = indexPath.element else {return}
            _tableviewSelected.accept(indexPath)
        }
        let _fetchData = PublishRelay<Void>()
        fetchData = AnyObserver<Void>() { indexPath in
            guard let indexPath = indexPath.element else {return}
            _fetchData.accept(Void())
        }
        let _dataSource = PublishRelay<[DataSource]>()
        dataSource = _dataSource.asObservable()


        _tableviewSelected.subscribe({ [weak self] indexPath in
            guard let self = self else {return}
            guard let indexPath = indexPath.element else {return}
            //indexPathがくるのでdatasourceをいじり
            //_dataSource.acceptでデータを渡してやる
        }).disposed(by: disposeBag)

        _fetchData.subscrive({ [weak self] _ in
            //ここでAPIを投げてレスポンスを_dataSourceにacceptする
        }).disposed(by: disposeBag)
        //ローカルにデータがあるなら普通に_dataSource.accept([DataSource])でok

    }
}

8.outputのdatasourceをtableviewにbind

//これでviewModelからのdatasourceがtableviewにバインドされ、
//値の変更がリアルタイムで反映される
viewModel.outputs.dataSource.bind(to: tableView.rx.items(dataSource: datasource!))
            .disposed(by: disposeBag)

9.まとめ

ViewModelからdatasourceをViewControllerのtableViewにバインドできていればok
データをいじるときはviewModelのオブザーバに購読させ、
そこでデータを加工し、加工したデータをdatasourceにacceptすれば
tableviewにバインドされているのでデータが反映される流れ。

fetchDataにイベントを送る処理やAPI部分は
書いていません。(API処理もRxSwiftがわかる前提で書いています)

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2