VIPER学ぼう　

#目次
[1.はじめに](#1)
[2.VIPERとは](#2)
[3.VIPERのそれぞれの役割](#3)
[4.View](#4)
[5.Interactor](#5)
[6.Presenter](#6)
[7.Entity](#7)
[8.Router](#8)
[9.最後に](#9)
[参考](#10)

<a id="1"></a>
#1.はじめに
iosアプリ開発を勉強していくうちに必ずぶち当たるアーキテクチャの一つである`VIPER`について記事を書きたいと思います。
今回は地図アプリを作成します。
僕は`VIPER`のサンプルをコーディングをしていくうちに感動しました。

<a id="2"></a>
#2.VIPERとは
VIPERとは`View`・`Interactor`・`Presenter`・`Entity`・`Router`のそれぞれの頭文字をとったものです。
また基本としてDI（Dependency Injection）で外部から注入し、単一原則のもとに分割します。
MVC、MVVMなどのアーキテクチャはViewが画面遷移も担当する必要があります。

<a id="3"></a>
#3.VIPERのそれぞれの役割
||役割
|-|-|
|View|UIの更新やPresenterにViewイベントの通知|
|Interactor|ビジネスロジックを担当|
|Presenter|ハブの役割※|
|Entity|データ構造の定義（Model）|
|Router|画面遷移|

※`View`に`UISearchBar`があったとしてエンターを押したときに`Presenter`へ通知し、`Presenter`は`Interactor`にデータの取得を依頼するなど

<a id="4"></a>
#4.View
早速Viewのコードを見ていきます。

```swift
import UIKit
import MapKit


①
protocol MapView: AnyObject {
    func updatePin(coordinate: CLLocationCoordinate2D)
    func showErrorAlert()
}

final class MapViewController: UIViewController, Storyboardable {
    
    ②
    var presenter: MapPresentation!
    
    private let pin = MKPointAnnotation()
    
    @IBOutlet private weak var mapView: MKMapView!
    
    private var searchBar: UISearchBar = {
        let searchBar = UISearchBar()
        searchBar.placeholder = "住所"
        searchBar.autocorrectionType = .no
        searchBar.autocapitalizationType = .none
        return searchBar
    }()
    override func viewDidLoad() {
        
        navigationItem.titleView = searchBar
        searchBar.delegate = self
        
    }
}

extension MapViewController: MapView {
    func updatePin(coordinate: CLLocationCoordinate2D) {
        guard let adress = searchBar.text else { return }
        updateScreenCenter(coordinate: coordinate)
        pin.title = adress
        pin.coordinate = coordinate
        mapView.addAnnotation(pin)
    }
    
    func showErrorAlert() {
        let alertController = UIAlertController(title: "No Result", message: "該当する検索結果がありませんでした", preferredStyle: .alert)
        let ok = UIAlertAction(title: "OK", style: .default)
        alertController.addAction(ok)
        present(alertController, animated: true)
    }
}

extension MapViewController: UISearchBarDelegate {
    ③
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        guard let searchText = searchBar.text else { return }
        presenter.searchButtonPushed(adress: searchText)
    }
}

private extension MapViewController {
    func updateScreenCenter(coordinate: CLLocationCoordinate2D) {
        let span = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        mapView.region = region
    }
}
```

①はPresenterから呼び出す関数をprotocolで定義？します。

|関数名|機能|
|-----|----|
|updatePin(coordinate: )|検索した住所の位置にピンをさす|
|func showErrorAlert()|エラーが発生したらアラートを出す|

②はpresenterにイベントを通知するために定義します。
③で検索したい住所をpresenterに渡しています。

<a id="5"></a>
#5.Interactor

```swift
import CoreLocation

protocol MapUsecase: AnyObject {
    func searchAdress(adress: String, completion: @escaping (Result<CLLocationCoordinate2D, Error>) -> Void)
}

final class MapInteractor {
    
}

extension MapInteractor: MapUsecase {
    
    func searchAdress(adress: String, completion: @escaping (Result<CLLocationCoordinate2D, Error>) -> Void) {
        Geocoder.fetchAdress(adress: adress) { result in
            switch result {
            case .success(let coordinate):
                completion(.success(coordinate))
                break
            case .failure(let error):
                completion(.failure(error))
                break
            }
        }
    }
    
}

```

Interactorはビジネスロジックを担当します。
特に説明はいらないと思いますが、APIを叩くなどです。

<a id="6"></a>
#6.Presenter

```swift
import Foundation

protocol MapPresentation: AnyObject {
    func searchButtonPushed(adress: String)
}

final class MapPresenter {
    private weak var view: MapView?
    private let router: MapWriteframe
    private let interactor: MapUsecase
    
    ①
    init(view: MapView, router: MapWriteframe, interactor: MapUsecase) {
        self.view = view
        self.router = router
        self.interactor = interactor
    }
}

extension MapPresenter: MapPresentation {
    ②
    func searchButtonPushed(adress: String) {
        //interactorに住所の検索を依頼
        interactor.searchAdress(adress: adress) {[weak self] result in
            switch result {
            case .success(let coordinate):
                self?.view?.updatePin(coordinate: coordinate)
                break
            case .failure:
                self?.view?.showErrorAlert()
                break
            }
        }
    }
}

```

①DIの部分です。Presenterはハブの役割なのでそれぞれのインスタンスが必要になります。
②viewで`searchBarSearchButtonClicked(_ searchBar: )`が呼び出されたときに`searchBar.text`を渡されて`interactor`に検索を依頼します。

<a id="7"></a>
#7.Entity
今回のアプリは自分で定義するEntityはありませんが（CLLocationCoordinate2D）、githubのAPIなどを叩くときに`RepositoryEntity`などを定義しましょう。

<a id="8"></a>
#8.Router

```swift
protocol MapWriteframe: AnyObject {
    
}

final class MapRouter {
    
    ①
    private weak var viewControlelr: UIViewController?
    
    private init(viewController: UIViewController) {
        self.viewControlelr = viewController
    }
    
    ②
    static func assembleModules() -> UIViewController {
        let view = MapViewController.instantiate()
        let router = MapRouter(viewController: view)
        let interactor = MapInteractor()
        let presenter = MapPresenter(view: view, router: router, interactor: interactor)
        
        view.presenter = presenter
        
        return view
    }
    
}

extension MapRouter: MapWriteframe {
    
}

```

①viewController使ってないのになんで宣言してるのと思われるかもしれないですが、画面遷移する場合に必要になります。（今回は必要ない）
②viewControllerの表示用の関数。

<a id="9"></a>
#9.最後に
一つの画面ごとにView、Interactor、Presenter、Entity、Routerが必要でめんどくささもありますが、テストのしやすさ、可読性、修正しやすさなど多くのメリットがあります。
僕が一番感動したのはコードの綺麗さです。
自分ができる人みたいな感覚になれておすすめです。

今回なんとなく記事にしてみましたがまだまだ理解が足りていないと感じたので精進します。
アドバイスなどありましたらよろしくお願い致します。

<a id="10"></a>
#参考
[VIPERアーキテクチャ まとめ](https://qiita.com/hicka04/items/09534b5daffec33b2bec)