Tired to write massive view controllers according apple MVC? There is an exit! Look at our non-reactive MVP solution. All you need is declare instances protocols and their implementations and compose them altogether!
- swift 5.3
- iOS 10 or bigger
- First, declare
State
andViewModel
implementations:
// MyState.swift
final class MyState {
var someVariable = "some value"
}
// MyViewModel.swift
final class MyViewModel: ViewModel {
var someVariable: Any
required init(delegate: MyViewModelDelegate) {
// initialize all required variables here
}
}
Please note, that required ViewModel
initializer takes associated type ViewModelDelegate
that can be used to initialize stored properties. You can pass here any protocol or use GenericViewModelDelegate<State>
. Last one provides the access to module state.
Variables of state and view model cold be not casted one to other, so you can make some specific stuff in view model initializer.
- Then, declare a protocols to send messages between architecture layers:
// MyModule.swift
protocol MyModuleInput: ModuleInput<FirstState> {
func doSomeSpecificStuff()
}
protocol MyModuleOutput {
func myModuleSendOutputMessage(_ moduleInput: MyModuleInput)
}
// MyViewController.swift
protocol MyViewOutput: ViewOutput {
func myButtonEventTriggered()
}
- Declare
ViewController
that implementsViewInput
protocol:
// MyViewController.swift
final class MyViewController: UIViewController {
typealias Output = MyViewOutput & ViewOutput
typealias ViewInput = MyViewInput
var output: Output
var viewModel: MyViewModel
}
extension MyViewController: View, MyViewInput {
func update(with viewModel: MyViewModel, force: Bool, animated: Bool) {
// TODO: Update view if needed
}
}
You do not need assign output
and viewModel
to some value - GenericModule
will make it automatically.
- Declare
Presenter
and implement or required protocols:
// MyPresenter.swift
final class MyPresenter: Presenter<MyState, MyViewController, MyModuleInput, MyModuleOutput, MyModuleDependencies> {
}
extension MyPresenter: MyModuleInput {
func doSomeSpecificStuff() {
// Handle module input event
}
}
extension MainPresenter: MainViewOutput {
func myButtonEventTriggered() {
// Handle view event
}
}
extension MainPresenter: MyModuleDependencies {
// Implement `ViewModelDelegate` here
}
Note that you should not implement ViewModelDelegate
if ViewModel
takes GenericViewModelDelegate
. Also, if you don't want to implement ViewModelDelegate
in Presenter
then you can override makeViewModelDelegate()
method and return custom intance:
// MyPresenter.swift
...
override func makeViewModelDelegate() -> MyViewModelDelegate {
// Return here custom view model delegate instance
}
...
- Declare
Module
that compose altogether:
// MyModule.swift
class MyModule: Module<MyPresenter> {
}
- All you need to do now - instantiate module bundle:
// MyCoordinator.swift
func startMyModule {
let module = MyModule(state: .init(), dependencies: MyModuleDependencies, output: self)
let viewController = module.viewController
//TODO: Make something with `viewController`
}
- All done:
ViewController
will holdPresenter
viaViewOutput
protocol usingoutput
property.Presenter
can send messages toViewController
viaViewInput
protocol using weakview
property. Also,Presenter
can send message to someModuleOutput
protocol usingoutput
property.
github rosberry/GenericModule
dependencies: [
...
.package(url: "https://github.com/rosberry/GenericModule", from: "1.0.0"),
...
]
This project is owned and maintained by Rosberry. We build mobile apps for users worldwide 🌏.
Check out our open source projects, read our blog or give us a high-five on 🐦 @rosberryapps.
This project is available under the MIT license. See the LICENSE file for more info.