【Swift】AlamofireでAPI通信をする

#はじめに
今回はQiitaAPIを叩いてAlamofireで通信していみたいと思います。

#作るもの

![ScreenShot 2021-05-11 3.31.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954451/4ecaffea-f77c-7d98-3d53-14f7bc062a76.png)

#GitHub

https://github.com/0429oonishi/Alamofire-practice

#実装

モデル

```swift
struct Article: Codable {
    let title: String
    let user: User
}

struct User: Codable {
    let id: String
}
```

コントローラー

```swift
final class ArticleListViewController: UIViewController {
    
    @IBOutlet private weak var tableView: UITableView!
    private var articles = [Article]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.dataSource = self
        getArticles()
        
    }
    
}

private extension ArticleListViewController {
    
    func getArticles() {
        APIClient().request { result in
            switch result {
                case .success(let articles):
                    self.articles = articles
                    DispatchQueue.main.async {
                        self.tableView.reloadData()
                    }
                case .failure(let error):
                    self.showAPIAlert(error: error)
            }
        }
    }
    
    func showAPIAlert(error: APIError) {
        let alert = UIAlertController(title: error.title, message: nil, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "閉じる", style: .cancel, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
    
}

// MARK: - UITableViewDataSource
extension ArticleListViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return articles.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = articles[indexPath.row].title
        return cell
    }
    
}
```

API

```swift
import Alamofire

typealias ResultHandler<T> = (Result<T, APIError>) -> Void

struct APIClient {
    
    func request(handler: @escaping ResultHandler<[Article]>) {
        let urlString = "https://qiita.com/api/v2/items"
        let url = URL(string: urlString)
        guard let url = URL(string: urlString) else {
            handler(.failure(.invalidURL))
            return
        }
        AF.request(urlString)
            .responseJSON { response in
                guard let data = response.data else {
                    handler(.failure(.invalidResponse))
                    return
                }
                do {
                    let articles = try JSONDecoder().decode([Article].self, from: data)
                    handler(.success(articles))
                } catch {
                    handler(.failure(.unknown(error)))
                }
            }
    }
    
}
```

その他

```swift
enum APIError: Error {
    case invalidURL
    case invalidResponse
    case unknown(Error)
}

extension APIError {
    
    var title: String {
        switch self {
            case .invalidResponse: return "無効なレスポンスです。"
            case .invalidURL: return "無効なURLです。"
            case .unknown(let error): return "予期せぬエラーが発生しました。\(error)"
        }
    }
    
}
```

#解説
大切なところのみ解説します。
本来はurlをそのまま書いておいたりするのは良くないのですが、今回はAamofireの解説ということで、省略します。

ここでリクエストを送信しています。

```swift
AF.request(urlString)
```

以下のようにして、通信を行います。


```swift
APIClient().request { result in
    switch result {
        case .success(let articles):
            self.articles = articles
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        case .failure(let error):
            self.showAPIAlert(error: error)
    }
}
```


#URLSessionとの比較

```swift
let request = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
    if let error = error {
        handler(.failure(.unknown(error)))
        return
    }
    if let data = data {
        do {
            let articles = try JSONDecoder().decode([Article].self, from: data)
            handler(.success(articles))
        } catch {
            handler(.failure(.unknown(error)))
        }
    }
}
task.resume()
```

Alamofireの方が明らかに簡潔にかけていますね。

























#おわりに
終わりです。