ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • RxFlow 적용기
    개발/Swift 2021. 5. 25. 17:44

    우선 프로젝트에서 적용 시킨 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))
    }

    댓글

Designed by Tistory.