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



## 1.Entityを用意する



```Swift
struct Data {
    var id
    var name
}
```

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


## 2.DataSourceを実装

```Swift
import RxDataSources

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

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


## 3. register

```Swift
tableView.register(UINib(nibName: identifier, bundle: nil), forCellReuseIdentifier: identifier)
```
collectionviewの場合はここをcollectionviewで

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

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


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

```Swift
//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

```Swift
//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インターフェース)

```Swift
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

```Swift
//これで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がわかる前提で書いています)