[Swift] APIを使用して郵便番号から住所を自動入力する(Storyboard版)

## はじめに
会員制のアプリケーションを開発する際、ユーザーに住所の入力を求めたい場合がありますよね。
ただ住所を都道府県から手入力しようとすると、文字数が非常に長いため、ユーザーにとっては手間となってしまいます。(ユーザーの離脱につながるリスクもある)
そこで多くのアプリケーションに導入されているのが、郵便番号からの住所自動入力です。
この技術をSwiftではどのように導入しているのか気になったため調査・実装してみました。
色々方法はあるようですが、今回はAPIを使用した方法で実装しています。

※本記事はStoryboardで実装しています。SwiftUI版は別途記事を作成します。

## 使用するAPI
以下のAPIを使用します。

https://zipaddress.net/

https://zipaddress.net/ こちらのURLの末尾に`?zipcode={郵便番号}`を追加すると、郵便番号に応じた住所を返してくれます。
例) https://api.zipaddress.net/?zipcode=1000001

## Storyboadに配置するパーツ
- Label ×1
- TextField ×1
- Button ×1
その他UI組みは各開発体制に準じて変更してください。
![スクリーンショット 2024-01-11 10.31.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656043/cc419801-4a44-cf06-504f-4874e5248e72.png)

## 実装
以下の内容を記述してください。

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet private weak var addressLabel: UILabel!
    @IBOutlet private weak var textField: UITextField!
    @IBOutlet private weak var fetchButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    // MARK: - IBActions
    @IBAction func fetchButtonTapped(_ sender: UIButton) {
        // 郵便番号が入力されているか確認
        guard let postalCode = textField.text, !postalCode.isEmpty else {
            // 郵便番号が入力されていない場合はエラーメッセージを表示
            addressLabel.text = "郵便番号を入力してください"
            return
        }

        // APIから住所を取得するメソッドを呼び出し
        getAddress(from: postalCode) { [weak self] address in
            DispatchQueue.main.async {
                // 取得した住所をLabelに表示
                self?.addressLabel.text = address
            }
        }
    }

    // MARK: - Private Methods
    private func getAddress(from postalCode: String, completion: @escaping (String) -> Void) {
        // APIのベースURLとリクエストURLを構築
        let baseURL = "https://api.zipaddress.net/"
        let urlString = "\(baseURL)?zipcode=\(postalCode)"
        print(urlString)

        // URLが正常に生成できるか確認
        guard let url = URL(string: urlString) else {
            // URL生成に失敗した場合はエラーメッセージを返す
            completion("エラーが発生しました")
            return
        }

        // URLSessionを使用してAPIからデータを取得
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            if let error = error {
                // ネットワークエラーが発生した場合はエラーメッセージを返す
                print("Error fetching address: \(error)")
                completion("エラーが発生しました")
                return
            }
            
            guard let data = data else {
                // 取得したデータがnilの場合はエラーメッセージを返す
                completion("住所が見つかりませんでした")
                return
            }
            
            do {
                // デコードして住所情報を取得
                let decoder = JSONDecoder()
                let addressResponse = try decoder.decode(AddressResponse.self, from: data)
                
                guard let address = addressResponse.data?.fullAddress else {
                    // 住所情報がnilの場合はエラーメッセージを返す
                    completion("住所が見つかりませんでした")
                    return
                }

                // 取得した住所をコールバックで返す
                completion(address)
            } catch {
                // デコードエラーが発生した場合はエラーメッセージを返す
                print("Error decoding address: \(error)")
                completion("エラーが発生しました")
            }
        }.resume()
    }
}


```

```swift: AddressResponse.swift
import UIKit

struct AddressResponse: Codable {
    let data: AddressData?
    
    struct AddressData: Codable {
        let fullAddress: String?
        
        enum CodingKeys: String, CodingKey {
            case fullAddress = "fullAddress"
        }
    }
}
```

## 実行結果
存在する郵便番号を入力すると、Labelに住所が反映されます。
存在しない郵便番号を入力すると、住所が存在しない旨を表示します。
入力内容に不備があると(スペースが入っている等)、エラーメッセージを表示します。
※ハイフンはあってもなくても同じ挙動になります。
![Simulator Screen Recording - iPhone 14 Pro - 2024-01-11 at 11.31.21.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656043/4de432e3-a8ae-5834-ecbc-f63f0b872f4a.gif)

## まとめ