I'm building my first app using VIPER architecture. I have two VCs: the main one and the modal one (presented modally from the main VC). There is a tableView inside my modal VC, and when the user selects a row there, I need to pass it to presenter and then from presenter to the main VC. I also need to keep the selected row highlighted in the modal VC, so if I close it and then present it again, the row will be still highlighted. I'm confused, because I don't know what's the best way to do it. What I've tried is including my modal VC into configurator and calling configurator two times: in the main VC and then again in the modal VC. It works fine. That's what my configurator looks like:
protocol ObserverConfiguratorProtocol {
func configure(with mainViewController: ObserverViewController, with modalViewController: CurrenciesViewController)
}
class ObserverConfigurator: ObserverConfiguratorProtocol {
func configure(with mainViewController: ObserverViewController, with modalViewController: CurrenciesViewController) {
let presenter = ObserverPresenter(view: mainViewController)
let interactor = ObserverInteractor(presenter: presenter)
let router = ObserverRouter(view: mainViewController)
mainViewController.presenter = presenter
modalViewController.presenter = presenter
presenter.interactor = interactor
presenter.router = router
presenter.view = mainViewController
presenter.modalView = modalViewController
}
}
viewDidLoad() in the main VC:
override func viewDidLoad() {
configurator.configure(with: self, view: CurrenciesViewController())
}
viewDidLoad() in the modal VC:
override func viewDidLoad() {
configurator.configure(with: ObserverViewController(), view: self)
}
Anyway, I'm not sure it corresponds VIPER principles. Does it, or there are better solutions? Any help is appreciated!
CodePudding user response:
If you are going to present another view controller, it should be on its own module for VIPER. ObserverViewController
should not know about the presence of CurrenciesViewController
.
Instead, only ObserverRouter
should know about how to build CurrenciesModule
and present and pass data into it. Something like this:
final class CurrenciesBuilder {
@available(*, unavailable) private init() { }
static func build(routerDelegate: ObserverRouterDelegate, selectedIndex: IndexPath?) -> CurrenciesViewProtocol {
let service = CurrenciesService()
let interactor = CurrenciesInteractor(service: service, selectedIndex: selectedIndex)
let view = CurrenciesViewController(nibName: String(describing: CurrenciesViewController.self), bundle: nil)
let router = CurrenciesRouter(vc: view)
router.delegate = routerDelegate
let presenter = CurrenciesPresenter(interactor: interactor,
view: view,
router: router)
view.presenter = presenter
return view
}
}
So, whenever you need to present CurrenciesViewController
, you could pass selectedIndex
and it can highlight it, if applicable. I would pass the information to the interactor, and let presenter know about it and do some changes in the view.
For passing the selectedIndex
back to ObserverViewController
, you could create a ObserverRouterDelegate
that can be triggered when CurrenciesViewController
is going to be dismissed.
protocol ObserverRouterDelegate: AnyObject {
func didDismiss(with selectedIndex: IndexPath?)
}
To wrap up; there should be 2 modules, ObserverModule
and CurrenciesModule
, that should have bidirectional connection through their routers. Since routers are responsible for presenting/dismissing views (and they hold a connection to their views), they can update their views and/or let the peer router update their own views.