I have been writing iOS programs for almost two years, during which I have written 3 iOS projects intermittently. I used MVP architecture for Android, but I never found a good MVP practice for iOS, so all the code in the previous project was stacked in the ViewController. The use of more comments and #MARK makes the code less cluttered, but viewControllers with hundreds or even thousands of lines can be frustrating.

It wasn’t until yesterday when I started my next iOS project that I googled “iOS MVP” again and finally found a few articles that led me to an easy-to-understand MVP practice. :

iOS-mvp-sample

Don’t Put View Code Into Your View Controller

Take the landing page for example. To implement a simple landing page as follows:

Step one, View

In the previous project, the UI code for the page was written in the ViewController. In this practice, the UI code is pulled out and written in loginView.swift (just input fields and buttons) :

import UIKit

protocol LoginViewDelegate: NSObjectProtocol {
    func loginWith(username: String, password: String)
}

class LoginView: UIView {
    var delegate: LoginViewDelegate?

    let usernameTf = UITextField()
    let passwordTf = UITextField()
    letloginBtn = UIButton() override init(frame: CGRect) { super.init(frame: frame) initView() } required init? (coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented")
    }

    convenience init() {
        self.init(frame: CGRect.zero)
        initView()
    }

    func initView() {self.backgroundcolor = uicolor.white // User name input box self.addSubView (usernameTf) UserNamETF.borderStyle =.roundedRect usernameTf.snp.makeConstraints { (make)inmake.centerX.equalToSuperview() make.top.equalToSuperview().offset(270) make.left.equalToSuperview().offset(40) Make. Right. EqualToSuperview (). Offset (40)} / / password input box self addSubview (passwordTf) passwordTf. BorderStyle. = roundedRect passwordTf.snp.makeConstraints { (make)inmake.centerX.equalToSuperview() make.top.equalTo(usernameTf.snp.bottom).offset(16) Make. Left. EqualToSuperview (). Offset (40) make. Right. EqualToSuperview (). The offset (40)} / Views/login button. The resetButton (loginBtn, title:"Login", color: UIColor.green, font: UIFont.systemFont(ofSize: 22))
        self.addSubview(loginBtn)
        loginBtn.addTarget(self, action: #selector(clickLoginButton), for: .touchUpInside)
        loginBtn.snp.makeConstraints { (make) in
            make.centerX.equalToSuperview()
            make.top.equalTo(passwordTf.snp.bottom).offset(20)
        }
    }

    @objc func clickLoginButton() { delegate? .loginWith(username: usernameTf.text ??"", password: passwordTf.text ?? "")}}Copy the code

The LoginViewDelegate protocol is implemented in the ViewController to capture the login button click event.

Step two, Presenter

Presenter has the following characteristics:

  • Logic for handling user interactions
  • Communicate with the Model layer, convert the data to a UI-friendly format, and update the view
  • Don’t rely on UIKit

In this example, presenter has only one method to perform the login request:

import Foundation

struct LoginSelfData {
    let username: String
    letpassword: String } protocol LoginSelf: NSObjectProtocol { func startLoading() func finishLoading() func loginSucceed() func loginFail(mes: String) func noNetwork() } class LoginPresenter { var loginSelf: LoginSelf? /// /// -parameters: // -username: username /// -password: password func loginWith(username: String, password: String) {let parameter = ["username": username, "password": password] ... // Execute the loginSelf request? .startLoading() ... // Return data is received loginSelf? .finishLoading() ... // loginSelf? .loginSucceed() ... // Failed loginSelf? .loginFail(mes:"msg")}}Copy the code

So as long as you implement the LoginSelf protocol in the ViewController, you can capture the callback method after the login request completes, and then you can update the view according to the different callback method.

Step three, ViewController

The code in the ViewController is as simple as initializing the View and Presenter separately and implementing their respective protocols.

import UIKit

class LoginViewController: UIViewController {
    let loginPresenter = LoginPresenter()
    var loginView: LoginView?

    override func viewDidLoad() { super.viewDidLoad() loginPresenter.loginSelf = self loginView = LoginView.init(frame: self.view.bounds) loginView? .delegate = self self.view = loginView} // MARK: - Update view extension LoginViewController: LoginSelf {funcstartLoading() {

    }

    func finishLoading() {

    }

    func loginSucceed() {

    }

    func loginFail(mes: String) {

    }

    func noNetwork() {}} extension LoginViewController: LoginViewDelegate {/// login /// /// - Parameters: /// -password: password func loginWith(username: String, password: String) { loginPresenter.loginWith(username: username, password: password) } }Copy the code

summary

Such a relatively simple iOS MVP practice is completed, after adopting this writing method, the code suddenly feels very clear, maybe the total amount of code will be a little more, but the single file will certainly be smaller than the previous fat ViewController, which is not only convenient for communication between colleagues, It is also convenient to find code and modify bugs.