FlowManager

public class FlowManager

Flow Manager is the core of our navigation system, it’s using this thay you will be able to make your navigation flow, it’s the important piece for you test your flows.

  • Flow Manager presentation style, as our flow start on top always, it’s a modal with navigation controller, by default is fullScreen, but with iOS 13 you can change this behaviour to have different style’s how to start / present your flow,

    • Variable:
      • flowPresentationStyle: This is the variable that you should change / set before call start() the flow;

    Declaration

    Swift

    public var flowPresentationStyle: UIModalPresentationStyle
  • Flow Manager initialiser

    Declaration

    Swift

    public init(navigation controller: UINavigationController?,
                container stack: ContainerFlowStack,
                setupInstance typeOfView: ViewIntanceFrom? = .nib,
                dismissed navigationFlow: (() -> ())? = nil)

    Parameters

    navigation

    This is the basic and convenient custom UINavigationController instance if you want to have your custom one, to work is mandatory, so, if use this you need to set, when set, we will check if first view controller is there and set the navigation flow.

    container

    It’s the ContainerFlowStack instance, it’s where we will look for the registered View Controller types in order to resolve and show on the screen.

    setupInstance

    It’s how will be our navigation, it’s a enum, if you don’t set we will assume that is nib view that you will be using.

    dismissed

    optional - Closure optional that can tell you when this navigation controller was completely closed.

  • The method responsible to start the flow, we do not start automatically when instantiate our FlowManager, so you can start as soon you want.

    Declaration

    Swift

    public func start(finishedLoad presenting: (() -> ())? = nil)

    Parameters

    finishedLoad

    optional - Convenience closure that will let you know when you finished the flow.

  • This method is only intended when you want to have just a navigation controller but only when start decide which view controller will be the root of that navigation controller.

    It’s basically the same behaviour as the initialiser that you need to pass the type of the view controller to be resolve, but with more options, in the scenario that only when you call start you want to check and validation which screen you want to have as the root, or even have a more generic way to show.

    Using this, is mandatory to you have the view controller that you intend to use be registered in your Container so we can resolve.

    Usage Example:

       flowManager.startWith(root: { () -> UIViewController.Type in
           // This is just a way to have some implementation that check the type
           // and will select the type according to the view controller, would be
           // your view controller type
           switch self.flowType {
           case .normal:
               return UIViewController.self
           case .withConfirmation:
               return UIViewController.self
           case .awareness:
               return UIViewController.self
           }
       }, resolved: { rootViewInstance in
           rootInstance.variable = "Any data that you want"
       })
    

    Declaration

    Swift

    public func startWith<T: UIViewController>(root instanceType: () -> T.Type,
                                               resolved instance: (T) -> (),
                                               finishedLoad presenting: (() -> ())? = nil)

    Parameters

    root

    The view controller type that you will have as root view controller for you navigation controller.

    resolved

    Closure that will return the loaded instance reference for this loaded view, it’s good when you want to set some values or pass any parameter not using the custom resolve, you will have the right reference to pass value.

    finishedLoad

    optional - Convenience closure that will let you know when you finished the flow.

  • This method is only intended when you want to have just a navigation controller but only when start decide which view controller will be the root of that navigation controller, but without parameter to be passed during the instantiation.

    It’s basically the same behaviour as the initialiser that you need to pass the type of the view controller to be resolve, but with more options, in the scenario that only when you call start you want to check and validation which screen you want to have as the root, or even have a more generic way to show.

    Using this, is mandatory to you have the view controller that you intend to use be registered in your Container so we can resolve.

    Usage Example:

    ````
       flowManager.startWith(root: { () -> UIViewController.Type in
           // This is just a way to have some implementation that check the type
           // and will select the type according to the view controller, would be
           // your view controller type
           switch self.flowType {
           case .normal:
               return UIViewController.self
           case .withConfirmation:
               return UIViewController.self
           case .awareness:
               return UIViewController.self
           }
       })
    ````
    

    Declaration

    Swift

    public func startWith<T: UIViewController>(root instanceType: () -> T.Type,
                                               finishedLoad presenting: (() -> ())? = nil)

    Parameters

    root

    The view controller type that you will have as root view controller for you navigation controller.

    finishedLoad

    optional - Convenience closure that will let you know when you finished the flow.

  • Method used to dismiss completely your flow.

  • animated: optional - If you want to animated the dismiss of your flow default is true.
  • completion: optional - Closure that you will receive an callback that you flow finished dismiss *(remember that if your instance is not strong you may not get this callback called.

  • Note

    It has @discardableResult because you can use other helper methods just after calling this method as we always return FlowManager instance.

    Usage Example:

       // Basic call, withoud parameters
       navigationFlow?.dismissFlowController()
    
       // Calling passing parameters
       navigationFlow?.dismissFlowController(animated: true, completion: {
           // Finished dismiss flow
       })
    

    Declaration

    Swift

    @discardableResult
    public func dismissFlowController(animated: Bool = true, completion: (() -> Void)? = nil) -> Self
  • Method used to pass Any parameter back when finish flow, as soon you implement the closure to receive parameter back when flow finish as soon you pass using this method, the parameter will be sent, it’s not typed, it’s Any so you need to cast if you want to identify and have typed parameter back, It’s recommended to use after call the dismissFlowController() method.

    Usage Example:

       // Basic call, withoud parameters
       navigationFlow?.dismissFlowController().finishFlowWith(parameter: "Finished")
    

    Declaration

    Swift

    public func finishFlowWith(parameter data: Any)

    Parameters

    parameter

    Any value that you want to pass back when flow finish.

  • This should be called only when you instantiate your FlowManager as it’s intended only to pass parameter back that you perhaps want like a response after finish

    Note

    It has @discardableResult because you can use other helper methods just after calling this method as we always return FlowManager instance.

    Usage Example:

       // Here we set after intantiate the `Flow Manager` that when dismiss
       // we want to `listen` any paramter that will be sent back
        FlowManager(root: view,
                    container: navigationStack).dismissedFlowWith { [weak self] closeAll in
    
           // Using this parameter for the situation that we want to dismiss both navigation from the top one
           if (closeAll as? Bool) == true {
               self?.navigationFlow?.dismissFlowController()
           }
        }.start()
    
       // And for use, to pass back we just need when dismiss call the method `.finishFlowWith(parameter: )`
       navigationFlow?.dismissFlowController().finishFlowWith(parameter: true)
    

    Declaration

    Swift

    @discardableResult
    public func dismissedFlowWith(parameter invoker: @escaping (Any) -> ()) -> Self

    Parameters

    parameter

    Any value that you want to pass back when flow finish.

  • This is intended to you know when you dismiss your presented modal view controller and you want to know if already finished close the presented one.

    Note

    It has @discardableResult because you can use other helper methods just after calling this method as we always return FlowManager instance.

    Usage Example:

        navigationFlow?.goNextAsModal().dismissedModal(callback: { [unowned self] in
           debugPrint("Finished close modal view")
           self.getSomeDataFromClosedModal()
        })
    

    Declaration

    Swift

    @discardableResult
    public func dismissedModal(callback invoker: @escaping () -> ()) -> Self

    Parameters

    parameter

    Any value that you want to pass back when flow finish.

  • Helper method that you can get the instance reference to the UINavigationController in case you do not provided a custom one

    Declaration

    Swift

    public func managerNavigation() -> UINavigationController?
  • Helper method that give you back the container flow stack reference

    Declaration

    Swift

    public func container() -> ContainerFlowStack?
  • Helper method that will print your stack, so you can have a visual understand how your stack is right now

    Declaration

    Swift

    @discardableResult
    public func stackState() -> String
  • This is the method that we will use in order to send parameter when resolve our instance Remember that when declare the parameter data that you want to be resolved need to be inside Tuple, as for swift when resolve everything need to have a type, with this we make sure that always have type and the values are inside

    Usage Example:

       // Basic implementation
        navigationFlow?.goNextWith(screen: ParameterFirstViewController.self, parameters: { () -> ((String, Int)) in
           return ("Felipe Garcia", 232)
        })
    
       // Using the parameters available
        navigationFlow?.goNextWith(screen: ParameterFirstViewController.self, parameters: { () -> ((String, Int)) in
           return ("Felipe Garcia", 232)
        }, resolve: .nib, resolved: { resolveView in
           // resolve view
        })
    
        // Basic implementation with custom Identifier
        navigationFlow?.goNextWith(screen: ParameterFirstViewController.self, customIdentifier: "ChoseThisScreen", parameters: { () -> ((String, Int)) in
           return ("Felipe Garcia", 232)
        })
    

    Declaration

    Swift

    public func goNextWith<T: UIViewController, Resolver>(screen view: T.Type,
                                                          customIdentifier: String? = nil,
                                                          parameters data: @escaping () -> ((Resolver)),
                                                          resolve asType: ViewIntanceFrom = .nib,
                                                          resolved instance: ((T) -> ())? = nil)

    Parameters

    screen

    The type of the view controller that you want to next.

    customIdentifier

    optional - String that is custom identifier to identify particular view

    paramer

    Closure that expect a type that will be used when resolve this screen that you are calling.

    resolve

    optional - How the view for this screen will be loaded, the default one is .nib.

    resolved

    optional - Convenience closure that will return the loaded instance reference for this loaded view, it’s good when you want to set some values or pass any parameter not using the custom resolve, you will have the right reference to pass value.

  • Method responsible to navigate to the next screen

    Usage Example:

       navigationFlow?.goNext(screen: SecondViewController.self,
                              resolve: .nib,
                              resolved: { resolveViewInstance in
           resolveViewInstance.nameForTitle = "Setting value on the next view"
       })
    
       // Basic implementation with custom Identifier
       navigationFlow?.goNext(screen: SecondViewController.self,
                              customIdentifier: "ChoseThisScreen","
                              resolve: .nib,
                              resolved: { resolveViewInstance in
           resolveViewInstance.nameForTitle = "Setting value on the next view"
       })
    

    Declaration

    Swift

    public func goNext<T: UIViewController>(screen view: T.Type,
                                            customIdentifier: String? = nil,
                                            resolve asType: ViewIntanceFrom = .nib,
                                            resolved instance: ((T) -> ())? = nil)

    Parameters

    screen

    The type of the screen that you want to go, need to be registered in your container flow stack.

    customIdentifier

    optional - String that is custom identifier to identify particular view

    resolve

    optional - How the view for this screen will be loaded, the default one is .nib.

    resolved

    optional - Convenience closure that will return the loaded instance reference for this loaded view, it’s good when you want to set some values or pass any parameter not using the custom resolve, you will have the right reference to pass value.

  • Method responsible to navigate to the next screen automatically resolve and go to the next view according to the order that you declared in your ContainerFlowStack, if no item found will just not navigation we do not throw any error

    Usage Example:

       // Without using any parameter - Indicated to the automatically navigation
        navigationFlow?.goNext()
    
       // Using the parameters available
        navigationFlow?.goNext(resolve: .nib,
                               resolved: { resolveViewInstance in
               resolveViewInstance.nameForTitle = "Setting value on the next view"
        })
    

    Declaration

    Swift

    public func goNext<T: UIViewController>(resolve asType: ViewIntanceFrom = .nib,
                                            customIdentifier: String? = nil,
                                            resolved instance: ((T) -> ())? = nil)

    Parameters

    resolve

    optional - How the view for this screen will be loaded, the default one is .nib.

    customIdentifier

    optional - String that is custom identifier to identify particular view

    resolved

    optional - Convenience closure that will return the loaded instance reference for this loaded view, it’s good when you want to set some values or pass any parameter not using the custom resolve, you will have the right reference to pass value.

  • Method responsible to navigate to the next screen using Modal Presentation, this method can automatically resolve too, if you do not need any of the parameters argument just call the method without implement any of the arguments and will automatically resolve according to you container stack declared.

    Note

    It has @discardableResult because you can use other helper methods just after calling this method as we always return FlowManager instance.

    Usage Example:

       // Simple implementation indicated for automatically navigation
       navigationFlow?.goNextAsModal()
    
       // Full implementation using all parameters
        navigationFlow?.goNextAsModal(screen: SecondViewController.self,
                                      resolve: .nib,
                                      animated: true,
                                      resolved: { resolveViewInstance in
               resolveViewInstance.nameForTitle = "Setting value on the next view"
        }, completion: {
           // Finished presenting this modal.
        })
    
         // Implementation specifying custom identifier
         navigationFlow?.goNextAsModal(screen: SecondViewController.self,
                                       customIdentifier: "MyCustomViewToLoad",
                                       resolve: .nib,
                                       animated: true,
                                       resolved: { resolveViewInstance in
                resolveViewInstance.nameForTitle = "Setting value on the next view"
         }, completion: {
            // Finished presenting this modal.
         })
    

    Declaration

    Swift

    @discardableResult
    public func goNextAsModal<T: UIViewController>(screen view: T.Type? = nil,
                                                   customIdentifier: String? = nil,
                                                   resolve asType: ViewIntanceFrom = .nib,
                                                   animated modalShow: Bool = true,
                                                   resolved instance: ((T) -> ())? = nil,
                                                   presentation style: UIModalPresentationStyle = .fullScreen,
                                                   completion: (() -> Void)? = nil) -> Self

    Parameters

    screen

    optional - The type of the screen that you want to go, need to be registered in your container flow stack.

    customIdentifier

    optional - String that is custom identifier to identify particular view

    resolve

    optional - How the view for this screen will be loaded, the default one is .nib.

    animated

    optional - If you want to show the modal view presentation animated or not, default is animated.

    resolved

    optional - Convenience closure that will return the loaded instance reference for this loaded view, it’s good when you want to set some values or pass any parameter not using the custom resolve, you will have the right reference to pass value.

    presentation

    optional - How the modal will be presented, as iOS 13 is not default fullScreen anymore, here the default will be fullScreen.

    completion

    optional - Called when the modal view is presented, so you know when success show.

  • Method responsible to navigate to the next screen using Modal Presentation, this method can automatically resolve too, if you do not need any of the parameters argument just call the method without implement any of the arguments and will automatically resolve according to you container stack declared.

    Note

    It has @discardableResult because you can use other helper methods just after calling this method as we always return FlowManager instance. You still have all the other method like the modal presentation without parameter.

    Usage Example:

       // Simple implementation indicated for automatically navigation
        navigationFlow?.goNextAsModalWith(parameters: {
            return ("Any Data")
        }, screen: SecondViewController.self })
    
        // Simple implementation with identifier
        navigationFlow?.goNextAsModalWith(parameters: {
            return ("Any Data")
        }, screen: SecondViewController.self, customIdentifier: "MyCustomViewToLoad" })
    

    Declaration

    Swift

    @discardableResult
    public func goNextAsModalWith<T: UIViewController, Resolver>(parameters data: @escaping () -> ((Resolver)),
                                                                 screen view: T.Type,
                                                                 customIdentifier: String? = nil,
                                                                 resolve asType: ViewIntanceFrom = .nib,
                                                                 animated modalShow: Bool = true,
                                                                 resolved instance: ((T) -> ())? = nil,
                                                                 presentation style: UIModalPresentationStyle = .fullScreen,
                                                                 completion: (() -> Void)? = nil) -> Self

    Parameters

    paramer

    Closure that expect a type that will be used when resolve this screen that you are calling.

    screen

    The type of the screen that you want to go, need to be registered in your container flow stack.

    customIdentifier

    optional - String that is custom identifier to identify particular view

    resolve

    optional - How the view for this screen will be loaded, the default one is .nib.

    animated

    optional - If you want to show the modal view presentation animated or not, default is animated.

    resolved

    optional - Convenience closure that will return the loaded instance reference for this loaded view, it’s good when you want to set some values or pass any parameter not using the custom resolve, you will have the right reference to pass value.

    presentation

    optional - How the modal will be presented, as iOS 13 is not default fullScreen anymore, here the default will be fullScreen.

    completion

    optional - Called when the modal view is presented, so you know when success show.

  • This is used to get back when you are navigating using flow manager, with this you can easy get back just one view or get back to the root view from you navigation controller stack, it’s even possible pass say to which screen you want to get back passing the type.

    Note

    It has @discardableResult because you can use other helper methods just after calling this method as we always return FlowManager instance.

    Usage Example:

        // Basic pop, just get back one screen animated
        navigationFlow?.getBack()
    
        // Specifying what type of the pop and the view that you want to get back
        navigationFlow?.getBack(pop: .pop(animated: true), screen: { viewToGo in
               viewToGo(FirstViewController.self)
        })
    

    Declaration

    Swift

    @discardableResult
    public func getBack<T: UIViewController>(pop withStyle: NavigationPopStyle = .pop(animated: true),
                                             screen view: (((T.Type) -> ()) -> ())? = nil) -> Self

    Parameters

    pop

    optional - Type of the pop action that you want to do, check NavigationPopStyle to see all possibilities, default is back one animated.

    screen

    optional - The type of the screen that you want to go, need to be registered in your container flow stack.

  • Flow Manager convenience initialiser for when you want to resolve the root view controller from your navigation controller receiving parameters.

    1 - When we are already in one storyboard, and we want to load another viewcontroller that is inside this storyboard you can resolve using rootInstance to resolve and will be set to as your root view controller inside your navigation controller.

    Example: We have setup our Storyboard, and we embed in on our view controllers our UINavigationController Them as we want to use the Flow Manager to handle our navigation, we need to get the reference to our view controller and set as our custom navigation, but we need to know who is the root of this navigation, so for this we pass the root type of the view.

    2 - When you are loading a completely new storyboard, as you will need to resolve your first / root view controller using the reference from the storyboar as it’s not loaded yet, so for this scenario you only pass the type of the first one root that will be resolved so we hande this resolution for you, otherwise will load with a black screen your navigation.

    Declaration

    Swift

    public convenience init<T: UIViewController, Resolver>(root instanceType: T.Type,
                                                           container stack: ContainerFlowStack,
                                                           parameters data: @escaping () -> ((Resolver)),
                                                           withCustom navigation: UINavigationController? = nil,
                                                           setupInstance type: ViewIntanceFrom = .nib,
                                                           dismissed navigationFlow: (() -> ())? = nil)

    Parameters

    root

    The view controller type that you will have as root view controller for you navigation controller.

    container

    It’s the ContainerFlowStack instance, it’s where we will look for the registered View Controller types in order to resolve and show on the screen.

    parameters

    Closure that expect the parameter that you want to be passed to when we resolve, need to follow the same order and type of the register that is expecting this parameter(s).

    withCustom

    optional - Custom UINavigationController it’s optional, if not we will use the default one from UIKit.

    setupInstance

    optional - It’s how will be our navigation, it’s a enum, if you don’t set we will assume that is nib view that you will be using.

    dismissed

    optional - Closure optional that can tell you when this navigation controller was completely closed.

  • Flow Manager convenience initialiser

    1 - When we are already in one storyboard, and we want to load another viewcontroller that is inside this storyboard you can resolve using rootInstance to resolve and will be set to as your root view controller inside your navigation controller.

    Example: We have setup our Storyboard, and we embed in on our view controllers our UINavigationController Them as we want to use the Flow Manager to handle our navigation, we need to get the reference to our view controller and set as our custom navigation, but we need to know who is the root of this navigation, so for this we pass the root type of the view.

    2 - When you are loading a completely new storyboard, as you will need to resolve your first / root view controller using the reference from the storyboar as it’s not loaded yet, so for this scenario you only pass the type of the first one root that will be resolved so we hande this resolution for you, otherwise will load with a black screen your navigation.

    Declaration

    Swift

    public convenience init<T: UIViewController>(root instanceType: T.Type,
                                                 container stack: ContainerFlowStack,
                                                 withCustom navigation: UINavigationController? = nil,
                                                 setupInstance type: ViewIntanceFrom = .nib,
                                                 dismissed navigationFlow: (() -> ())? = nil)

    Parameters

    root

    The view controller type that you will have as root view controller for you navigation controller.

    container

    It’s the ContainerFlowStack instance, it’s where we will look for the registered View Controller types in order to resolve and show on the screen.

    withCustom

    optional - Custom UINavigationController it’s optional, if not we will use the default one from UIKit.

    setupInstance

    optional - It’s how will be our navigation, it’s a enum, if you don’t set we will assume that is nib view that you will be using.

    dismissed

    optional - Closure optional that can tell you when this navigation controller was completely closed.

  • This is intend to when you are sending parameter but you are using Storyboard as navigation as when it’s the Storyboard the one responsible for resolve your instance, the best that we can provide is if you implement this method in our class so as soon was instantiated we will send and you will be able to receive parameter from the caller.

    Usage Example:

        // Send the data to the next view controller when using Storyboard
        navigationFlow?.goNextWith(screen: AutomaticallyFirstViewController.self, parameters: { ("Felipe", 3123.232, "Florencio", 31) })
    
       // Implemented in the View Controller that you intend to receive the data that will be sent.
        navigationFlow?.dataFromPreviousController(data: { (arguments: (String, Double, String, Int)) in
           let (first, second, third, fourth) = arguments
           debugPrint("First parameter: \(first) - Storyboard Automatically Navigation")
           debugPrint("Second parameter: \(second) - Storyboard Automatically Navigation")
           debugPrint("Third parameter: \(third) - Storyboard Automatically Navigation")
           debugPrint("Fourth parameter: \(fourth) - Storyboard Automatically Navigation")
        })
    

    Declaration

    Swift

    public func dataFromPreviousController<Parameter>(data parameter: (Parameter) -> ())

    Parameters

    data

    Closure that will receive the parameter, you need to declare the types the same order that you are expecting.