1
1

More than 1 year has passed since last update.

iOS強化月間 - iOSアプリ開発の知見を共有しよう -

printしないエラーハンドリング

Last updated at Posted at 2023-09-17

記事を書くモチベーション

  • <実現したかったこと>エラーが起きた時にユーザーにエラーの内容に応じたアラートを表示する。
  • エラーハンドリングについて調べると、printでエラーを出力して処理したり、カスタムエラーを投げている記事がたくさんありました。
  • しかし、私は個人開発で、なるべく標準ライブラリのエラーを利用したかった。
  • ようやく期待する動作をする書き方を見つけたのでそれを書いてみます。

サンプルコードの概要

  • QiitaAPIを叩いて、返ってきたレスポンスをデコードして画面に作成日とユーザー名を表示します。
  • このサンプルでエラーが発生するのは、APIにセッションしている時とデコードするときです。
  • 期待する動作は、エラー発生時に、内容に応じたアラートをユーザーに表示することです。

結論

説明の前に先にコードの全体を示します。以下のコードをそのままプロジェクトに貼りつけると動くと思います。。。

ContentView.swift
import SwiftUI

struct Item: Decodable {
    let id: String
    let createdAt: String
    let user: User

    enum CodingKeys: String, CodingKey {
        case id
        case createdAt = "created_at"
        case user
    }
}

struct User: Decodable {
    let name: String
    let profileImageURL: String

    enum CodingKeys: String, CodingKey {
        case name
        case profileImageURL = "profile_image_url"
    }
}

struct ContentView: View {
    private let urlString = "https://qiita.com/api/v2/items?page=1&per_page=10"
    @State private var isShowAlert = false
    @State private var error: Error?
    @State private var items: [Item] = []

    var body: some View {
        VStack {
            if let item = items.randomElement() {
                VStack {
                    Text(item.createdAt)
                    Text(item.user.name)
                }
                .font(.title3)
            }

            Button("データ取得") {
                getItem()
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
        .alert(
            "エラー発生",
            isPresented: $isShowAlert,
            presenting: error,
            actions: {_ in }) { error in
                Text(error.localizedDescription)
            }
    }

    private func getItem() {
        Task {
            do {
                guard let url = URL(string: urlString) else {
                    return
                }

                let (data, _) = try await URLSession.shared.data(from: url)
                let decodedItems = try JSONDecoder().decode([Item].self, from: data)
                self.items = decodedItems

            } catch let decodeError as DecodingError {
                error = decodeError
                isShowAlert = true
            } catch let urlError as URLError {
                error = urlError
                isShowAlert = true
            } catch {
                self.error = error
                isShowAlert = true
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
  1. 正常にデータを取得したときは次のようになります。ユーザー名は表示されない場合もあります。

  2. Wifiを切ってみると次のようになります。

  3. urlStringを適当に触って、URLを不正なものにしてみます。

  4. User構造体のCodingkeysのprofile_image_urlを適当に触って不正なものにします。

説明

ポイントは、do-catch文のcatch節でのパターンマッチです。
それぞれの型にキャストすることでマッチさせます。型としてマッチさせるので、caseごとに列挙する必要がありません!

do {
     // do節でDecodingErrorまたはURLErrorが
     // 投げられる可能性あり
} catch let decodeError as DecodingError { // DecodingErrorにキャスト
  error = decodeError
  isShowAlert = true
} catch let urlError as URLError {     // URLErrorにキャスト
   error = urlError
   isShowAlert = true
} catch {                                // 上記2つに当てはまらないエラー
  self.error = error
   isShowAlert = true
}

まとめ

  • この書き方を見つけたきっかけが、Apple公式ドキュメントのError型の記事で、灯台下暗しといった感じでした。

  • アラートの表示だけでなく、エラーのcaseによって処理を分けられそうなので引き出しが増えてよかったです。
  • この記事が少しでも役に立ったと感じて頂けたら、イイねをしてもらえると嬉しいです!
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1