검색 화면 등의 인터랙션을 받고 → 모델의 변화 → UI 업데이트 (⇒ 이러한 양방향 데이터 흐름에서 알엑스의 구조가 사용하기 좋다.)

<aside> ✏️ 2022.11.03.목

다른 화면에서도 Rx를 적용해보겠다. 언젠간.

</aside>

작업

검색화면

IMG_6739.PNG

이렇게 검색화면이 있다고 할 때, 뷰의 입력과 그에 따른 모델의 변화를 정리하면 아래와 같다.

위의 과정을 뷰모델이 관리한다고 생각할 수 있다.

final class SearchViewModel {
    var isFiltering: BehaviorRelay<Bool> = BehaviorRelay(value: false)
    
    var location = ["강남구", "강동구", "강북구", "강서구", "관악구", "광진구", "구로구", "금천구", "노원구", "도봉구", "동대문구", "동작구", "마포구", "서대문구", "서초구", "성동구", "성북구", "송파구", "양천구", "영등포구", "용산구", "은평구", "종로구", "중구", "중랑구"]
    var filteredLocation: BehaviorRelay<[String]> = BehaviorRelay(value: [])
    
    var numberOfRowsInSection: Int {
        return filteredLocation.value.count
    }
    
    func cellForRowAt(at indexPath: IndexPath) -> String {
        return filteredLocation.value[indexPath.row]
    }
    
    func filterLocation(_ text: String) {
        let filtered = location.filter { $0.contains(text) }
        filteredLocation.accept(filtered)
    }
}

그리고 뷰에서 하는 일은

    private func bind() {
        viewModel.filteredLocation.accept(viewModel.location)
        
        viewModel.filteredLocation
            .withUnretained(self)
            .bind { vc, location in
                vc.rootView.tableView.reloadData()
            }
            .disposed(by: disposeBag)
        
        searchBar.rx.text.orEmpty
            .debounce(RxTimeInterval.microseconds(5), scheduler: MainScheduler.instance)
            .withUnretained(self)
            .bind { vc, value in
                if value != "" {
                    vc.viewModel.isFiltering.accept(true)
                    vc.viewModel.filterLocation(value)
                }
            }
            .disposed(by: disposeBag)
        
        searchBar.rx.searchButtonClicked
            .withUnretained(self)
            .bind { vc, _ in
                vc.searchBar.resignFirstResponder()
            }
            .disposed(by: disposeBag)
        
        viewModel.filteredLocation
            .bind(to: rootView.tableView.rx.items(cellIdentifier: MainSearchTableViewCell.reuseIdentifier, cellType: MainSearchTableViewCell.self)) { [weak self] index, item, cell in
                guard let self = self else { return }
                
                if let text = self.searchBar.text {
                    if self.viewModel.isFiltering.value {
                        cell.setData(item, true, text)
                    } else {
                        cell.setData(item, false, text)
                    }
                }
            }
            .disposed(by: disposeBag)
        
        rootView.tableView.rx
            .itemSelected
            .withUnretained(self)
            .subscribe(onNext:  { (vc, value) in
                vc.locationClosure?(vc.viewModel.filteredLocation.value[value.row])
                vc.navigationController?.popViewController(animated: true)
            })
            .disposed(by: disposeBag)
    }