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

为DecodingError实现CustomNSError后未生效的原因排查

问题原因与解决方案

为什么你的扩展没生效?

核心问题出在Swift Error与Objective-C NSError的桥接机制上:
Crashlytics的recordError:是Objective-C方法,接受NSError *参数。当你把Swift原生的DecodingError(遵循Error协议)传给OC方法时,Swift会自动触发系统默认的桥接逻辑——这个过程完全绕过了你实现的CustomNSError扩展。

CustomNSError协议本质是给Swift环境设计的,系统默认的跨语言桥接不会调用协议里的自定义方法。对于DecodingError这种系统内置的Error枚举,Swift有一套固定的桥接规则,直接将其映射为带有NSCocoaErrorDomain的标准NSError,完全无视你写的扩展逻辑。

正确的解决思路:自定义错误包装器

既然扩展系统Error类型的CustomNSError在OC桥接时不生效,我们可以把系统DecodingError包装成自定义的Error类型,让这个自定义类型实现CustomNSError——这样桥接到OC时,系统就会使用我们的自定义规则生成NSError。

步骤1:创建自定义错误包装器

enum AppDecodingError: CustomNSError {
    case decodingError(DecodingError)
    
    // 自定义错误域
    static var errorDomain: String {
        return "com.domain.App.ErrorDomain.DecodingError"
    }
    
    // 自定义错误码
    var errorCode: Int {
        switch self {
        case .decodingError(let error):
            switch error {
            case .dataCorrupted: return 1
            case .keyNotFound: return 2
            case .typeMismatch: return 3
            case .valueNotFound: return 4
            }
        }
    }
    
    // 自定义用户信息
    var errorUserInfo: [String : Any] {
        switch self {
        case .decodingError(let error):
            switch error {
            case .dataCorrupted(let context):
                var userInfo: [String: Any] = [
                    "debugDescription": context.debugDescription,
                    "codingPath": context.codingPath.map { $0.stringValue }.joined(separator: ".")
                ]
                guard let underlyingError = context.underlyingError else { return userInfo }
                userInfo["underlyingErrorLocalizedDescription"] = underlyingError.localizedDescription
                userInfo["underlyingErrorDebugDescription"] = (underlyingError as NSError).debugDescription
                userInfo["underlyingErrorUserInfo"] = (underlyingError as NSError).userInfo.map {
                    "\($0.key): \(String(describing: $0.value))"
                }.joined(separator: ", ")
                return userInfo
            case .keyNotFound(let codingKey, let context):
                return [
                    "debugDescription": context.debugDescription,
                    "codingPath": context.codingPath.map { $0.stringValue }.joined(separator: "."),
                    "codingKey": codingKey.stringValue
                ]
            case .typeMismatch(_, let context), .valueNotFound(_, let context):
                return [
                    "debugDescription": context.debugDescription,
                    "codingPath": context.codingPath.map { $0.stringValue }.joined(separator: ".")
                ]
            }
        }
    }
}

步骤2:修改日志工具的log方法

让方法自动识别并包装DecodingError:

func log(_ error: Error) {
    if let decodingError = error as? DecodingError {
        // 包装成自定义错误后传入Crashlytics
        let wrappedError = AppDecodingError.decodingError(decodingError)
        Crashlytics.sharedInstance().recordError(wrappedError)
    } else {
        // 其他错误直接处理
        Crashlytics.sharedInstance().recordError(error)
    }
}

步骤3:简化调用逻辑

现在你不需要手动判断错误类型,直接传入捕获到的Error即可:

do {
    let decoder = JSONDecoder()
    let test = try decoder.decode(SomeObject.self, from: someShitJSON)
} catch {
    ErrorLogger.sharedInstance.log(error)
}

这样处理后,Crashlytics控制台就能正确显示你自定义的错误域、错误码和调试信息了,完全不需要额外手动传入用户信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:45:24