MaryLitv21
MaryLitv21

Reputation: 448

Pass data between VIPER modules

I'm building my first app using VIPER. I have two modules: ObserverModule and CurrenciesModule. I modally present CurrenciesViewController from ObserverViewController, and when CurrenciesViewController is being dismissed, I need ObserverViewController to know about it. I've found that I need to use delegates for that purpose. So I create a protocol:

protocol CurrenciesDelegate {
    func onCurrenciesScreenDismissed()
}

Then I create a property of this protocol inside my CurrenciesPresenter:

 var delegate: CurrenciesDelegate?

Then, when my CurrenciesViewController is being dismissed, and notifies my presenter about it, I start calling my delegate from presenter in such way:

  delegate?.onCurrenciesScreenDismissed()

Then, I sign my ObserverPresenter to CurrenciesDelegate and realize its method. Inside of it I tell my view to update:

extension ObserverPresenter: CurrenciesDelegate {
    
    func onCurrenciesScreenDismissed() {
        view.updateView()
    }
    
}

I realize that now it's a time to make dependencies, and it's the most confusing part. I've tried doing it inside of CurrenciesConfigurator, but it doesn't work:

protocol CurrenciesConfiguratorProtocol {
    func configure(with viewController: CurrenciesViewController)
}

class CurrenciesConfigurator: CurrenciesConfiguratorProtocol {
    func configure(with viewController: CurrenciesViewController) {
        let presenter = CurrenciesPresenter(view: viewController)
        let interactor = CurrenciesInteractor(presenter: presenter)
        let router = CurrenciesRouter(view: viewController)
 
        viewController.presenter = presenter
        
        presenter.interactor = interactor
        presenter.router = router
        var currenciesDelegate: CurrenciesDelegate!
        presenter.delegate = currenciesDelegate
    }

}

That's how I call my configurator from my CurrenciesViewController:

let configurator = CurrenciesConfigurator()
    
       override func viewDidLoad() {
        configurator.configure(with: self)
        presenter.viewLoaded()
       }

I have no idea of how to configure dependencies make it work as expected. Any help is appreciated!

Upvotes: 2

Views: 1579

Answers (1)

Andreas ZUERCHER
Andreas ZUERCHER

Reputation: 902

