검색 화면 등의 인터랙션을 받고 → 모델의 변화 → UI 업데이트 (⇒ 이러한 양방향 데이터 흐름에서 알엑스의 구조가 사용하기 좋다.)
<aside> ✏️ 2022.11.03.목
다른 화면에서도 Rx를 적용해보겠다. 언젠간.
</aside>
이렇게 검색화면이 있다고 할 때, 뷰의 입력과 그에 따른 모델의 변화를 정리하면 아래와 같다.
위의 과정을 뷰모델이 관리한다고 생각할 수 있다.
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)
}
}
isFiltering
변수를 두어서 이 변수의 변화에 따라 뷰에서 키보드가 올라가고 ↔ 내려가는 것을 관리할 수 있다.그리고 뷰에서 하는 일은
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)
}