#11 Present and Dismiss a ViewController in Coordinator - RxSwift MVVM Coordinator iOS App
coordinator
패턴에서의 모달 전환 구현Routing
프로토콜 내 모달 프레젠트 및 디스미스 함수 구현protocol RouterProtocol {
func push(_ drawable: Drawable, isAnimated: Bool, onNavigationBack: NavigationBackClosure?)
func pop(_ isAnimated: Bool)
func popToRoot(_ isAnimated: Bool)
func present(_ drawable: Drawable, isAnimated: Bool, onDismiss: NavigationBackClosure?)
}
present
함수 추가func present(_ drawable: Drawable, isAnimated: Bool, onDismiss closure: NavigationBackClosure?) {
guard let viewController = drawable.viewController else { return }
if let closure = closure {
closures.updateValue(closure, forKey: viewController.description)
}
navigationController.present(viewController, animated: isAnimated)
viewController.presentationController?.delegate = self
}
extension Router: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
executeClosures(presentationController.presentedViewController)
}
}
presentationControllerDidDismiss
함수를 사용 가능executeClosures()
를 사용함으로써 딕셔너리에 기록해놓은 클로저가 해당 라우터 클래스에서 실행됨private func showAirportDetails(model: AirportModel) {
let detailCoordinator = AirportDetailsCoordinator(router: router)
add(coordinator: detailCoordinator)
detailCoordinator.isCompleted = { [weak self, weak detailCoordinator] in
guard let detailCoordinator = detailCoordinator else { return }
self?.remove(coordinator: detailCoordinator)
}
detailCoordinator.start()
}
AirportsCoordinator
에서 클릭 이벤트가 발생하는 곳detailCoordinator
라는 새로운 전환이 일어날 때 isCompleted
- 즉 onDismiss
클로저에 파라미터로 들어갈 클로저를 선언coordinator
에 들어간 메모리를 다시 제거해주는 장소override func start() {
let vc = AirportDetailsViewController()
router.present(vc, isAnimated: true, onDismiss: isCompleted)
}
AirportsCoordinator
에서 작동하는 start()
함수present
함수가 실행되는 코드private func setViewModel() {
viewModel = viewModelBuilder((
selectAirport: tableView.rx.modelSelected(AirportViewPresentable.self).asDriver(onErrorDriveWith: .empty()), ()
))
}
AirportsViewController
에서 해당 setViewModel
함수가 실행되는 구문tableView.rx.modelSelected
를 통해 반응형으로 현재 유저가 선택한 셀의 모델(AirportViewPresentable
)을 뷰 모델 빌더에게 넘겨줄 수 있음typealias Input = (
selectAirport: Driver<AirportViewPresentable>, ()
)
private typealias RoutingAction = (airportSelectRelay: PublishRelay<AirportModel>, ())
private let routingAction = (airportSelectRelay: PublishRelay<AirportModel>(), ())
typealias Routing = (airportSelect: Driver<AirportModel>, ())
lazy var router: Routing = (airportSelect: routingAction.airportSelectRelay.asDriver(onErrorDriveWith: .empty()), ())
private
으로 감싸 놓은 Relay
를 통해 Driver
를 만들어주는 역할func process(dependencies: Dependencies) {
input
.selectAirport
.map { [models = dependencies.models] viewModel in
models.filter({ $0.code == viewModel.code}).first
}
.compactMap({ $0 })
.map({ [routingAction] in
routingAction.airportSelectRelay.accept($0)
})
.drive()
.disposed(by: disposeBag)
}
Set<AirportModel>
가운데 입력된 모델과 코드가 일치하는 모델만 필터링, 라우팅 액션 Relay
에 값을 들여보내는 곳