如何在单一报告模块中展示Persistence与App Store错误
跨类错误统一展示方案建议
问题背景
需要在单一报告模块中展示App Store类及Persistence.swift文件中的错误。当前视图内的错误可通过EnvironmentObject关联至错误报告模块,但该方式无法直接应用于非视图类(如App Store购买逻辑类、Persistence.swift)的错误上报。
现有代码示例
视图内错误上报示例
// the save button has been pressed struct saveButton: View { @Environment(\.managedObjectContext) var viewContext @EnvironmentObject var errorHandling: ErrorHandling var body: some View { // prepping data for save to core data do { try viewContext.save() } catch { // report: Unable to Save Transaction self.errorHandling.handle(error: AppError.saveTransactionError) } }
App Store购买方法(需补充错误上报)
@MainActor func purchase() { Task.init { guard let product = products.first else { return } guard AppStore.canMakePayments else { return } do { let result = try await product.purchase() switch result { case .success(let verification): switch verification { case .verified(let transaction): await transaction.finish() // save to user defaults self.purchasedIds = transaction.productID break case .unverified: // throw PurchaseError.failed // FIX: report error (Unable to purchase verification failed) break } break case .userCancelled: break // asked to buy (setting on phone prevents purchases) case .pending: break default: break } } catch { print(error.localizedDescription) // FIX: report error (Unable to Complete Purchase) } } }
自定义错误枚举
enum AppError: LocalizedError { case storePurchaseError case storeVerificationError var errorDescription: String? { switch self { case .storePurchaseError: return NSLocalizedString("Store Purchase Error", comment: "") case .storeVerificationError: return NSLocalizedString("Store Verification Error", comment: "") } } }
错误弹窗展示模块代码
struct ErrorAlert: Identifiable { var id = UUID() var message: String var dismissAction: (() -> Void)? } class ErrorHandling: ObservableObject { @Published var currentAlert: ErrorAlert? func handle(error: Error) { currentAlert = ErrorAlert(message: error.localizedDescription) } } struct HandleErrorsByShowingAlertViewModifier: ViewModifier { @StateObject var errorHandling = ErrorHandling() func body(content: Content) -> some View { content .environmentObject(errorHandling) .background( EmptyView() .alert(item: $errorHandling.currentAlert) { currentAlert in Alert( title: Text("Error"), message: Text(currentAlert.message), dismissButton: .default(Text("Ok")) { currentAlert.dismissAction?() } ) } ) } } extension View { func withErrorHandling() -> some View { modifier(HandleErrorsByShowingAlertViewModifier()) } }
跨类错误上报解决方案
方案1:依赖注入传递ErrorHandling实例
将ErrorHandling作为依赖传递给非视图类,确保类之间的耦合度可控:
- 修改非视图类(如StoreManager)的初始化方法,接收
ErrorHandling实例:class StoreManager { private let errorHandling: ErrorHandling init(errorHandling: ErrorHandling) { self.errorHandling = errorHandling } @MainActor func purchase() { Task.init { // ... 原有购买逻辑 ... case .unverified: // 上报验证失败错误 errorHandling.handle(error: AppError.storeVerificationError) break // ... 原有逻辑 ... } catch { // 上报购买失败错误 errorHandling.handle(error: AppError.storePurchaseError) } } } - 在视图中创建非视图类实例时,从
EnvironmentObject获取errorHandling并传入:struct PurchaseView: View { @EnvironmentObject var errorHandling: ErrorHandling @State private var storeManager: StoreManager? var body: some View { Button("Purchase") { storeManager?.purchase() } .onAppear { storeManager = StoreManager(errorHandling: errorHandling) } } }
方案2:单例模式快速实现(适合小型项目)
将ErrorHandling改为单例,非视图类可直接调用静态实例上报错误:
- 修改
ErrorHandling为单例:class ErrorHandling: ObservableObject { static let shared = ErrorHandling() @Published var currentAlert: ErrorAlert? func handle(error: Error) { currentAlert = ErrorAlert(message: error.localizedDescription) } } - 修改视图Modifier,使用单例实例:
struct HandleErrorsByShowingAlertViewModifier: ViewModifier { @ObservedObject var errorHandling = ErrorHandling.shared func body(content: Content) -> some View { content .environmentObject(errorHandling) .background( EmptyView() .alert(item: $errorHandling.currentAlert) { currentAlert in Alert( title: Text("Error"), message: Text(currentAlert.message), dismissButton: .default(Text("Ok")) { currentAlert.dismissAction?() } ) } ) } } - 非视图类中直接调用上报:
注意:单例会降低代码可测试性,大型项目建议优先使用依赖注入方案。case .unverified: ErrorHandling.shared.handle(error: AppError.storeVerificationError) break // ... catch { ErrorHandling.shared.handle(error: AppError.storePurchaseError) }
方案3:Combine事件流解耦(进阶方案)
通过Combine发布错误事件,实现非视图类与错误模块的解耦:
- 扩展
ErrorHandling添加错误事件流:import Combine class ErrorHandling: ObservableObject { @Published var currentAlert: ErrorAlert? private let errorSubject = PassthroughSubject<Error, Never>() private var cancellables = Set<AnyCancellable>() init() { errorSubject .receive(on: DispatchQueue.main) .sink { [weak self] error in self?.currentAlert = ErrorAlert(message: error.localizedDescription) } .store(in: &cancellables) } func handle(error: Error) { errorSubject.send(error) } func sendError(_ error: Error) { errorSubject.send(error) } } - 非视图类持有
ErrorHandling实例后,调用sendError方法上报错误,逻辑与依赖注入方案一致,但通过Combine实现了事件解耦。
内容的提问来源于stack exchange,提问作者Galen Smith
相关产品推荐
相关产品推荐

