개발/Swift

RxFlow 적용기

덤벨로퍼 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))
}