🪣
Appで取得したエラーを別Viewに伝播するシンプルなパターン
概要
- Appで受け取ったエラーを別の画面でアラート表示したいというケースに対して、
@Observableな状態クラスを 1 つ用意し、Appがそれを保持・更新し、View側は@Environmentから監視するパターンを考える
全体方針
- アプリ全体のエラー状態を表すクラス
AppErrorStateを@Observableで定義する - Appが
AppErrorStateを保持し、.environment(appErrorState)で全Viewに渡す - 各Viewは
@Environment(AppErrorState.self)で状態を読み取り、.onChange(of:)などで変化を検知してアラートを出す- アラートを閉じるタイミングで
lastError = .noneに戻す
- アラートを閉じるタイミングで
エラー状態クラスAppErrorState
import Observation
/// アプリ全体で共有するエラー状態。
@Observable
final class AppErrorState {
/// 直近のエラー種別。
enum LastError: Equatable {
/// エラーなし(初期状態/ユーザー確認後のリセット状態)
case none
/// メッセージを伴うエラー
case message(String)
}
/// 直近のエラー。
///
/// View 側はこの値を監視してアラート表示などを行う。
/// アラート表示後は `.none` に戻す運用を想定。
var lastError: LastError = .none
}
Appで保持してEnvironment に流す
import SwiftUI
@main
struct ExampleApp: App {
/// アプリ全体のエラー状態
private let appErrorState = AppErrorState()
private let sampleUseCase = SampleUseCase()
var body: some Scene {
WindowGroup {
RootView()
// AppErrorStateをEnvironmentに注入する
.environment(appErrorState)
.task {
// 起動時に何か同期処理を行う
await performInitialSync()
}
}
}
private func performInitialSync() async {
do {
try await sampleUseCase.run()
} catch {
await MainActor.run {
// エラーを受けてAppErrorStateを更新
appErrorState.lastError = .message("初期同期に失敗しました。時間をおいて再度お試しください。")
}
}
}
}
View 側で監視してアラート表示
import SwiftUI
struct RootView: View {
/// AppErrorStateをEnvironmentから取得
@Environment(AppErrorState.self) private var appErrorState
/// この画面でのエラーアラート表示状態
@State private var isShowingErrorAlert = false
@State private var errorMessage = ""
var body: some View {
NavigationStack {
VStack(spacing: 16) {
Text("Root View")
.font(.title2)
NavigationLink("詳細画面へ") {
DetailView()
}
}
.padding()
.task {
// 起動時にすでにエラーがあれば一度だけ拾う
handle(lastError: appErrorState.lastError)
}
.onChange(of: appErrorState.lastError) { _, newValue in
// 以降の変化を監視
handle(lastError: newValue)
}
.alert("エラー", isPresented: $isShowingErrorAlert) {
Button("OK") {
// アラートを閉じたタイミングでリセット
appErrorState.lastError = .none
}
} message: {
Text(errorMessage)
}
}
}
/// AppErrorState の内容に応じてアラート表示を制御する。
///
/// - Parameter lastError: 直近のエラー状態。
private func handle(lastError: AppErrorState.LastError) {
switch lastError {
case .none:
break
case .message(let message):
errorMessage = message
isShowingErrorAlert = true
}
}
}
Discussion