안녕하세요~!
오늘은 rxCocoa를 사용해 UITableView 에 데이터를 어떻게 뿌려주는지! 그 방법들에 대해 알아보겠습니다🧐
단일 데이터를 UILabel이나 UITextView 에 바인드 시켜주는건 그렇게 어렵지 않았는데요.
그런데 UITableView, UITableViewDataSource의 메서드들은 Observable과 어떻게 엮어서 사용해야할지...?!?!? 는 전혀 감이 안잡히더라구요.
찾아보니 rxCocoa와 rxDataSource를 사용하면 간편하게 바인드할 수 있었는데요, 그중에서도 rxCocoa의 메서드에 대해 공부해보았습니다.
rxCocoa에서는 3가지 메서드를 제공해주는데, 그 중 2개 메서드의 사용방법과 차이점에 대해 알아보도록 하겠습니다.
🌟 rxCocoa 소스 코드
https://github.com/ReactiveX/RxSwift/blob/main/RxCocoa/iOS/UITableView%2BRx.swift
public func items<Sequence: Swift.Sequence, Source: ObservableType>
        (_ source: Source)
        -> (_ cellFactory: @escaping (UITableView, Int, Sequence.Element) -> UITableViewCell)
        -> Disposable
        where Source.Element == Sequence {
            return { cellFactory in
                let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper<Sequence>(cellFactory: cellFactory)
                return self.items(dataSource: dataSource)(source)
            }
    }
중첩된 구조의 메서드 형태입니다.
각 파라미터의 역할입니다.
source : Observable sequence of items
cellFactory : element를 이용해 cell을 configure
Disposable을 리턴합니다.
구현은 복잡한데 사용은 어렵지 않습니다🙂
먼저 테이블 뷰 셀에 제공할 데이터 배열을 Observable로 만들어줍니다.
let items = Observable.just([
             "First Item",
             "Second Item",
             "Third Item"
         ])
items.bind(to: _) 에 바인딩할테이블뷰.rx.items 를 넣고,
메서드의 후행클로저에 tableView, row, element를 이용해서 cell을 configure해줍니다. 
    items.bind(to: tableView.rx.items) { (tableView, row, element) in
             let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
             cell.textLabel?.text = "\(element) @ row \(row)"
             return cell
         }
         .disposed(by: disposeBag)
후행클로저에서 제공되는 파라미터 중tableView에는 제가 위에서 넣어준 바인딩할 테이블뷰, row는 deque될 cell의 row의 Int값, element는 데이터 타입(String)이 들어갑니다.  
row마다 다른 cell을 리턴해주어야할 때 사용할 수 있겠네요
// output: Observable<[ProductViewModel]>
output.products
            .bind(to: tableView.rx.items) { (tableView, row, element) in
                guard let cell = tableView.dequeueReusableCell(withIdentifier: "ProductTableViewCell") as? ProductTableViewCell else{
                    return ProductTableViewCell()
                }
                cell.fill(with: element)
                return cell
            }
            .disposed(by: disposeBag)
두번재 방법은 첫번째 방법보다 더 진화된 방법입니다. 리턴할 Cell의 타입을 파라미터로 받아서 타입캐스팅 까지 해준 Cell을 반환합니다.
  public func items<Sequence: Swift.Sequence, Cell: UITableViewCell, Source: ObservableType>
        (cellIdentifier: String, cellType: Cell.Type = Cell.self)
        -> (_ source: Source)
        -> (_ configureCell: @escaping (Int, Sequence.Element, Cell) -> Void)
        -> Disposable
        where Source.Element == Sequence {
        return { source in
            return { configureCell in
                let dataSource = RxTableViewReactiveArrayDataSourceSequenceWrapper<Sequence> { tv, i, item in
                    let indexPath = IndexPath(item: i, section: 0)
                    // cellType으로 강제 캐스팅
                    let cell = tv.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! Cell
                    configureCell(i, item, cell)
                    return cell
                }
                return self.items(dataSource: dataSource)(source)
            }
        }
    }
1번 방법과 동일하게 셀에 바인드할 데이터 배열의 Observable을 준비합니다.
  let items = Observable.just([
             "First Item",
             "Second Item",
             "Third Item"
         ])
1번 방법과 동일하게 첫번째 파라미터에 바인딩할 테이블뷰.rx.item 를 넣고, cellIdentifer 에 Cell Identifier, cellType에 커스텀 셀 타입을 명시합니다.
그리고 후행 클로저에서 제공된 row , element를 사용해 cell을 configure합니다. 
        items.bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { (row, element, cell) in
                cell.textLabel?.text = "\(element) @ row \(row)"
             }
             .disposed(by: disposeBag)
// output: Observable<[ProductViewModel]>
  output.products
            .bind(to: tableView.rx.items(cellIdentifier: "ProductTableViewCell", cellType: ProductTableViewCell.self)) { (row, element, cell) in
                // cell: ProductTableViewCell
                cell.fill(with: element)}
            .disposed(by: disposeBag)
element로 셀을 configure, 그리고 cell을 리턴한다. element로 configue, 그리고 cell을 리턴한다. (단 강제 캐스팅을 하므로 cellType이 틀리면 크래시남 주의)