@MaryLitv21, the good news is that you are focused on the correct goal: managing dependencies in VIPER, as visualizing the correct dependencies is the path to true understanding of VIPER. The bad news is that your current mode of thinking is skew & divergent enough to the path that you need to travel that old habits are too comforting.

  1. In general, no view controller should communicate ingress or egress to any other view controller. There might occasionally be exceptions to that for multiple views incredibly intimately tied together on the same screen but this is a good rule of thumb to almost always obey in VIPER. Every view controller knows only its own pixel stuff for its own view. All navigation functionality is now forbidden, as that where-we-go-next functionality is now a presenter-router stimulus-response conversation via entities across the router façade (explained below), not however some Apple (or Google) framework suggests doing it from one view controller to another view controller. The same lesson applies any time that there is an Apple-framework way (or Google-framework way on Android) that seems to compete with VIPER's zone-quarantine rules; when such a conflict arises, VIPER defines that conflict away as forbidden (although there might be rare exceptions when views conspire as an aggregate on the same screen concurrently unbeknownst to the app-domain-centric presenter as some Apple-centric amalgamation of cooperating views on the same screen which might have no equivalent if the app were ported to, say, Android—but such situations are rare, so the tail/exceptional-case should not wag the dog/wise-grand-vision).
  2. Each view controller should be focused solely on managing & interacting with Apple-stuff in Apple-think-world (or if your app were ported to Swift-itself-without-Apple-iOS/MacOS frameworks running atop Android: Google-stuff in Goolgle-think-world); the view controllers should never do anything in your app's mission/purpose/design/choices, as the view controller is now demoted to being a pixel manipulator, deferring all app-domain stuff other than Apple-think pixels to the p zone through the response direction of the v zone's façade. This is your key lesson to learn. The rest of your app needs to be your-team's-stuff in your-team's-world without contamination from Apple-think from the quarantined Apple-think-world (and when ported to Android, without contamination from Google-think from the quarantined Google-think-world). You will know that you are achiving this life-purpose correctly when you see the Apple-stuff so quarantined so harshly that you see that it can be swapped out at a cut-here-with-scissors line for Google-stuff instead).
  3. Now without the habits of Apple-think (or Google-think) to use directly at all, you(r team) must treat themselves as if it is, say, 1985 all over again: you must architect software from scratch, using those architectural dependencies & information flows that naturally make perfect sense to your app & its app domain. This is where VIPER's guidance applies. Instead of view controllers being the (massive-view-controller, big ball of mud) central nexus of the app, the view controllers are demoted to doing only pixel stuff and only when pixel stuff is what your app needs right now—hence the word: quarantine. So with that in mind, presenter becomes the central nexus without any (and I repeat: any!) contamination from the platform underneath your app. This means even choosing which Swift language features are forbidden in which information flows and in which direction. Because it is the equivalent of, say, 1985, everything is soft clay, which requires you(r team) to have much self-discipline & clarity of vision from a software architect, much the same as a building architect, to assure that a higgly-piggly mess does not ensue (a.k.a. Big Ball of Mud architecture; massive view controller & the like—both of which are what VIPER is forestalling).
  4. With Presenter being the central nexus for all app-domain ingress & egress information flows, Entities are the lingua franca that Presenter utilizes to communicate the information flows across v, i, p, & r zone boundaries. One does not grab a hammer/delegates/frameworks to make all interzone information flow look like a nail; instead the software architect overtly wisely chooses the right tool for the job to accomplish the information flow wisely & simply without downside or detriment. If the software architect is pulling “a snake-pit of … retain cycles and memory leaks when multiple different objects in a VIPER stack hold strong references to each other” unwisely out of thin air, then the software architect didn't plan the information flows out wisely enough. The software architect must utilize such design patterns as factory to create instantiations in other v, i, & r zones without the Presenter central nexus knowing the identifier of those particular classes. To do this, each v, i, & r zone publishes a façade to p zone, where that façade contains the factory along with other interzone app-domain-Entity-only ways of communication of information flows. Instantiating whatever (which might be a single object or might be a tightly interworking community of objects) is one such information flow. Destroying it (e.g., destroying whatever view controller[s] that were doing their pixel business when leaving that screen) is another such information flow. Evoking the v, i, or r response by the p zone feeding in an Entity-expressed stimulus (e.g., hey, router: where does the app go next based on this stimulus in this context, where “this” is expressed abstractly as, say, see-the-graph-now from the mathematical-formula context that we are now leaving, not a particular UI gesture [swipe left; pinch] from a particular UI view). Another information flow expressed in app-domain-centric terms would be: e.g., hey, database interactor, retrieve the customer data for this search criterion expressed in Entities, neither here is a SQL statement nor CoreData stuff in the p-i zone boundary. Keep doing this a hundred times for each information flow to Entity-ize it & app-domain-ize it instead of using infrastructural ways of expressing it.
  5. MaryLitv21, since you were most focused on a specific communication technology (i.e., delegates), I should speak to specific alternative communication technologies to utilize instead across the v, i, p, & r zone boundaries. The trouble with delegate is that in its most natural/naïve form, delegates require vastly vastly vastly too much awareness of the identifiers of the classes. This awareness directly destroys the quarantining that is the entire purpose of VIPER. Instead, pretending like it is, say, 1985 again when all the world is the software architect's blank canvas without Apple's (or Google's) habits to directly invoke from a framework or from a language feature, the VIPER software architect knows (or decides-right-then-&-there-then-knows-henceforth) which thread(-pool)s are intended to execute what, either today or in well-planned evolution of the app. Hence, the software architect tells the team that for this information flow, this communications technology (e.g., asynchronous invocation of this API in the façade; simple subroutine invocation on the same thread; message queue posting/ingress) will be utilized when evoking such-&-such functionality by the Presenter interzone across v, i, or r zone boundaries by utilizing this API composed entirely of Entities as parameters/fields/attributes.

This diagram below should be posted on the wall of every person on your team. Indeed, it would be best to redraw it with each façade shown in the v, i, & r zones, so that it is obvious what the p zone is actually evoking Entity-expressed responses from by Entity-expressed stimuli via the thin veneer of the façade, not by invoking anything in the guts of within the v, i, or r zone. Also, each of the bilateral pairs of arrows should be labeled in your(-team's) redraw of this diagram to overtly state which communication technology that it is (e.g., asynchronous operation with callback upon completion, subroutine invocation that pends/suspends-invoker's-execution until completion, message queue posting & pending). You(r team) must have a published API for each façade between zones; perhaps developers can add to it themselves, which makes the software architect a police-esque veto function when some developer destroys the quarantine; perhaps the software architect owns the published façade's API, where a developer bypassing the façade to destroy each zone's quarantine lowers the developer's performance review harshly. The interzone quarantine is inviolable in VIPER. That inviolability said, the usage of Entities as the lingua franca across zone boundaries for interzone evocations of APIs in the façades is implied but not overtly shown in the diagram below.

VIPER inter-zone dependency diagram

Upvotes: 0

Related Questions