【SwiftUI / 通信入門】通信を使った基本的なミニアプリ作ってみる(QiitaAPIで)

【SwiftUI / 通信入門】通信を行う基本的なミニアプリ作ってみる(QiitaAPIで)

実行環境

Swift5.6.1
Xcode14.0
macOS12.6

作るもの

QiitaAPIから最新記事のタイトル20件を取得し、List表示させるだけのミニアプリを作ります。
実際に手元で確認したい場合は、こちらのGitHubからダウンロード下さい。
通信はURLSessionを利用します。

実装

通信エラーの内容を定義

通信は必ずしも上手くいくとは限りません。
通信がうまくいかなかった場合の通信のエラーを、enum型で定義しおきます。
今回は、新しく作ったAPIError.swift ファイルに下記のコードを記述しました。

enum APIError: Error {
    case invalidURL
    case networkError
    case noneValue
    case unknown

    var title: String {
        switch self {
        case .noneValue:
            return "値が空で取得されたエラー"
        case .invalidURL:
            return "無効なURLのエラー"
        case .networkError:
            return "ネットワークエラー"
        default:
            return "不明なエラー"
        }
    }
}

通信のModelを実装

通信のプロトコルを作成しました。
コールバック関数の中では、後で作成するArticleと先程作成したAPIErrorを記述しています。
(今回プロトコルを作成した理由は、本記事では書いていないテストコードやstubを楽に実装できるように作成しました。)

protocol ArticleListAPIClientProtocol {
    func fetch(completion: @escaping ((Result<[Article], APIError>) -> Void))
}

上で作成したプロトコルに準拠した、ArticleListAPIClientのModelを作成しました。
今回は、URLSessionで通信を行います。

import Foundation

class ArticleListAPIClient: ArticleListAPIClientProtocol {
    func fetch(completion: @escaping ((Result<[Article], APIError>) -> Void)) {

        guard let url = URL(string: "https://qiita.com/api/v2/items") else {
            return  completion(.failure(.invalidURL))
        }

        let request = URLRequest(url: url)
        URLSession.shared.dataTask(with: request) { (data, response, error) in

            do {
                guard let data = data else { throw APIError.networkError }
                guard let articleList = try? JSONDecoder().decode([Article].self, from: data) else {
                    throw APIError.noneValue
                }
                DispatchQueue.main.async {
                    completion(.success(articleList))
                }
            } catch {
                if error as? APIError == APIError.networkError {
                    completion(.failure(.networkError))
                } else if error as? APIError == APIError.noneValue {
                    completion(.failure(.noneValue))
                } else {
                    completion(.failure(.unknown))
                }
            }
        }.resume()
    }
}

通信のModelを呼び出す

import SwiftUI

// Viewの値を宣言的に変更させるために、ObservableObjectに準拠させておく
final class ArticleViewModel: ObservableObject {
    // @PublishedでViewで表示される値を宣言的に変更できる
    @Published var articles: [Article] = [Article]()
    private let articleListAPIClient = ArticleListAPIClient()

    init() {
        loadArticles()
    }

    private func loadArticles() {
        articleListAPIClient.fetch(completion: { result in
            switch result {
            case .success(let articleList):
                self.articles = articleList

            case .failure(let error):
                print(error.title)
            }
        })
    }
}

Viewの実装

import SwiftUI

// JSONから変換するためにCodableに準拠させておく
struct Article: Codable {
    let title: String
}

struct ArticleView: View {
    @ObservedObject private var viewModel = ArticleViewModel()

    var body: some View {
        VStack {

            List {
                ForEach(0..<viewModel.articles.count, id: \.self) { index in
                    Text(viewModel.articles[index].title)
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ArticleView()
    }
}

まとめ

ということで、本記事はURLSessionで通信を行う基本的なミニアプリを作ってみました。
アドバイスや改善などあれば本記事の最後のコメント欄からお願いします。
最後まで読んでいただきありがとうございました!

参考

>> Apple公式ドキュメント(URLSession)
>> API通信を行う際のエラー処理
>> さわって学べる!iOSテスト駆動開発

作業効率がグッと上がるPC道具

間違いなしのSwift書籍2冊



コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です