1. Brief introduction of architecture design mode
- The current mainstream architecture modes are as follows:
- MVC
- MVP
- MVVM
- VIPER
- The essential purpose of architectural design patterns
MVP, MVVM and VIPER are all evolved from MVC. They are all proposed to solve practical problems in the development process. They have their own advantages and disadvantages and applicable scenarios, and their essential purpose is to continuously separate logic from ViewController
- In terms of function, the above four architectural design patterns can be divided into three levels:
Model
Layer: responsible for data access, which can be divided into the following two categories- Business processing: daily development
DAO
,Service
Both are business request modules derived from the Model layer that handle user-submitted requests. - Data hosting: Entity classes used specifically to host business data, such as Student, User, and other entities defined in development.
- Business processing: daily development
View
Layer: Responsible for the display of viewsController/Presenter/ViewModel
: A mediator between the Model and View, generally responsible for updating the Model when the user manipulates the View and updating the View when the Model changes
- A good architecture or architectural pattern should meet the following three requirements:
- Good division of responsibilities
- testable
- Ease of use
2. MVC
2.1 Traditional MVC
- Existing problems:
- Difficult to unit test
- View and Model are seriously coupled
- Entities are coupled to each other
2.2 Apple’S MVC
Apple believes that in the traditional MVC pattern, the View directly observes the Model object through the Observer mode to get relevant notifications, and this design will result in the View and Model object cannot be widely reused because of the coupling relationship between the View and the Model it is observing. Therefore, Apple’s VERSION of MVC is basically the same as traditional MVC, except that it isolates the View and Model.
Massive-View-Controller
Dispatch and cancel network requests, business logic processing, delegate and datasource processing
2.3 How to use it in the project
- A less formal way to write this is that Apple’s MVC principle, Model and View, is decoupled, but this principle is violated in many projects
var userCell = tableView.dequeueReusableCellWithIdentifier("identifier") as UserCell
userCell.configureWithUser(userModel) // The Cell(a View) is directly bound to a Model
Copy the code
The Cell is tied directly to a Model, but this happens all the time, and it’s not a problem to use this way, but it’s not easy to unit test.
If you are following MVC strictly, you should configure the cell from the Controller (setting the cell’s property assignment directly) rather than passing the Model into the cell, but this will increase the code for the Controller.
- My personal suggestion
Define the protocol, model inherits the protocol
protocol UserDataProtocal {
var name: String {get}
var age: Int {get}}struct User: UserDataProtocal {
var name: String
var age: Int
}
var data = userModel as? UserDataProtocal
var userCell = tableView.dequeueReusableCellWithIdentifier("identifier") as UserCell
userCell.configureWithUser(data)// The data protocol is passed
Copy the code
2.4 summarize
Disadvantages of Apple MVC:
- The View and Model are separated, but the View and Controller are still tightly coupled.
- You can only test your Model.
- Contoller had too much on his plate and not enough division of responsibilities
- Not suitable for small, medium sized applications
Advantages of Apple MVC:
- Low coupling
- High reusability
- Low lifecycle cost
- MVC takes the art out of developing and maintaining user interfaces
- High maintainability
- The deployment of fast
Apple’s MVC, in fact, is a view-driven design, and controllers exist only to manage views. Apple left it up to us to design the relationship between UIViewController and Model.
3. MVP
MVP evolved from the MVC Model, where controllers were replaced with presenters, in order to completely sever the connection between the View and the Model, with presenters acting as a bridge.
- View is responsible for the presentation and layout management of the interface, exposing the View update and data retrieval interface to Presenter
- Presenter is responsible for receiving events from the View, updating the View through the interface provided by the View, and managing the Model
3.1 the characteristics of
- The View does not communicate with the Model, but is delivered by a Presenter, which completely separates the Model from the View. The main application logic is implemented in the Presenter
- A View is very thin and does not deploy any business logic, which is called a Passive View, meaning there is no initiative, whereas a Presenter is very thick and all the logic is deployed there
- A Presenter is not directly associated with a specific View, but interacts with a defined interface that allows the Presenter to remain the same when changing the View so that it can be reused.
- The Model is responsible for accessing network data
3.2 the demo
For the sake of space: here is a simple abbreviated version
- controller
class ViewController: LoginViewProtocol {
let presenter = LoginPresenter.init(view: self)}Copy the code
- Presenter
class LoginPresenter {
var model: LoginModelProtocol?
weak var view: LoginViewProtocol?
init(view: LoginViewProtocol?). {self.view = view
model = LoginModel(present: self)}Copy the code
- model
class LoginModel {
weak var present: LoginPresenter?
init(present: LoginPresenter?). {self.present = present
}
func login(usrName:String,pwd:String,callback:((String)->Void)) {
// The network request processing method is encapsulated and cannot be coupled
print("Into the model")
HttpUtils.post(usrName: usrName, pwd: pwd) { (result) in
self.present.dosomething()
}
}
Copy the code
3.3 Advantages of MVP
- The model is completely separate from the view, and we can modify the view without affecting the model
- Models can be used more efficiently because all interaction takes place in one place — within Presenter we can use a Presenter for multiple views without having to change Presenter logic, which is very useful because views change more often than models
- Put logic in Presenter and unit test it out of the user interface
3.4 Disadvantages of MVP
- Interface explosion
- Presenter is very heavy
3.5 The difference between MVC and MVP
- In traditional MVC, the View interacts directly with the Model layer and reads data, not through the Controller
- In MVP, the View does not interact directly with the Model layer, but with the Presenter and Controller, and all interaction takes place inside the Presenter
4.MVVM
The primary MVVM is the ViewModel
Model
: Provides the data modelView
: Responsible for view displayViewModel
: Used to describe the state of the View, such as the color of the View, displayed text and other attribute class information, the View is abstracted into a special Model, and hold and manage the Model, maintain business logic
4.1 the characteristics of
MVVM
Compared withMVP
That will bePresenter
becomeViewModel
.ViewModel
You can View it as ViewThe data modelA combination of Presenter and Presenter.- The data in MVVM can be bidirectional binding, that is, the data in ViewModel will also change when the data in ViewModel changes, and the data in ViewModel will also change when the data in ViewModel changes
- The MVVM pattern is similar to the MVC pattern in that its main purpose is to separate views from models.
4.2 Two-way binding of data
In MVP, a View describes itself as an interface, and in MVVM, as a ViewModel. So how does the ViewModel update its changes to the View? So MVVM often comes with data binding, which can be achieved using KVO or Notification technologies.
If we don’t want to do it ourselves, then we have two options:
- Binding based on KVO, such as RZDataBinding and SwiftBond
- Fully functional responsive programming, e.g. ReactiveCocoa, RxSwift
4.3 Advantages of MVVM
- Independent development: Developers can focus on business logic and data development (ViewModel), designers can focus on page design, and Expression Blend makes it easy to design interfaces and generate XML code
- Testable: Interfaces have always been harder to test, but tests can now be written against the ViewModel
- Reusability: You can put some view logic in a ViewModel and have many views reuse that view logic
4.4 MVVM shortcomings
- Data binding makes bugs hard to debug.
- For large projects, data binding requires more memory
4.4 the demo
Here because of the length reasons, do not write too fine, there is a general understanding of the line there are a lot of implementation plan, you can know which suits you
- The definition of the ViewModel
class RegisterViewModel {
// The signal to refresh the control
private let refreshSubject = PublishSubject<Void> ()// For external subscriptions
let result: Driver<RegisterStatsModel>
// loading
let loading: Driver<Bool>
// error
let error: Driver<Error>
init(dateTypeAndDate: Driver< (DateType.Date)>) {
loading = loadingTracker.asDriver()
error = errorTracker.asDriver()
result = Driver.combineLatest(refreshSubject.asDriverOnErrorJustComplete(), dateTypeAndDate)
.flatMapLatest{ (_, arg) -> Driver<RegisterStatsModel> in
let (dateType, date) = arg
return RegisterService.getRegisterStatis(date: date, type: dateType)
}
}
}
Copy the code
- ViewController
func bindViewModel(a) {
viewModel
.result
.drive(onNext: { [unowned self] model in
//dosomething
}
})
.disposed(by: rx.disposeBag)
// loading
viewModel.loading
.drive(view.rx.toastActivity)
.disposed(by: rx.disposeBag)
// error
viewModel.error
.drive(view.rx.toast)
.disposed(by: rx.disposeBag)
}
Copy the code
💣💣💣 In the actual use process, I found that many project views are still passed in model, not ViewModel. In fact, this violates the principle of MVVM, which needs to be noted
4.5 Questions raised by ViewModel
Putting the business logic into the ViewModel takes the load off the UIViewController, but it just shifts the problem, and the ViewModel will end up just another Massive ViewModel.
Compared to MVP, MVVM abstracts views in a more elegant way. However, it is similar to the MVP in that it decouples the View from the Model and still does not further subdivide the Controller.
So how to further divide the responsibilities of Controller? The answer is VIPER.
5. VIPER
The VIPER architecture is designed based on the external-in dependencies, which are presented as: View -> Presenter -> Interactor -> Entity->Router
5.1 Division of Responsibilities
View
- Provide complete view, responsible for view composition, layout, update
- Provides an interface for presenters to update views
- Sends view-related events to the Presenter
Presenter
- Receives and processes events from the View
- Request the Interactor to invoke the business logic
- Provides data from the View to the Interactor
- Receives and processes data callback events from Interactor
- Notify the View to update
- Switch to another View through the Router
Router
- Provides the function of jumping between views, reducing the coupling between modules
- Initialize VIPER modules
Interactor
- Maintain key business logic functions and provide existing business use cases to presenters
- Maintain, obtain, and update the Entity
- When business related events occur, handle the event and notify the Presenter
Entity
- Ordinary entities
5.2 the advantages
- Good testability. UI testing and business logic testing can be done separately.
- Easy to iterate. Each part follows a single responsibility, and it’s clear where the new code should go.
- High isolation and low coupling. One module’s code does not easily affect another.
- Easy to work in a team. Clear division of labor in each part, easy to unify the code style in team cooperation, can quickly take over other people’s code.
5.3 disadvantages
- The larger the number of classes in a module, the larger the amount of code, the more time it takes to design interfaces between layers.
- Initialization of a module is complicated. To open a new interface, you need to generate a View, Presenter, and Interactor, and set up dependencies among them. There is no native way to set up complex initialization in iOS
5.4 the demo
Due to excessive code, the complete implementation was put on Github via the VIPER code portal
View some demo code
protocol ReposViewType: class {
var presenter: ReposPresenterType? { get set }
func didReceiveRepos(a)
func showLoading(a)
func hideLoading(a)
func displayAlert(for id: Int)
}
protocol ReposWireframeType: class {
static func createReposModule(a) -> UIViewController
}
protocol ReposPresenterType: class {
var view: ReposViewType? { get set }
var interactor: ReposInteractorInputsType? { get set }
var wireframe: ReposWireframeType? { get set }
func onViewDidLoad(a)
func didChangeQuery(_ query: String?)
func didSelectRow(_ indexPath: IndexPath)
func numberOfListItems(a) -> Int
func listItem(at index: Int) -> RepoViewModel
}
protocol ReposInteractorInputsType: class {
var presenter: ReposInteractorOutputsType? { get set }
func fetchRepos(for query: String)
func fetchInitialRepos(a)
}
protocol ReposInteractorOutputsType: class {
func didRetrieveRepos(_ repos: [Repo])
}
protocol ReposRemoteDataManagerType: class {
func fetchRepos(for query: String, completion: @escaping ([Repo]) -> ())
}
Copy the code
5.5 summarize
VIPER is an architectural design within a single interface module, rather than the design of the entire APP architecture. It has little to do with the overall architecture of app, and there is no case of premature use of VIPER. So, strictly speaking, it’s the complex interface that works better for VIPER, not the larger app
Refer to the article
- The differences between MVC, MVP, and MVVM patterns
- MVC, MVP, MVVM evolution
- IOS MVP refactoring practices
- IOS VIPER architecture practices