Swift解码API数据时遇字段缺失/为空如何避免崩溃?
我从API获取数据并解码到如下WatchProvider结构体:
struct WatchProvider: Identifiable, Decodable { let id = UUID() let link: String? let rent: [WatchProviderDetails]? let buy: [WatchProviderDetails]? let flatrate: [WatchProviderDetails]? enum CodingKeys: String, CodingKey { case link case rent case buy case flatrate } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) link = try container.decodeIfPresent(String.self, forKey: .link) rent = try container.decode([WatchProviderDetails].self, forKey: .rent) buy = try container.decode([WatchProviderDetails].self, forKey: .buy) flatrate = try container.decode([WatchProviderDetails].self, forKey: .buy) } }
但API返回的数据中,rent、flatrate等字段时常缺失或为空,解码时会抛出如下错误:
sink error: keyNotFound(CodingKeys(stringValue: "rent", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "results", intValue: nil), CodingKeys(stringValue: "US", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: "rent", intValue: nil) ("rent").", underlyingError: nil))
核心修复方案
你当前的问题根源在于手动实现解码初始化方法时,对rent、buy、flatrate使用了decode(_:forKey:)方法——这个方法强制要求目标字段必须存在,否则直接抛出解码错误。要实现字段缺失时忽略并返回nil,需改用decodeIfPresent(_:forKey:)方法。
另外注意代码中的笔误:flatrate字段解码时误用了.buy的Key,会导致flatrate始终读取buy的数据,需修正为.flatrate。
修改后的初始化方法如下:
init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) link = try container.decodeIfPresent(String.self, forKey: .link) // 改用decodeIfPresent,字段缺失时自动返回nil rent = try container.decodeIfPresent([WatchProviderDetails].self, forKey: .rent) buy = try container.decodeIfPresent([WatchProviderDetails].self, forKey: .buy) // 修正flatrate对应的CodingKey flatrate = try container.decodeIfPresent([WatchProviderDetails].self, forKey: .flatrate) }
额外优化:使用自动合成解码逻辑
如果你的结构体所有需要解码的字段都是可选类型,且CodingKeys与字段名完全匹配,可以直接移除手动实现的init(from decoder:),让Swift自动合成解码逻辑。自动合成的实现会默认对可选字段使用decodeIfPresent,无需额外代码,更简洁且不易出错。
优化后的结构体代码:
struct WatchProvider: Identifiable, Decodable { let id = UUID() let link: String? let rent: [WatchProviderDetails]? let buy: [WatchProviderDetails]? let flatrate: [WatchProviderDetails]? enum CodingKeys: String, CodingKey { case link case rent case buy case flatrate } // 移除手动解码实现,依赖Swift自动合成的逻辑 }
这样无论API返回的字段是否存在,可选类型的字段都会被自动处理为nil,不会中断整个解码流程。
内容的提问来源于stack exchange,提问作者Mark George

