Swift中UserModel不遵循Decodable协议问题求助
问题排查与解决:UserModel 不遵循 Decodable/Equatable/Hashable 协议
核心问题拆解
1. 初始 Decodable 兼容错误原因
初始代码里,CodingKeys仅定义了tokenIssuedAt、createdAt、updatedAt三个映射字段,但结构体中还有id、email、settings三个属性未在CodingKeys中声明。编译器自动合成Codable实现时,若存在CodingKeys,只会处理其中列出的字段,导致找不到剩余属性的解码规则,触发Type 'UserModel' does not conform to protocol 'Decodable'错误。
另外,嵌套的Settings结构体存在字段名不匹配:JSON中是"wishlist",代码里写的是wishlists(多了末尾的s),这会直接导致解码失败,进一步加剧Decodable兼容问题。
2. 自定义初始化器引发的 Equatable/Hashable 错误
添加自定义init(from:)初始化器后,编译器无法自动合成Equatable和Hashable的实现——这两个协议的自动合成依赖于编译器能完整推断所有属性的比较/哈希逻辑,自定义初始化器打破了这个条件,因此出现新的协议不兼容错误。
修复方案
方案一:依赖编译器自动合成(推荐)
无需自定义初始化器,只需修正两个关键点:
- 让
CodingKeys包含所有结构体属性,需要映射的字段保留别名,其余字段直接用原名。 - 修正
Settings的字段名,与JSON完全匹配。
完整代码:
struct UserModel: Codable, Hashable, Identifiable { let id: String let email: String let tokenIssuedAt: Int let createdAt: String let updatedAt: String let settings: Settings enum CodingKeys: String, CodingKey { case id, email, settings case tokenIssuedAt = "token_issued_at" case createdAt = "created_at" case updatedAt = "updated_at" } struct Settings: Codable, Hashable { var wishlist, collection, findable: Bool } }
方案二:自定义初始化器 + 手动实现 Equatable/Hashable
如果必须使用自定义初始化器(比如有特殊解码逻辑),需要:
- 在初始化器中解码所有属性,不能只处理
settings。 - 手动实现
Equatable的==方法和Hashable的hash(into:)方法。
示例代码:
struct UserModel: Codable, Hashable, Identifiable { let id: String let email: String let tokenIssuedAt: Int let createdAt: String let updatedAt: String let settings: Settings enum CodingKeys: String, CodingKey { case id, email, settings case tokenIssuedAt = "token_issued_at" case createdAt = "created_at" case updatedAt = "updated_at" } // 自定义解码初始化器,必须处理所有属性 init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(String.self, forKey: .id) email = try container.decode(String.self, forKey: .email) tokenIssuedAt = try container.decode(Int.self, forKey: .tokenIssuedAt) createdAt = try container.decode(String.self, forKey: .createdAt) updatedAt = try container.decode(String.self, forKey: .updatedAt) settings = try container.decode(Settings.self, forKey: .settings) } struct Settings: Codable, Hashable { var wishlist, collection, findable: Bool } // 手动实现Equatable static func == (lhs: UserModel, rhs: UserModel) -> Bool { return lhs.id == rhs.id && lhs.email == rhs.email && lhs.tokenIssuedAt == rhs.tokenIssuedAt && lhs.createdAt == rhs.createdAt && lhs.updatedAt == rhs.updatedAt && lhs.settings == rhs.settings } // 手动实现Hashable func hash(into hasher: inout Hasher) { hasher.combine(id) hasher.combine(email) hasher.combine(tokenIssuedAt) hasher.combine(createdAt) hasher.combine(updatedAt) hasher.combine(settings) } }
验证解码
可用以下代码测试解码是否正常:
let jsonString = """ [ { "id": "1cc02c5f-2cb3-4546-a2d7-a824188e667d", "email": "trenton@example.com", "token_issued_at": 1676418140, "created_at": "2023-02-09T01:51:08.733Z", "updated_at": "2023-02-14T23:46:14.136Z", "settings": { "collection": false, "wishlist": false, "findable": true } } ] """ if let jsonData = jsonString.data(using: .utf8) { do { let users = try JSONDecoder().decode([UserModel].self, from: jsonData) print(users.first?.settings.wishlist) // 输出 false,符合JSON数据 } catch { print("解码失败:\(error)") } }
内容的提问来源于stack exchange,提问作者Anon
相关产品推荐
相关产品推荐

