Swift中do-try-catch二次解码自定义结构体出现编译错误如何解决?
问题原因
URLSession.shared.dataTask的回调闭包是不允许抛出未处理的错误的非抛错类型(Data?, URLResponse?, Error?) -> Void。你的代码里第二个try decoder.decode(ErrorInfoWithErrorCode.self, from: data)没有被do-catch包裹,它抛出的错误会向上穿透整个闭包,导致编译器判定这个闭包是抛错类型,和系统要求的类型不匹配,触发编译错误。
解决方案
方案1:调整do-catch结构,包裹所有可能抛错的try
把第二次解码逻辑也放到独立的do-catch块中处理即可,修改后的代码如下:
struct ErrorInfo: Codable { public let message: String? public let details: String? public let debugInformation: String? } struct ErrorInfoWithErrorCode: Codable { public let message: [String: Int] public let details: String? public let debugInformation: String? } let request = URLRequest(url: URL(string: "https://www.google.com/")!) let decoder = JSONDecoder() // 原代码漏了decoder初始化,此处补上 let task = URLSession.shared.dataTask(with: request) { data, response, error in if let httpResponse = response as? HTTPURLResponse, !(200..<300).contains(httpResponse.statusCode), let data = data { do { // 先尝试解码为message是String的结构 let errorMessage = try decoder.decode(ErrorInfo.self, from: data) print(errorMessage) } catch { do { // 第一次解码失败,尝试第二种结构 let errorMessage = try decoder.decode(ErrorInfoWithErrorCode.self, from: data) print(errorMessage) } catch { // 两次都失败的处理逻辑,不要用fatalError,线上环境会触发崩溃 print("未知错误格式:\(error.localizedDescription)") } } } } task.resume()
方案2:更优雅的实现:合并两个结构体,兼容两种message类型
你可以直接定义一个枚举适配两种类型的message,不需要写两个独立的Codable结构体,代码更简洁易维护:
enum MessageType: Codable { case string(String) case dict([String: Int]) init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let str = try? container.decode(String.self) { self = .string(str) } else if let dict = try? container.decode([String: Int].self) { self = .dict(dict) } else { throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "message字段类型不支持")) } } } struct UnifiedErrorInfo: Codable { public let message: MessageType public let details: String? public let debugInformation: String? } let request = URLRequest(url: URL(string: "https://www.google.com/")!) let decoder = JSONDecoder() // 使用时只需要解码一次 let task = URLSession.shared.dataTask(with: request) { data, response, error in if let httpResponse = response as? HTTPURLResponse, !(200..<300).contains(httpResponse.statusCode), let data = data { guard let errorInfo = try? decoder.decode(UnifiedErrorInfo.self, from: data) else { print("解码失败") return } // 根据message类型做不同处理 switch errorInfo.message { case .string(let str): print("错误信息:\(str)") case .dict(let dict): print("错误码字典:\(dict)") } } } task.resume()
内容的提问来源于stack exchange,提问作者hiroshi046
相关产品推荐
相关产品推荐

