1. Brief introduction of architecture design mode

  1. The current mainstream architecture modes are as follows:
  • MVC
  • MVP
  • MVVM
  • VIPER
  1. 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

  1. In terms of function, the above four architectural design patterns can be divided into three levels:
  • ModelLayer: responsible for data access, which can be divided into the following two categories
    • Business processing: daily developmentDAO,ServiceBoth 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.
  • ViewLayer: Responsible for the display of views
  • Controller/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
  1. 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:
  1. Difficult to unit test
  2. View and Model are seriously coupled
  3. 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 model
  • View: Responsible for view display
  • ViewModel: 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

  • MVVMCompared withMVPThat will bePresenterbecomeViewModel.ViewModelYou 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