简体   繁体   中英

Reusing View Controller within Coordinator pattern

I have a flow ( Flow A ) of ViewController's in my iOS app. Flow A is rather complex (depending on circumstances, some view controllers are shown early, or not shown at all, etc.). In order to handle that I am using the coordinator pattern.

The code (simplified):

protocol Coordinator {
    func start()
}

protocol FlowACoordinatable {
    var coordinator: FlowACoordinator
}

class FlowACoordinator: Coordinator {

    private var navigationController: UINavigationController

    private var firstVC: FirstViewController
    private var secondVC: SecondViewController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func start() { ... }

    func present(_ viewController: (FlowACoordinatable & UIViewController)) {
        viewController.coordinator = self
        self.navigationController.pushViewController(viewController, animated: true)
    }

    ...
}

class FirstViewController: UIViewController, FlowACoordinatable {

    var coordinator: FlowACoordinator?

    func buttonTapped() {
        self.coordinator?.goToNextStep()
    }
}

....

FlowACoordinator contains logic about how and when to present view controller's using the present() method. So far so good.

Now I have a second flow, Flow B , mostly different from Flow A. Except I want to share a view controller between the two, let's call it SharedViewController . This is where things get strange, because I have no really good idea on how to share this view controller between the two.

The problem: I have a two way communication - the coordinator sets itself as the coordinator of the view controller it presents & the view controller calls methods on the coordinator as a response to user interaction. SharedViewController is managed by one of two Coordinators, though, and somehow it has to pass information to the current Coordinator, regardless of which one it is.

So far I found two solutions, both of them not satisfying:

  1. An additional coordinator that handles just SharedViewController - this is a lot of overhead and largely defeats the purpose of Coordinators.

  2. Implementing FlowACoordinatable , FlowBCoordinatable , ... in SharedViewController , and have multiple coordinator properties and call all of them at appropriate times. Also a lot of overhead, boilerplate code and calls to coordinators.

Any ideas on how to solve this nicely?

I am having the same situation and I am also not sure what is the best scenario to deal with it. I have a viewController that must be used in different coordinators.

It is OK when the given viewController does not need a coordinator itself. For instance, let's call it a DisplayPopupViewController. I create a Protocol called CanDisplayPopupProtocol:

protocol CanDisplayPopupProtocol {}

extension CanDisplayPopupProtocol where Self: Coordinator {
    func toDisplayPopupViewController() {
        let vc = DisplayPopupViewController.instantiate()
        navigationController.pushViewController(vc, animated: true)
    }
}

Then in Coordidnator1:

extension Coordinator1: CanDisplayPopupProtocol{}

And in Coordinator2:

extension Coordinator2: CanDisplayPopupProtocol{}

Now both coordinator have the toDisplayPopupViewController() method.

As I said before this is fine when I don't need to pass the coordinator to the viewController, in this case the DisplayPopupViewController does not need a coordinator, since it will be dismissed and do not need any navigation.

But it gets a lot more complex when there is a need to assign a coordinator to the viewController in this case, which os the coordinator I pass?

The solution I found which isn't very elegant in my point of view is to change the coordinator type in the viewController to the Coordinator protocol, so instead of this:

weak var coordinator: Coordinator1?

I will use:

weak var coordinator: Coordinator?

And then in the CanDisplayPopupProtocol I will test which coordinator I am dealing with and assign the correct coordinator to the viewController, like this:

protocol CanDisplayPopupProtocol {}

extension CanDisplayPopupProtocol where Self: Coordinator {
    func toDisplayPopupViewController() {
        let vc = DisplayPopupViewController().instantiate()
        switch self {
        case is Coordinator1:
            vc.coordinator = self as? Coordinator1
        case is Coordinator2:
            vc.coordinator = self as? Coordinator2
        default: break
        }
        navigationController.pushViewController(vc, animated: true)
    }
}

This isn't pretty and there is another downside to it. Inside the DisplayPopupViewController every time I need to use one of the coordinator's methods I need to test which coordinator type I am using.

switch coordinator {
case is Coordinator1:
    (coordinator as! Coordinator1).toDisplayPopupViewController()
case is Coordinator2:
    (coordinator as! Coordinator2).toDisplayPopupViewController()
default: break
}

I am sure this is not the best use of Protocols, hope someone following this thread will have a better solution for this problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM