LoginSignup
4
2

More than 5 years have passed since last update.

RxFlow in Swift

Posted at

Introduction

RxFlow is a navigation framework for iOS applications based on a Flow Coordinator pattern. It is also important to keep in mind that RxFlow uses protocol oriented programming so that it doesn’t freeze your code in an inheritance hierarchy. RxFlow removes the navigation code from the View Controllers and it promotes Dependency Injection.
RxFlow is mainly about handling navigation state changes in a reactive way. In order to be reused in multiple contexts, these states must be unaware of the current navigation Flow the user is in. Therefore, instead of meaning “I want to go to this screen”, a state will rather mean “Someone or something has done this action” and RxFlow will pick the right screen according to the current navigation Flow. With RxFlow, this navigation states are called Steps.

RxFlow_logo.png

Terminology

Flow: each Flow defines a navigation area within your application.
Step: each Step is a navigation state in your application. Combinaisons of Flows and Steps describe all the possible navigation actions.
Stepper: it can be anything that can emit Steps. Steppers will be responsible for triggering every navigation actions within the Flows
Presentable: it is an abstraction of something that can be presented, basically UIViewController and Flow are Presentable
NextFlowItem: it tells the Coordinator what will be the next thing that will produce new Steps in its Reactive mechanism
Coordinator: the job of the Coordinator is to mix combinaisons of Flows and Steps in a consistent way.

Example

enum AppStep: Step {
    case intro
    case details
}

Flow has to implement:
- “navigate(to:)” function that makes the navigation actions happen according the Flow and the Step
- “root” UIViewController on which will be based the navigation in this Flow

class AppFlow: Flow {
    // MARK: - Properties
    var root: Presentable {
        return self.rootViewController
    }
    private let viewModel: AppViewModel
    private lazy var rootViewController: UINavigationController = {
        let vc = UINavigationController()
        vc.setNavigationBarHidden(false, animated: true)
        return vc
    }()

    // MARK: - Init
    init(stepper viewModel: AppViewModel) {
        self.viewModel = viewModel
    }

    // MARK: - Navigation switch
    func navigate(to step: Step) -> NextFlowItems {
        guard let step = step as? AppStep else { return NextFlowItems.none }
        switch step {
        case .intro:
            return navigateToIntro()
        case .details:
            return navigateToDetails()

        }
    }
}

When it navigates to the new screen

// MARK: - Navigate to Intro
extension AppFlow {
    private func navigateToIntro() -> NextFlowItems {
        let viewControllerIntro: ViewController = ViewController()
        viewControllerIntro.setViewModel(viewModel: self.viewModel)
        let nexFlowItem = NextFlowItem(nextPresentable: viewControllerIntro, nextStepper: viewModel)
        return NextFlowItems.one(flowItem: nexFlowItem)
    }
}

// MARK: - Navigate to Details
extension AppFlow {
    private func navigateToDetails() -> NextFlowItems {
        let viewControllerDetail: ViewControllerDetail = ViewControllerDetail()
        viewControllerDetail.setViewModel(viewModel: self.viewModel)
        let nextFlowItem = NextFlowItem(nextPresentable: viewControllerDetail, nextStepper: viewModel)
    }
}

Stepper can be anything: a custom UIViewController, a ViewModel, a Presenter… Once it is registered in the Coordinator, a Stepper can emits Steps via its “step” property (which is a RxSwift subject). The Coordinator will listen for these Steps and call the Flow’s “navigate(to:)” function.
For Example:

class AppViewModel: Stepper {
    init() {
        self.step.accept(AppStep.intro)
    }

    public func toDetails() {
        self.step.accept(AppStep.details)
    }

    public func toIntro() {
        self.step.accept(AppStep.intro)
    }

    public func toSetupFlow() {
        self.step.accept(AppStep.setupFlow)
    }

    public func toFinished() {

toDetails function is called when user click a button to a detail screen. This function emits a new value in the “self.step” Rx stream.

To Sum up process:
- the navigate(to:) function is called with a Step as a parameter
- according to this Step, some navigation code is called (side effects)
- also according to this Step, NextFlowItems are produced. Therefore, Presentables and Steppers are registered into the Coordinator
- Steppers emit new Steps and here we go again

Sample Code
You can try to understand the RxFlow more with the sample code here in GitHub

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2