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