記事を書くモチベーション
- <実現したかったこと>エラーが起きた時にユーザーにエラーの内容に応じたアラートを表示する。
- エラーハンドリングについて調べると、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()
}
}
説明
ポイントは、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によって処理を分けられそうなので引き出しが増えてよかったです。
- この記事が少しでも役に立ったと感じて頂けたら、イイねをしてもらえると嬉しいです!