[Swift]API呼び出しMVPで書いてみた

## 概要

仕事でMVPを使用し開発する場が増えてきているので、
自分の練習としてMVPを使用しAPIを叩いています。
Alamofire・Codable等あまり慣れていないため、
下にある参考文献の記事を見て、自分なりに作成いたしました。
こう書いた方がより良いですよ等ありましたら、ご指摘いただけるとありがたいです。
またQiitaに投稿するのが初めてのため、読みにくかったら申しわけございません。

## 環境

- xcode10.0
- swift4.2
- ライブラリ
    - Alamofire 4.7.3
    - SVProgressHUD （見栄えをよくするため）
- API
    - Qiitaのやつ


## 実行結果

|APIListViewController  |APIInformationViewController  |
|---|---|
|<img src="https://qiita-image-store.s3.amazonaws.com/0/255707/7fa28422-f732-58d5-28e7-6aea93e02065.png" width=70%>  |<img src="https://qiita-image-store.s3.amazonaws.com/0/255707/30ece0bd-5269-02c0-1e0e-4fff7e28e33d.png" width=70%>  |

<!-- <img src="/Users/tatsuhikosakamoto/Desktop/APIList.png" width=50%><img src="/Users/tatsuhikosakamoto/Desktop/WebView.png" width=50%> -->

<!-- ![代替テキスト](/Users/tatsuhikosakamoto/Desktop/APIList.png  "画像タイトル")
![代替テキスト](/Users/tatsuhikosakamoto/Desktop/WebView.png  "画像タイトル") -->


## 作成ファイルの説明
- View 
    - APIListViewController ・・・　取得した情報をTableViewに表示
    - APIInformationViewController　・・・　取得したurlをもとに情報をWKWebViewに表示
- Presenter
    - APIListPresenter　・・・　ModelのAPI情報を取得処理を呼び出し、取得した情報をViewに通知する
- Model
    - APIOperater ・・・　実際にAPIを叩いて情報を取得している
    - APIInformationModel ・・・　取得した情報用の構造体

## View
##### APIListViewController
ここではPresenterにあるAPI取得処理を呼び、
Presenterから通知された情報を表示するのみ。

viewDidLoad()で
APIListPresenterのapiListViewプロパティにselfを設定する、
APIListPresenterのAPI情報を取得する処理を呼ぶ。


```APIListViewController.swift

final class APIListViewController: UIViewController {
    
    private let apiListPresenter = APIListPresenter()
    private var articleList = [APIInformationModel]()
    private var url = ""
    
    @IBOutlet private weak var apiListTableview: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        apiListPresenter.apiListView = self
        apiListPresenter.requestAPIInformations()
        
        // インジケータの設定
        SVProgressHUD.show(withStatus: "取得中...")
        SVProgressHUD.setDefaultMaskType(.black)
    }
}
```
拡張してAPIListViewを継承させている。
Presenterから通知が飛んできて、それぞれのプロトコルメソッドが実行される。

```APIListViewController.swift
extension APIListViewController: APIListView {

    /// Presenterから通知されてきた情報をTableviewで使用する配列に格納
    ///
    /// - Parameter article: api取得情報
    func responseAPIInformations(article: [APIInformationModel]) {
        // インジケータを止める
        SVProgressHUD.dismiss()

        articleList = article
        // tableviewをリロードし、情報をtableviewに表示する
        apiListTableview.reloadData()
    }
    
    /// 情報の取得に失敗したとき
    func errorResponseAPI() {
        // インジケータを止める
        SVProgressHUD.dismiss()
        print("エラー")
    }
}
```
Tableview表示の処理

```APIListViewController.swift
extension APIListViewController: UITableViewDelegate, UITableViewDataSource {
    
    /// セルの数を指定
    ///
    /// - Parameters:
    ///   - tableView: TableView
    ///   - section: 対象セクション番号
    /// - Returns: セル数
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return articleList.count
    }
    
    /// セルの内容を指定
    ///
    /// - Parameters:
    ///   - tableView: TableView
    ///   - indexPath: 対象セクション及びセル番号情報
    /// - Returns: セル
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = articleList[indexPath.row].title

        return cell
    }
    
    /// セル選択時
    ///
    /// - Parameters:
    ///   - tableView: TableView
    ///   - indexPath: 対象セクション及びセル番号情報
    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        url = articleList[indexPath.row].url
        self.performSegue(withIdentifier: "APIInformationViewControllerPush", sender: nil)
        
        tableView.deselectRow(at: indexPath, animated: true)
    }
}

```


##### APIInformationViewController
ここではAPIListViewControllerから遷移した際、値渡しで取得した
urlを渡し、それを元にWKWebViewに表示しています。
コードは特に変わったことはしていないので省きます。。。


## Persenter
##### APIListPresenter
ModelのAPI取得処理を呼び出し、戻ってきた情報をViewcontrollerに通知する。

```APIListPresenter.swift
// 委譲する処理をプロトコルメソッドとして宣言する。
protocol APIListView: NSObjectProtocol {
    func responseAPIInformations(article: [APIInformationModel])
    func errorResponseAPI()
}
```

```APIListPresenter.swift
final class APIListPresenter {
    
    weak var apiListView: APIListView?
    
    /// APIOperater.getAPIInformationsを呼び出し、情報を取得する
    func requestAPIInformations() {
        
        APIOperater.getAPIInformations(callback: { [weak self] (articles) in
            guard let strongSelf = self else { return }

            // 情報が取得できているか判定
            if let articleValue = articles {
                // 取得成功の場合は、成功パターンのプロトコルメソッドの引数に取得した情報を渡し呼び出し
                strongSelf.apiListView?.responseAPIInformations(article: articleValue)
            } else {
                // 取得失敗の場合、失敗パターンのプロトコルメソッドを呼び出し
                strongSelf.apiListView?.errorResponseAPI()
            }
        })
    }
}
```

## Model

##### APIOperater 
API取得の処理を行う。
ここでは取得に成功した際、情報をcallbackの引数に設定している。

```APIOperater.swift
struct APIOperater {
    
    /// APIを呼び出し
    ///
    /// - Parameter callback: callbackメソッドの引数に、取得した情報を構造体で渡す
    static func getAPIInformations(callback: @escaping ([APIInformationModel]?) -> Void) {
            
        Alamofire.request("https://qiita.com/api/v2/items").validate().responseJSON { response in
            guard let data = response.data else {
                return
            }
            do {
                // JSONのマッピング
                let articles = try JSONDecoder().decode([APIInformationModel].self, from: data)
                callback(articles)
  
            } catch {
                callback(nil)
            }
        }
    }
}
```
##### APIInformationModel
JSONのマッピングで使用するためCodableを継承させている。
色々定義できるが、今回は使うものだけ定義している。

```APIInformationModel.swift
struct APIInformationModel: Codable {
    var title: String
    var user: User
    var url: String
    struct User: Codable {
        var id: String
    }
}
```


## 最後に。。。
今回は本当に簡単な感じの実装になりましたが、
おそらくもっとMVPの利点が見られる場面や、
もっと良い書き方などがあると思います。
それらに出会うためにも、
これからも勉強していきます。。。。。
またメソッド名・ファイル名・変数名も、
よりわかりやすい名前がかけるよう努力していきます！！


## 参考文献
Codableのところを参考にさせていただきました。
http://www.cl9.info/entry/2017/10/06/134510
https://hfoasi8fje3.hatenablog.com/entry/2018/07/05/185348