passepartout-apple/Passepartout/App/Reusable/ErrorHandler.swift

70 lines
2.0 KiB
Swift

import SwiftUI
// https://www.ralfebert.com/swiftui/generic-error-handling/
private struct ErrorAlert: Identifiable {
let id = UUID()
let title: String?
let message: String
let dismissAction: (() -> Void)?
}
@MainActor
final class ErrorHandler: ObservableObject {
static let shared = ErrorHandler()
@Published fileprivate var currentAlert: ErrorAlert?
func handle(_ error: Error, title: String? = nil, onDismiss: (() -> Void)? = nil) {
currentAlert = ErrorAlert(
title: title,
message: AppError(error).localizedDescription,
dismissAction: onDismiss
)
}
func handle(title: String, message: String, onDismiss: (() -> Void)? = nil) {
currentAlert = ErrorAlert(
title: title,
message: message,
dismissAction: onDismiss
)
}
}
struct HandleErrorsByShowingAlertViewModifier: ViewModifier {
@ObservedObject private var errorHandler: ErrorHandler
init() {
errorHandler = .shared
}
func body(content: Content) -> some View {
content
// Applying the alert for error handling using a background element
// is a workaround, if the alert would be applied directly,
// other .alert modifiers inside of content would not work anymore
.background(
EmptyView()
.alert(item: $errorHandler.currentAlert) { currentAlert in
Alert(
title: Text(currentAlert.title ?? Unlocalized.appName),
message: Text(currentAlert.message.withTrailingDot),
dismissButton: .cancel(Text(L10n.Global.Strings.ok)) {
currentAlert.dismissAction?()
}
)
}
)
}
}
extension View {
func withErrorHandler() -> some View {
modifier(HandleErrorsByShowingAlertViewModifier())
}
}