【アーキテクチャ】MVP【Swift】

# MVPとは？
Model, View, Presenterのこと。

# Presenter
ModelとViewを繋ぐ橋渡し役

```
import Foundation
import UIKit

protocol UserPresenterDelegate: AnyObject {
    func presentUsers(users: [User])
    func presentAlert(title: String, message: String)
}

typealias PresenterDelegate = UserPresenterDelegate & UIViewController

class UserPresenter {
    
    weak var delegate: PresenterDelegate?
    
    public func getUsers() {
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else {
            return
        }
        
        let task = URLSession.shared.dataTask(with: url) { [weak self] data,_,error in
            guard let data = data, error == nil else {
                return
            }
            
            do {
                let users = try JSONDecoder().decode([User].self, from: data)
                self?.delegate?.presentUsers(users: users)
            }
            catch {
                print(error)
            }
        }
        task.resume()
    }
    
    public func setViewDelegate(delegate: PresenterDelegate) {
        self.delegate = delegate
    }
    
    public func didTap(user: User) {
        delegate?.presentAlert(
            title: user.name,
            message: "\(user.name) has an email of \(user.name) & a username of \(user.username)")
    }
    
}

```

# Model
アプリのデータ本体、加工、取得、保存などを記載
Presenterから移譲された処理を実行
処理完了後にpresenterに通知

```
import Foundation

struct User: Codable {
    let name: String
    let email: String
    let username: String
}
```

# View
UIView / UIViewController
Modelとは直接やりとりしない
Presenterにイベントを移譲
Presenterから貰った処理結果をそのまま表示する

```
import UIKit

class UserViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UserPresenterDelegate {
    
    private let tableView: UITableView = {
        let table = UITableView()
        table.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        return table
    }()
    
    private var users = [User]()
    
    private let presenter = UserPresenter()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        title = "Users"
        
        // table
        view.addSubview(tableView)
        tableView.delegate = self
        tableView.dataSource = self
        
        // presenter
        presenter.setViewDelegate(delegate: self)
        presenter.getUsers()
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        tableView.frame = view.bounds
    }
    
    // tableview delegate and datasource
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return users.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = users[indexPath.row].name
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // ask presenter to handle the tap
        presenter.didTap(user: users[indexPath.row])
    }
    
    // presenter delegate
    func presentUsers(users: [User]) {
        self.users = users
        
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
    
    func presentAlert(title: String, message: String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel, handler: nil))
        present(alert, animated: true)
    }
}
```

# MVPのメリット
・View と Model の間の依存性がない
・MVVMに比べるとシンプル

# MVPのデメリット
・ViewとPresenterの間の依存性が高いという短所
・コード量が増える

# 参考
https://www.youtube.com/watch?v=SFqIP5jYn_4
https://qiita.com/hicka04/items/25be38a90fdde29c97c2
https://laptrinhx.com/iosapuriakitekucha-bi-jiao-jian-tao-cocoa-mvc-mvvm-mvp-cleanarchtecture-3832902000/