Decoder遇nil值崩溃及枚举编解码失效问题求助
嗨,我来帮你解决这两个编解码的常见问题,直接上实用方案:
一、非可选属性解码遇nil崩溃的解决方案
既然不能把属性改成可选类型,也没法用try-catch兜底,那核心思路就是在解码阶段手动处理null值,给非可选属性注入默认值,有两种常用方式:
方式1:手动实现init(from:)方法
直接在类的解码初始化方法里,对每个非可选字段用decodeIfPresent替代decode,再通过??赋予默认值。这样即使后端返回null,也不会触发崩溃,属性依然保持非可选类型:
class User: Codable { // 非可选属性,无需改为? let username: String let age: Int let score: Double enum CodingKeys: String, CodingKey { case username, age, score } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 给每个字段设置默认值,处理null场景 username = try container.decodeIfPresent(String.self, forKey: .username) ?? "匿名用户" age = try container.decodeIfPresent(Int.self, forKey: .age) ?? 18 score = try container.decodeIfPresent(Double.self, forKey: .score) ?? 0.0 } }
方式2:用属性包装器简化代码(适合多属性场景)
如果类的属性很多,手动写init(from:)会很繁琐,可以自定义一个@Default属性包装器,让非可选属性自动使用默认值解码:
// 定义协议,要求类型提供默认值 protocol DefaultValueProvider { static var defaultValue: Self { get } } // 给基础类型扩展默认值 extension String: DefaultValueProvider { static var defaultValue: String { "" } } extension Int: DefaultValueProvider { static var defaultValue: Int { 0 } } extension Double: DefaultValueProvider { static var defaultValue: Double { 0.0 } } // 自定义属性包装器 @propertyWrapper struct Default<T: Codable & DefaultValueProvider> { var wrappedValue: T } // 让包装器支持Codable extension Default: Codable { init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() wrappedValue = try container.decodeIfPresent(T.self) ?? T.defaultValue } } // 使用示例 class User: Codable { let username: String // 如果需要自定义默认值,还是用方式1 @Default var age: Int // 自动用Int的默认值0 @Default var score: Double // 自动用Double的默认值0.0 }
二、枚举编解码异常的修复方案
枚举编解码出问题,通常是原始值不匹配或关联值格式不兼容导致的,分两种情况处理:
情况1:带原始值的枚举(如String/Int)
如果后端返回的原始值不在枚举的case列表中,默认解码会崩溃,这时候需要自定义解码逻辑,给未知值设置默认case:
enum Gender: String, Codable { case male, female, other // 自定义解码,处理未知原始值 init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let rawValue = try container.decode(String.self) // 如果原始值不存在,默认用.other self = Gender(rawValue: rawValue) ?? .other } }
情况2:带关联值的枚举
带关联值的枚举无法自动合成Codable(或合成的格式和后端不匹配),必须手动实现init(from:)和encode(to:):
enum PaymentMethod: Codable { case creditCard(number: String, expiry: String) case paypal(email: String) // 定义编码需要的Key enum CodingKeys: String, CodingKey { case type, number, expiry, email } // 自定义解码逻辑 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(String.self, forKey: .type) switch type { case "credit_card": let number = try container.decode(String.self, forKey: .number) let expiry = try container.decode(String.self, forKey: .expiry) self = .creditCard(number: number, expiry: expiry) case "paypal": let email = try container.decode(String.self, forKey: .email) self = .paypal(email: email) default: // 可以抛自定义错误,或者设置默认case(比如case unknown) throw DecodingError.dataCorruptedError( forKey: .type, in: container, debugDescription: "不支持的支付方式:\(type)" ) } } // 自定义编码逻辑 func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { case .creditCard(let number, let expiry): try container.encode("credit_card", forKey: .type) try container.encode(number, forKey: .number) try container.encode(expiry, forKey: .expiry) case .paypal(let email): try container.encode("paypal", forKey: .type) try container.encode(email, forKey: .email) } } }
内容的提问来源于stack exchange,提问作者Iraniya Naynesh
相关产品推荐
相关产品推荐

