［iOS/Swift］Alamofire を利用したアプリ実装（郵便番号検索アプリを例に）

### 書くこと
Alamofire を利用した API 呼び出しを利用してアプリを実装する

### 書いた理由
Alamofire を利用した API 呼び出し方法を忘れた時に確認する

### 参考にした記事
[標準とAlamofireでAPIコール処理を書き比べてみる(Swift)](https://qiita.com/uhooi/items/c0e083f916dc516175bd)

上記記事に紹介されていたコードに、UIを実装した
（すごくわかりやすい記事です）

### 前提条件
1. テキストフィールド、ボタンおよび検索結果を表示するラベルは `Main.storyboard` で設定
2. 上記要素は、`ViewController.swift` に紐づけている
3. `CocoaPods` を利用して `Alamofire` はすでに導入済である

### アプリの機能
テキストフィールドに郵便番号を入力し［Seach］ボタンをタップすると、ラベル（水色）にその郵便番号に対応する住所を表示する。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199441/661fef6a-6ffd-a466-8d19-61f7fe53f161.png" width=300>

・郵便番号が入力されていない場合や入力した文字が7桁ではない場合はアラートを表示する

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199441/16f522b4-8ddc-2625-4f62-3791abc50ca2.png" width=300>



### コード例
```swift:AddressModel.swift
// このコードは参考にした記事より流用した
struct AddressModel: Decodable {
    var results: [Result]

    struct Result: Decodable {
        var address1: String
        var address2: String
        var address3: String
        var kana1: String
        var kana2: String
        var kana3: String
    }
}
```

```swift:ViewController.swift
import UIKit
import Alamofire

class ViewController: UIViewController {
    
    private var addresses: AddressModel?

    @IBOutlet weak var zipCodeTextField: UITextField!
    @IBOutlet weak var seachButton: UIButton!
    @IBOutlet weak var resultLabel: UILabel!
    
    @IBAction func tapSeachButton(_ sender: Any) {
        self.getAddress(zipCode: zipCodeTextField.text)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    
    private func getAddress(zipCode: String?) {
        self.validate(zipCode: zipCode)
        
        let baseUrl = "https://zipcloud.ibsnet.co.jp/api/"
        let searchUrl = "\(baseUrl)search"
        let parameters: [String: Any] = ["zipcode": zipCode!]
        let headers: HTTPHeaders = ["Content-Type": "application/json"]
        
        self.requestAPI(url: searchUrl, parameters: parameters, headers: headers)
    }
    
    private enum ErrorType:String {
        case zipCodeNil = "郵便番号が確認できません"
        case zipCodeBrank = "郵便番号を入力してください"
        case zipCodeLengthNot7 = "郵便番号は7桁で入力してください"
    }
    
    private func validate(zipCode: String?){
        guard let zipCode = zipCode else { self.showAlert(error: .zipCodeNil); return }
        if zipCode == "" { self.showAlert(error: .zipCodeBrank); return }
        if zipCode.lengthOfBytes(using: .utf8) != 7 { self.showAlert(error: .zipCodeLengthNot7); return }
    }
    
    private func showAlert(error: ErrorType) {
        let alert = UIAlertController(title: "検索に失敗しました", message: error.rawValue, preferredStyle: .alert)
        let action = UIAlertAction(title: "OK", style: .default, handler: nil)
        alert.addAction(action)
        self.present(alert, animated: true, completion: nil)
    }
    
    private func requestAPI(url:String,parameters:[String:Any],headers:HTTPHeaders) {
        AF.request(url, method: .get, parameters: parameters, encoding: URLEncoding(destination: .queryString), headers: headers).responseJSON { response in
            guard let data = response.data else { return }
            do {
                try self.parse(data: data)
            } catch let error {
                print("Error: \(error)")
                self.showError()
            }
        }
    }
    
    private func parse(data:Data) throws {
        self.addresses = try JSONDecoder().decode(AddressModel.self, from: data)
        if let address = self.addresses {
            self.show(result: "\(address.results[0].address1) \(address.results[0].address2) \(address.results[0].address3)")
        }
    }
    
    private func show(result:String){
        resultLabel.text = result
    }
    
    private func showError(){
        resultLabel.text = "検索結果が取得できませんでした"
    }
}

```

### 参考資料
[Alamofire](https://github.com/Alamofire/Alamofire)
[標準とAlamofireでAPIコール処理を書き比べてみる(Swift)](https://qiita.com/uhooi/items/c0e083f916dc516175bd)