#6 Navigation and Passing Data between ViewControllers - RxSwift MVVM Coordinator iOS App
Coordinator
연결Coordinator
을 통한 네비게이션 컨트롤러 내 뷰 푸시private func setViewModel() {
viewModel = viewModelBuilder((
searchText: searchBarController.searchBar.rx.text.orEmpty.asDriver(),
citySelect: tableView.rx.modelSelected(CityViewModel.self).asDriver()
))
}
modelSelected
라는 rx
에 연결된 프로퍼티를 통해 현재 선택된 도시 뷰 모델을 asDriver
로 변환한 뒤 전달SearchViewModel
)이 가지고 있는 citySelect
는 특정 셀을 선택할 때마다 새롭게 업데이트typealias Input = (
searchText: Driver<String>,
citySelect: Driver<CityViewModel>
)
citySelect
가 추가됨에 따라 검색 뷰 컨트롤러의 뷰 빌더를 통해 전달 가능private typealias RoutingAction = (citySelectedRelay: PublishRelay<Set<AirportModel>>, ())
private let routingAction: RoutingAction = (citySelectedRelay: PublishRelay(), ())
typealias Routing = (citySelected: Driver<Set<AirportModel>>, ())
lazy var router = (citySelected: routingAction.citySelectedRelay.asDriver(onErrorDriveWith: .empty()), ())
citySelect
값을 통해 만들어줄 중간 단계 라우팅PublishRelay
, Driver
를 통해 각각 값이 전달/보관/UI를 그리기 위한 구동 단계를 형성PublishRelay
는 밖에 보이지 않게 가리고, UI로 연결되는 Driver
는 노출 input
.citySelect
.map({ $0.city })
.withLatestFrom(state.airports.asDriver()) {( city: $0, airports: $1 )}
.map { city, airports in
airports.filter({ $0.city == city })
}
.map({ [routingAction] in
routingAction.citySelectedRelay.accept($0)
})
.drive()
.disposed(by: disposeBag)
citySelect
는 Driver
형태로 이미 뷰 모델이 가지고 있는 다른 데이터(공항 데이터)와 연결, 필터링을 통해 결과적으로 데이터 바인딩이 일어나는 라우팅 액션의 citySelectedRelay
에 값을 전달override func start() {
let searchVC = SearchViewController()
let service = AirportService.shared
searchVC.viewModelBuilder = { [disposeBag] in
var viewModel = SearchViewModel(input: $0, airportService: service)
viewModel
.router
.citySelected
.map { [weak self] models in
self?.showAirports(usingModel: models)
}
.drive()
.disposed(by: disposeBag)
return viewModel
}
navigationController.pushViewController(searchVC, animated: true)
}
SearchCoordinator
의 start
함수 내에서도 라우터를 계속 관찰, PublishRelay()
를 통해 받아오는 값을 models
로 연결해 showAirports
함수에 연결private func showAirports(usingModel models: Set<AirportModel>) {
let airportsCoordinator = AirportsCoordinator(navigationController: navigationController)
add(coordinator: airportsCoordinator)
airportsCoordinator.start()
}
models
에 해당 데이터가 들어온 뒤 주어진 뷰에 따른 Coordinator
를 추가하고, 해당 Coordinator
를 구동하는 기본 로직은 상동import UIKit
class AirportsCoordinator: BaseCoordinator {
private let navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
override func start() {
let vc = AirportsViewController()
self.navigationController.pushViewController(vc, animated: true)
}
}
네비게이션 이동 과정을 자체를 주관하는
Coordinator
패턴의 동작대로, 뷰를 로드하고 삭제하는 과정을 위임하고 있다. 아직까지 그 '힘'이라고 해야 하나, 장점을 명백하게 느끼지는 못하겠지만, 일단은 다채롭다.