You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单一报告模块中展示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作为依赖传递给非视图类,确保类之间的耦合度可控:

  1. 修改非视图类(如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)
            }
        }
    }
    
  2. 在视图中创建非视图类实例时,从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改为单例,非视图类可直接调用静态实例上报错误:

  1. 修改ErrorHandling为单例:
    class ErrorHandling: ObservableObject {
        static let shared = ErrorHandling()
        @Published var currentAlert: ErrorAlert?
    
        func handle(error: Error) {
            currentAlert = ErrorAlert(message: error.localizedDescription)
        }
    }
    
  2. 修改视图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?()
                                }
                            )
                        }
                )
        }
    }
    
  3. 非视图类中直接调用上报:
    case .unverified:
        ErrorHandling.shared.handle(error: AppError.storeVerificationError)
        break
    // ...
    catch {
        ErrorHandling.shared.handle(error: AppError.storePurchaseError)
    }
    
    注意:单例会降低代码可测试性,大型项目建议优先使用依赖注入方案。

方案3:Combine事件流解耦(进阶方案)

通过Combine发布错误事件,实现非视图类与错误模块的解耦:

  1. 扩展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)
        }
    }
    
  2. 非视图类持有ErrorHandling实例后,调用sendError方法上报错误,逻辑与依赖注入方案一致,但通过Combine实现了事件解耦。

内容的提问来源于stack exchange,提问作者Galen Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 21:05:37