为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
相关产品推荐
相关产品推荐

