[RxCocoa] TableView에 Observable 바인드하는 방법

Lily·2022년 6월 19일
0

rxSwift🏃🏻‍♀️

목록 보기
4/6

안녕하세요~!

오늘은 rxCocoa를 사용해 UITableView 에 데이터를 어떻게 뿌려주는지! 그 방법들에 대해 알아보겠습니다🧐


단일 데이터를 UILabel이나 UITextView 에 바인드 시켜주는건 그렇게 어렵지 않았는데요.
그런데 UITableView, UITableViewDataSource의 메서드들은 Observable과 어떻게 엮어서 사용해야할지...?!?!? 는 전혀 감이 안잡히더라구요.

찾아보니 rxCocoarxDataSource를 사용하면 간편하게 바인드할 수 있었는데요, 그중에서도 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을 리턴합니다.

구현은 복잡한데 사용은 어렵지 않습니다🙂

Example

먼저 테이블 뷰 셀에 제공할 데이터 배열을 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)
            }
        }
    }

Example

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을 리턴한다.
  • 두번째 방법: 명시한 cellType의 cell을 받아서, element로 configue, 그리고 cell을 리턴한다. (단 강제 캐스팅을 하므로 cellType이 틀리면 크래시남 주의)
profile
i🍎S 개발을 합니다

0개의 댓글