[RxSwift] Reactive TableView: Model Binding

Junyoung Park·2022년 8월 31일
0

RxSwift

목록 보기
1/25
post-thumbnail
post-custom-banner

RxSwift Beginners Episode 1 - Transform a classic TableView App into a Reactive one

RxSwift

RxSwift Git

RxSwift 사용 예시

viewModel
    .rows
    .bind(to: resultsTableView.rx.items(cellIdentifier: "WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in
        cell.title = viewModel.title
        cell.url = viewModel.url
    }
    .disposed(by: disposeBag)
  • binddisposed가 해당 테이블 뷰에 적용되고 있는데, Rx를 사용하지 않으면 세 줄로는 표현이 불가능함
  • 일반적인 델리게이트 패턴을 따르지 않고도 반응형 구현 가능

RxSwift 사용 목적

  • 선언적인 방법으로 앱 코드를 작성하는 게 가능
  1. Composible: 구성 및 조합이 가능
  2. Resuable: 코드 재활용
  3. Declarative: 선언적 → 정의는 불변, 데이터만 변하기 때문
  4. Understandable and concise: 추상화 레벨을 높이고 일시적인 상태를 없앰
  5. Stable: 전적으로 유닛 단위
  6. Less stateful: 단방향 데이터 플로우로 어플리케이션을 모델링하기 때문
  7. Without leaks: 리소스 관리가 간단

RxSwift의 최대 장점은 iOS9부터 지원이 가능하다는 점이라 생각한다. iOS13부터 스위프트 공식 지원 프레임워크로 Combine이 들어왔고, iOS15부터는 async/await가 가능하기 때문이다.

  • 실상 RxSwift로 구현 가능한 비동기적 동작 방법을 다른 방식으로 구현 가능하니, 코코아팟을 통해 서브 파티로 설치해야 하는 RxSwift의 사용도가 점점 줄어드리라 생각한다.
  • 하지만, 세상은 그렇게 녹록치 않은 법. 여전히 iOS13 버전을 적용하기 어려운 부분이 상당하고, 기존 코드를 변환하는 것 역시 상당한 부담이 될 수도 있을 것 같다.
  • (기존의 반응형 코드를 작성하는) RxSwift를 안다면 Combine을 보다 쉽게, async/await를 보다 간편하게 접근할 수 있으리라 믿는다.

RxSwift 개념

Observerable

  • 특정 값의 변화를 감지, 알림을 내보내는 역할

Observer

  • Observable을 구독, 값의 변화를 알림 받는 역할

Disposable Bag

  • RxSwift가 메모리 관리를 하는 데 도움이 되는 도구

Reactive TableView: Model Binding

구현 목표

  • 기존의 UIKit의 UITableView 델리게이트 패턴을 RxSwift 패턴으로 변경

구현 태스크

  1. CocodPod을 통해 RxSwift, RxCocoa 설치하기
  2. 기존의 UITableView 델리게이트 및 데이터 소스 바인딩 구현을 RxSwift로 변경하기
  3. Observable, rx, disposed를 통한 감지 / 감지에 대한 반응 / 메모리 관리하기

핵심 코드

    private func setTableViewRx() {
        tableViewRxItems
            .bind(to: tableView
                .rx
                .items(cellIdentifier: "tableViewCell")) {
                    (tv, tableViewItem, cell) in
                    cell.textLabel?.text = tableViewItem
                }
                .disposed(by: disposeBag)
    }

소스 코드

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {
    
    let tableViewRxItems = Observable.just(["Item 1", "Item 2", "Item 3", "Item 4"])
    @IBOutlet weak var tableView: UITableView!
    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setTableViewRx()
    }
    
    private func setTableViewRx() {
        tableViewRxItems
            .bind(to: tableView
                .rx
                .items(cellIdentifier: "tableViewCell")) {
                    (tv, tableViewItem, cell) in
                    cell.textLabel?.text = tableViewItem
                }
                .disposed(by: disposeBag)
    }
}
  • Combine 프레임워크와 같은 동작 → 관찰을 받는 퍼블리셔Observable, 퍼블리셔에 대한 구독 rx, 구독을 취소하거나 해당 값을 저장하는 공간 cancellables-DisposeBag
  • UITableView의 델리게이트/데이터소스 패턴을 따르지 않고 곧바로 바인딩하고자 하는 데이터와 테이블 뷰를 연결
  • 클로저의 해당 파라미터 tv, tableViewItem, cell은 각각 해당 테이블 뷰, 해당 테이블 뷰에 매칭되는 데이터, 데이터에 씌울 셀을 의미
  • items를 통해 몇 번째 셀에 몇 번째 인덱스 값이 들어갈지 알 수 있는 까닭은 tableViewRxItems에서 Observable.just()를 통해 등록했기 때문
public static func just(_ element: Element) -> Observable<Element> {
        Just(element: element)
    }
// Returns an observable sequence that contains a single element.
  • RxSwift 문서에 나와 있는 Just. CombineJust와 매우 유사
import UIKit

class ViewController: UIViewController {
    let tableViewItems = ["Item 1", "Item 2", "Item 3", "Item 4"]
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setTableView()
    }
    
    private func setTableView() {
        tableView.delegate = self
        tableView.dataSource = self
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tableViewItems.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .default, reuseIdentifier: "tableViewCell")
        cell.textLabel?.text = tableViewItems[indexPath.row]
        return cell
    }
}
  • 기존의 UITableView 구현 코드
  • 해당 테이블 뷰의 변화를 감지하는 델리게이트를 사용, 어떤 데이터 소스를 사용할 것인지 할당하기 위한 테이블 뷰 전용 함수를 활용

구현 화면

RxSwift에 등장하는 sink 개념 등 이전 Combine 프레임워크를 익힐 때 마주쳤던 개념이 매우 많다! 다시 한 번 짚고 가면서 비동기 데이터를 처리하는 방법론 자체에 익숙해지자.

profile
JUST DO IT
post-custom-banner

0개의 댓글