はじめに
現在、MVPアーキテクチャを学習中です。
さっと動かせるものを作りたいと思い、TableViewを使用したToDo形式のサンプルを作成しました。
個人の備忘録としても残しておきたいと思います。
MVPアーキテクチャとは?
MVPに関しては過去に書いた記事に詳しく書いたので、そちらを参考にしていただければと思います。
https://qiita.com/taro-ken/items/e2432d2028134e9a7307
実践
下記のようなサンプルを作成します!
はい、超シンプルなUIですが…笑
Cellに追加されて、タップするとその文字をアラートとして出します。
ではコードを見ていきます。
ソースコード
まずはPresenterです。
PresenterがViewControllerからの入力を受け取り
その結果をViewControllerに出力しています。
import Foundation
protocol ToDoPresenterInput {
var numberOfItems: Int { get }
func item(index: Int) -> ToDoModel
func add(text:String?)
func didSelect(index: Int)
func didEditingDelete(index: IndexPath)
}
protocol ToDoPresenterOutput:AnyObject {
func update()
func alert(text:String?)
}
final class ToDoPresenter {
private var output:ToDoPresenterOutput!
private var todoModel:[ToDoModel]
init(output:ToDoPresenterOutput){
self.output = output
self.todoModel = []
}
}
extension ToDoPresenter:ToDoPresenterInput{
func didSelect(index: Int) {
output.alert(text: todoModel[index].title)
}
func didEditingDelete(index: IndexPath) {
todoModel.remove(at: index.item)
output.update()
}
func add(text:String?){
guard let text = text, !text.isEmpty else { return }
let todoModel = ToDoModel.init(title: text)
self.todoModel.append(todoModel)
output.update()
}
func item(index: Int) -> ToDoModel {
todoModel[index]
}
var numberOfItems: Int {
todoModel.count
}
}
次はViewControllerです。
inputプロパティとinjectメソッドを用意して
ここで外部からPresenterを繋げてます。
import UIKit
final class ToDoViewController: UIViewController {
@IBOutlet private weak var todoText: UITextField!
@IBOutlet private weak var tableView: UITableView! {
didSet {
tableView.register(UINib.init(nibName: ToDoTableViewCell.className, bundle: nil), forCellReuseIdentifier: ToDoTableViewCell.className)
tableView.delegate = self
tableView.dataSource = self
}
}
private var presenter: ToDoPresenterInput!
func inject(presenter: ToDoPresenterInput) {
self.presenter = presenter
}
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction private func tapAdd(_ sender: Any) {
presenter.add(text: todoText.text)
todoText.text = ""
}
}
private extension ToDoViewController {
## //アラートを出す処理
func showAlert(text: String?) {
let alertVC = UIAlertController(title: "タイトル", message: text, preferredStyle: .alert)
alertVC.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alertVC, animated: true, completion: nil)
}
}
extension ToDoViewController:ToDoPresenterOutput{
func alert(text: String?) {
self.showAlert(text: text)
}
func update() {
tableView.reloadData()
}
}
extension ToDoViewController:UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: ToDoTableViewCell.className) as? ToDoTableViewCell else {
fatalError()
}
let todoModel = presenter.item(index: indexPath.item)
cell.configure(todoModel: todoModel)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
presenter.numberOfItems
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
presenter.didSelect(index: indexPath.row)
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
presenter.didEditingDelete(index: indexPath)
}
}
}
画面の立ち上げはRouterクラスにて行っています。
その際、PresenterとViewControllerを繋げています。
import UIKit
final class Router {
static let shared = Router()
private init() {}
private var window: UIWindow?
func showRoot(window: UIWindow) {
guard let vc = UIStoryboard.init(name: "ToDo", bundle: nil).instantiateInitialViewController() as? ToDoViewController else {
return
}
## //presenterとvc同士を繋ぎ合う
let presenter = ToDoPresenter(output: vc)
vc.inject(presenter: presenter)
let nav = UINavigationController(rootViewController: vc)
window.rootViewController = nav
window.makeKeyAndVisible()
self.window = window
}
private func show(from: UIViewController, to: UIViewController, completion:(() -> Void)? = nil){
if let nav = from.navigationController {
nav.pushViewController(to, animated: true)
completion?()
} else {
from.present(to, animated: true, completion: completion)
}
}
}
このような感じで、シンプルではありますがMVPで実装できたかと思います。
全体のソースコードは下記にまとめています!
https://github.com/taro-ken/ToDo-MVP
間違っている箇所等あれば、ご指摘お願いいたします。
ご覧いただきありがとうございました♪