RxCocoaのUITableViewのbind(to: )にRegistrableを使って処理の簡略化
前提
Registrable型に則ればあとは型推論によるextensionの実装で済ませようというアプローチです
キャストが失敗したら?だったり、各型のIdentifierを取得する手間をextensionに閉じ込めることができるので、
シーケンスに集中することができるためコードの見通しが良くなります。
RxCocoaのBindToにこの仕組を使う
RxCocoaではbind(to)
でシーケンスなElementをUITableViewのデータソースとして扱い、それを表示する拡張があります。
下記がその実装になります。
public func items<S: Sequence, Cell: UITableViewCell, O : ObservableType> (cellIdentifier: String, cellType: Cell.Type = Cell.self) -> (_ source: O) -> (_ configureCell: @escaping (Int, S.Iterator.Element, Cell) -> Void) -> Disposable where O.E == S { return { source in return { configureCell in let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper<S> { (tv, i, item) in let indexPath = IndexPath(item: i, section: 0) let cell = tv.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! Cell configureCell(i, item, cell) return cell } return self.items(dataSource: dataSource)(source) } } }
しかしこのままではRegistrableにしているのにIdentifierをまた書かなければなりません。そこで、Registrableの仕組みをextensionに内包してしまおうという発送です。
デフォルトの実装に対してRegistrableを使ったインターフェイスを定義して、処理を軽くラップすれば実現することができます。
実際の変更点としては2点です
- cellTypeのみを引数にとるようにジェネリクスの型を調整
- 内部で
cellIdentifier
にはRegistrable.reuseIdentifire
を使ってもとの実装に処理を委譲
UITableView+Rx.swift
extension Reactive where Base: UITableView { public func items<S: Sequence, Cell: UITableViewCell & NibRegistrable, O : ObservableType> (cellType: Cell.Type = Cell.self) -> (_ source: O) -> (_ configureCell: @escaping (Int, S.Iterator.Element, Cell) -> Void) -> Disposable where O.E == S { return self.items(cellIdentifier: cellType.reuseIdentifier, cellType: cellType.self) } }
こうすることで、tableViewの処理が4行(bind() {}で書いているところ)で収まります。
サンプル(ViewController.swift#L39-L42)
class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! let searchModel = GithubSearchModel() let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() tableView.register(SearchResultCell.self) bind() searchModel.search(q: "Rx") } } private extension ViewController { func bind() { searchModel.searchResult.asObservable() .bind(to: tableView.rx.items(cellType: SearchResultCell.self)) { (row, element, cell) in cell.setData(data: element) }.disposed(by: disposeBag) } }
上記の前提としては
- RegistrableでUITableViewCellの登録処理や取得処理をラップ
- Modelには検索結果を
Variable<ResultData>
として公開しておき、ViewController側わModelをSubscribeしておく
ただし、このbind(to:)
は複数のcellのタイプを使えないのでご注意を。。。