P.S:
This is not a opinionated question. Its a legitimate doubt on wiring various modules in VIPER. Its a theoretical question so no code attached. I just need to know how do we wire up View-Presenter-Router in this specific case without breaking ground rules of VIPER
I am trying my hands-on with VIPER for the first time. Here is my fundamental understanding of VIPER.
View: Supposed to show UI Controls and capture IBActions
and call its presenter's delegate method to process events
Presenter: Will process all the UI related data and prepares data for rendering and hands over the data back to View. Whenever a screen transition is required it calls its router and asks the router to perform transition
P.S: Presenter will not have any UIComponents in it. So no import UIKit
statement in presenter.
Router: is responsible for performing screen transitions usually does it with the help of wireframes (optional but good to have such class in app)
Interactor: Contains all business logic.Presenter will call Interactor whenever processing based on business logic is required.
Entity: POJO classes (Simple Swift Objects or Core data entities).
Now comes the question:
If my assumptions are right, Presenter is supposed to be a plain Swift class with no UIKit
access.
If true, assume I press a button on my ViewControllerA
and I need to push another ViewControllerB
on top of it, obviously ViewControllerA
will talk to PresenterA
and tells it that button is tapped, now PresenterA
should talk to RouterA
and tell it to push ViewControllerB
.
Because Router can have access to UIKit
I can easily create a new instance of ViewControllerB
using storyboard instance or from xib, but in order to push that instance I need the ViewControllerA's instance.
But PresenterA
can not hold a reference to ViewControllerA
or can be passed as argument to PresenterA
in function because UIViewController
belongs to UIKit
and Presenters are not supposed to have UI statements.
Probable Solutions I can think of:
Solution 1:
While creating Router instance, pass corresponding ViewController
instance as a part of its init (Dependency injection phase) that way Router will always have reference to ViewController it belongs to
Solution 2:
Have a Router declare its protocol and implement it in ViewController and whenever reference to ViewController needed use delegate of Router. But this contradicts the rules of VIPER that routers are not supposed to talk to View.
Am I thinking straight? Am I right with my assumptions? If yes what is the correct way to deal with this problem, Please suggest
Router
is the object responsible for navigation, it's natural it would hold the reference toViewControllerA
, since it presented it in the first place. Similarly,Router
would hold the reference toUINagivationController
orUITabBarController
if your application uses them – EtalonViewControllerA
is supposed to be presented byRouter
in the first place, so it should be created in theRouter
andRouter
should retain reference to it. – EtalonRouter
in entire application, that's the whole point of VIPER - a single object responsible for navigation, not eachViewController
having its own router. Also, you speak ofViewControllers
having their ownPresenters
each, where I would argue thatViewControllers
should bePresenters
– Etalon