【Swift】 Alamofire로 API 통신을 한다

소개



이번에는 QiitaAPI를 두드려 Alamofire에서 통신하고 싶습니다.

만드는 것





GitHub



구현



모델
struct Article: Codable {
    let title: String
    let user: User
}

struct User: Codable {
    let id: String
}

컨트롤러
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
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)))
                }
            }
    }

}

기타
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의 해설이라고 하는 것으로, 생략합니다.

여기에서 요청을 제출하고 있습니다.
AF.request(urlString)

다음과 같이 통신을 수행합니다.
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과의 비교


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는 분명히 간결합니다.

결론



끝입니다.

좋은 웹페이지 즐겨찾기