API通信を行う際のエラー処理

検索キーを用いてgithubAPIを叩き、リポジトリのデータを取得するメソッドの中でエラー処理を書いたのでメモとして記録します。
searchBarに入力した検索ワードからリポジトリを検索してTableViewに表示し、検索を押してエラーが出た際に内容をUIAlertControllerで表示するシンプルなアプリです。
こんな感じ↓
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893593/f4990bff-8f01-8e62-f30e-5f299b826e0e.png"width=40%>


#環境
・macOS Big Sur バージョン11.2.3
・XCode 12.4
・Swift5.3.2
・Alamofire5.4.1 ＋ Codableを使用

#1. エラーを定義する
```APIError.swift
enum APIError: Error{
    case searchWordEmpty
    case invalidURL
    case networkError
    case unknown
    
    var title: String{
        switch self{
        case .networkError:
            return "ネットワークエラー"
        case .unknown:
            return "不明なエラー"
        default:
            return "検索エラー"
        }
    }
    
    var description: String{
        switch self{
        case .searchWordEmpty:
            return "検索ワードを入力してください。"
        case .invalidURL:
            return "別の検索ワードを試してください。検索ワードには半角英数字のみお使いいただけます。"
        case .networkError:
            return "接続環境の良いところでもう一度お試しください。"
        case .unknown:
            return "不明なエラーです。"
        }
    }

    func showAlert(from: UIViewController){
        UIAlertController.showAPIErrorAlert(error: self, from)
    }
}
```
ユーザーに分かりやすいように4種類のエラーに分けました。
ユーザー側で修正不可能なエラーは全て不明なエラーとして表示されます。
#2. UIAlertControllerの拡張機能を作る
```ExtensionUIAlertController.swift
extension UIAlertController{
    static func showAPIErrorAlert(error: APIError, _ viewController: UIViewController){
        let title = error.title
        let message = error.description
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let action = UIAlertAction(title: "OK", style: .default) { (action) in
            alert.dismiss(animated: true, completion: nil)
        }
        alert.addAction(action)
        viewController.present(alert, animated: true, completion: nil)
    }
}
```
titleに先程定義したエラーのタイトル、messageにエラーの詳細(description)を表示させ、OKを押したら閉じるアラートを作成し表示します。
拡張機能+staticでどこでも簡単に呼び出せるようにしました。
#3. API通信を行うメソッドでエラーを取得する
```GithubAPI.swift
func getRepositoryDataOf(_ searchWord:String, completion:@escaping(Result<(),APIError>)->Void){

        guard let url = URL(string: "https://api.github.com/search/repositories?q=\(searchWord)")
        else { return completion(.failure(.invalidURL)) } //①

        AF.request(url, method: .get, parameters: nil, encoding: JSONEncoding.default).responseJSON { (response) in
            do{
                guard let data = response.data
                else { throw APIError.networkError } //②
                let result = try JSONDecoder().decode(Repository.self, from: data)
                let tableViewDatasource = TableViewDataSource.shared
                tableViewDatasource.repositories = result
                DispatchQueue.main.async {
                    completion(.success(()))
                }
            }catch{
                if error as? APIError == APIError.networkError{
                    completion(.failure(.networkError)) //②
                }else{
                    completion(.failure(.unknown)) //③
                }
                print(error)
            }
        }
    }
```
Result型を用いてcompletionでエラーを渡します。
successの時のデータはシングルトンに格納しているため値渡しは行っていません。
①guard letを使ってエラーを返します
②guard letでthrowしたエラーをdo catch構文、completionを使い渡しています。
③上記2つ以外のAPI通信時に起こるエラーは全てunknownとして渡します
#4. ViewControllerでアラートを表示する
```ViewController.swift
func getRepositoryData(searchWord: String){

        GithubAPI.getRepositoryDataOf(searchWord){ [unowned self] (result) in
            switch result{
            case .success(()):
                tableView.reloadData()
            case .failure(let error): 
                error.showAlert(from: self)
            }
        }
    }

//searchBarDelegateメソッド
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {

        guard let searchWord = searchBar.text,
              !searchWord.isEmpty
        //検索ワードが空の時のエラー
        else { return UIAlertController.showAPIErrorAlert(error: .searchWordEmpty, self) } 
        getRepositoryData(searchWord: searchWord)
    }
```
API通信を行ってcompletionで受け取ったエラーごとにalertを作成し表示しています。
検索ワードが空の時はAPI通信を行う前にguard letを用いてエラーを表示しています。

#5. 終わりに
API通信時のエラーはこれで網羅できたのではないでしょうか。
修正したほうが良い点があればコメントください！