iOS app architecture we use.
MVPCF = Model View Presenter Coordinator Factory;
It is similar to the combination of Reactive MVP + Coordinator + Factory. Presenter is the same thing as ViewModel in MVVM but it has a different name. ViewModel name is used for a different approach;
SOLID principles are the core of our architecture.
For our architecture, we created a pretty simple and small library RCKit aka Reactive Clean Kit, that contains everything we need for building an app using this architecture.
1. For assembling project we use DI pattern with Swinject library.
App has shared Container where we register all shared dependencies we need to use across the whole app.
Every flow has its own folder. Every flow has its "manager" called Coordinator. Every flow has its own DI Container that contains specific dependencies for the flow.
This object is responsible for handling navigation through the application, handling push notifications, and other routing actions.
Coordinator uses one container viewcontroller like UINavigationController or UISplitViewController.
Coordinator builds the screens (Modules) and controls the flow.
The only exception is a main ApplicationCoordinator that accepts UIWindow instead of container ViewController.
3. Every Module for example has the following structure:
Abstraction for creation anything. Factory is very important part of this architecture, because it injects dependencies. BaseFactory contains Container we use to inject dependencies that we need. Factory can be used for Modules as well as building Cells, NSOperations, etc where we need to inject something. We can inject factories where we need and it gives us a lot of flexibility!
protocol <Name>Factory {
func make()-> <Name>Routes
}
Abstraction for Routes of the Module. ViewController implements this protocol. Closures fire in Coordinator and Coordinator performs what is needed.
protocol <Name>Routes: ModuleRoutes {
var canceled: (()->Void)! { get set }
var finished: ((String)->Void)! { get set }
}
Abstraction for Presenter. It has default implementation. It contains a logic of the presentation. It has the bindings for input and output. Presenter contains structures called ViewModel as well as plain types, ready to use in View.
// for example
struct <SomeCell>ViewModel {
let name: String
let dateFormatter: String
}
protocol <Name>Presenter {
var searchInput: BehaviorRelay<String?> { get }
var searchResults: BehaviorRelay<[<SomeCell>ViewModel]> { get }
var nextButtonAvailable: BehaviorRelay<Bool> { get }
}
Abstraction for view. It has default implementation inherited from UIView.
It contains custom view for ViewController.
protocol <Name>View: ViewContainer {
var addButton: UIButton { get }
var textField: UITextField { get }
}
Inherired from UIViewController Implements Routes protocol.
It is responsible for:
- binding Presenter to View;
- registering for events as a button tap;
- firing Routes closures. Basically it is the main building structure of screens.
class Default<Name>Presenter: <Name>Presenter{
let name: BehaviorRelay<String?> = BehaviorRelay(value: nil)
let addButtonAvailable: BehaviorRelay<Bool> = BehaviorRelay(value: false)
private var creating = BehaviorRelay(value: false)
private var disposable: Disposable?
private let disposeBag = DisposeBag()
private let modelManager: ModelManager
private let reachabilityManager: ReachabilityManager
// injecting via initializer
init(modelManager: ModelManager, reachabilityManager: ReachabilityManager){
self.modelManager = modelManager
self.reachabilityManager = reachabilityManager
}
}
Every dependency should have only one responsibility!
For example:
protocol ReachabilityManager {
var connectionIsReachable: BehaviorRelay<Bool> { get }
}
Or this:
protocol FirstLaunchManager {
func performTasksOnFirstLaunch()
}
Or this:
protocol LogoutManager {
func logout()
}
-
MVVM and MVP. MVPCF uses MVVM and MVP as a base architecture and improves both with coordinators and factories;
-
VIPER. It has a Presenter as the main Object of the module. Router is not Obvious. MVPCF has ViewController as the main object of the module and Coordinator that handles the navigation. Also, standard VIPER is not reactive from the box.
-
RIBs. Interactor in RIBs is the main object of module, but MVPCF has ViewController as the main object. Our approach is more obvious for typical developers.