RxFlow 적용기
우선 프로젝트에서 적용 시킨 Flow 는 이렇다
AppFlow → MainFlow
AppInitViewController 에서 init 함수들이 돌고 끝이나면
MainFlow 로 넘어가서 MainViewController 를 push 하고
MainViewController 에서 버튼을 클릭하면 CodingViewController 를 push 한다.
따라서 Step은 이렇게 단순하다.
enum AppStep: Step {
case introIsRequired
case mainIsRequired
case codingIsRequired
}
Flow의 시작은 SceneDelegate에서 시작해줬다.
let appFlow = AppFlow()
let appStepper = OneStepper(withSingleStep: AppStep.introIsRequired)
self.coordinator.coordinate(flow: appFlow, with: appStepper)
Flows.use(appFlow, when: .created) { (root) in
window.rootViewController = root
window.makeKeyAndVisible()
}
AppFlow를 사용하고 OneStepper를 넣어줘서 AppFlow이 시작하면서 introIsRequired 스텝이 처음으로 들어온다. Flows.use를 시작하여 AppFlow를 시작해준다.
이때 rootViewController를 변경해준다. Root는 AppFlow에서 지정해준다.
AppFlow에서는 UINavigationController를 생성해주고 root로 지정해준다.
var root: Presentable {
return self.rootViewController
}
private lazy var rootViewController: UINavigationController = {
let viewController = UINavigationController()
viewController.setNavigationBarHidden(true, animated: false)
return viewController
}()
이렇게 함으로써 AppFlow를 시작했던 SceneDelegate에서
window.rootViewController를 AppFlow.rootViewController 로 변경해준것이다.
AppFlow에서는 두가지 navigate가 있다. introIsRequired mainIsRequired
introIsRequired 는 단순히 storyboard 를 불러와서 push 할것이고
mainIsRequired는 MainFlow로 변경할것이다.
func navigate(to step: Step) -> FlowContributors {
guard let step = step as? AppStep else { return .none }
switch step {
case .mainIsRequired:
return navigateToMainViewController()
case .introIsRequired:
return navigateToIntroController()
default:
return .none
}
}
introIsRequired
private func navigateToIntroController() -> FlowContributors {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "Main") as! AppInitViewController
self.rootViewController.pushViewController(viewController, animated: false)
return .one(flowContributor: .contribute(withNext: viewController))
}
storyboard의 이름은 실제 storyboard 파일명이고 withIdentifier 는 storyboard안에 identifier이다.
현재는 AppFlow → introIsRequired
mainIsRequired
private func navigateToMainViewController() -> FlowContributors { let flow = MainScreenFlow(root: self.rootViewController) let nextStep = OneStepper(withSingleStep: AppStep.mainIsRequired) return .one(flowContributor: .contribute(withNextPresentable: flow, withNextStepper: nextStep)) }
private func navigateToMainViewController() -> FlowContributors {
let flow = MainScreenFlow(root: self.rootViewController)
let nextStep = OneStepper(withSingleStep: AppStep.mainIsRequired)
return .one(flowContributor: .contribute(withNextPresentable: flow, withNextStepper: nextStep))
}
SceneDelegate에서 사용했듯이 flow와 step을 리턴해줬다.
하지만 여기서 pushViewController 를 사용하지 않음을 볼수있다.
RxFlow 예제 샘플코드 에서는 위의 경우처럼 다른 플로우를 사용할때 (갈아탈때?)
pushViewController 나 present를 사용했었다.
RxFlow 샘플코드
private func navigationToDashboardScreen() -> FlowContributors { let dashboardFlow = DashboardFlow(withServices: self.services) Flows.use(dashboardFlow, when: .created) { [unowned self] root in self.rootViewController.pushViewController(root, animated: false) } return .one(flowContributor: .contribute( withNextPresentable: dashboardFlow, withNextStepper: OneStepper( withSingleStep: DemoStep.dashboardIsRequired))) }
private func navigationToDashboardScreen() -> FlowContributors {
let dashboardFlow = DashboardFlow(withServices: self.services)
Flows.use(dashboardFlow, when: .created) { [unowned self] root in
self.rootViewController.pushViewController(root, animated: false)
}
return .one(flowContributor: .contribute(
withNextPresentable: dashboardFlow,
withNextStepper: OneStepper(
withSingleStep: DemoStep.dashboardIsRequired)))
}
하지만 실제로 내 코드에서는 pushViewController를 하면 에러가 발생한다.
현재 rootViewController는 UINavigationController이고 (AppFlow의 rootViewController)
pushViewController 할 root 또한(MainFlow 의 rootViewController)
UINavigationController 이기 때문이다.
UINavigationController push UINavigationController = 에러
present는 화면의 크기가 크게 벗어나고 화면에 보여지는 문제가 있었다.
방법을 찾던중 rootViewController를 MainFlow 를 init 할때 MainFlow의 root로 넘겨주는방법을 선택했다.
class MainScreenFlow: Flow {
var root: Presentable {
return rootViewController
}
init(root:UINavigationController) {
self.rootViewController = root
}
private let rootViewController : UINavigationController!
이방법을 통해 문제를 해결할수 있었다.
MainFlow 에서는 단순히 Storyboard를 불러와서 push해주면된다.
private func navigateToMainScreen() -> FlowContributors {
let viewModel = inject(service: AppSettingViewModel.self)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let viewController = storyboard.instantiateViewController(withIdentifier: "MainViewController") as? MainViewController else {return .none}
self.rootViewController.pushViewController(viewController, animated: false)
return .one(flowContributor: .contribute(withNextPresentable: viewController, withNextStepper: viewModel as! Stepper))
}