JSON 转 Swift Codable 模型实战:省去手写 iOS 数据模型
iOS 开发接 API 时,手写 Codable 结构体既慢又容易错。这篇讲怎么把 JSON 直接转成遵循 Codable 的 Swift struct,涵盖 CodingKeys 映射、可选类型推断和嵌套类型。
JSON 转 Swift Codable 模型实战:省去手写 iOS 数据模型
接第三方 API 时,我最烦的一步就是照着响应里几十个字段,一个一个敲 Swift 模型。字段是 snake_case,得想 CodingKeys;某个字段有时缺,得判断是不是可选;嵌套对象还要拆成单独的类型。敲错一个 key,运行时直接抛 keyNotFound,而不是编译期报错。把 JSON 直接转成遵循 Codable 的 Swift 结构体,这些活就全省了。
为什么 Swift 的 Codable 值得认真对待
Codable 是 Encodable 和 Decodable 的组合协议。一个类型只要声明遵循 Codable,编译器就帮你合成解码和编码逻辑,配合 JSONDecoder 就能把网络数据直接变成强类型对象。比起老写法 JSONSerialization.jsonObject(...) as? [String: Any] 再一层层 json?["id"] as? String 地挖,Codable 的好处是:每个字段的类型在编译期就定死了,key 打错编译不过,而不是上线后悄悄返回 nil。
iOS 解码模型习惯上用 struct,因为它是值类型,白送值语义、自动逐成员复制,现代 Swift 并发里还自动满足 Sendable。只有当模型要在多个视图间共享并就地修改,或者被 Core Data、Realm 这类层持有时,才切成 class。
CodingKeys:把线上 key 映射回 Swift 属性
JSON 里的 key 经常是 created_at、site_admin 这种 snake_case,但 Swift 习惯写成 createdAt、siteAdmin。属性名一旦和 key 不一致,就需要一个 CodingKeys 枚举把属性映射回精确的 JSON key:
enum CodingKeys: String, CodingKey {
case userId = "user_id"
case createdAt = "created_at"
}
有了它,JSONDecoder 不用全局设 keyDecodingStrategy 也能正确往返。当每个 key 都已经匹配 camelCase 属性时,枚举可以省略,由 Swift 自动合成。
可选类型:哪些字段该是 T?
这是最容易踩坑的地方。一个字段什么时候该写成 T??规则是:值出现过 JSON null,或者这个 key 在 JSON 数组的某些元素里缺失。第二条才是关键。单个对象没有"key 缺失"的信号,所以它的字段除非值本身就是 null,否则都被推断成非 Optional。想探可选性,得把样本包成数组。
举个真实例子。某 endpoint 有时带 cancelled_at,有时不带,你把两次响应作为数组粘进来:
输入:
[
{ "id": 1, "amount": 9.9 },
{ "id": 2, "amount": 10, "cancelled_at": "2026-06-01" }
]
转出:
struct OrderResponse: Codable {
let id: Int
let amount: Double
let cancelledAt: String?
enum CodingKeys: String, CodingKey {
case id
case amount
case cancelledAt = "cancelled_at"
}
}
注意三处推断:amount 在一个样本里是整数 10、另一个里是 9.9,所以放宽到安全的超集 Double;cancelledAt 在第一个元素里缺失,于是变成 String?;cancelled_at 和属性名不一致,枚举里就生成了对应的映射。在视图代码里写 if let cancelledAt,就能干净地区分"已取消"和"仍有效",而不是 key 一缺失就崩。
嵌套对象和金额这类细节
嵌套对象会抽成自己的命名类型,User.address 变成 UserAddress,而不是塞一坨字典。对象数组会折叠成一个共享类型,[{"a":1},{"a":1,"b":2}] 出来是一个同时带两个字段(b 可空)的 struct,不会变成两个几乎重复的类型。
有个地方要手动收尾:金额。工具对任何带小数的数字都推断成 Double,但 Double 是二进制浮点,会把 0.1 或货币总额表示错。涉及钱的字段,生成后请改成 Decimal。JSON 不带"这个数字需要精确十进制精度"的信号,这一步只能你来判断。
我自己的用法
我现在接新 API 的流程很固定:curl 一次响应,把 JSON 粘进 JSON 转 Swift Codable 工具,根名设成 OrderResponse,保持 struct + let,把输出拖进 .swift 文件,调一句 try JSONDecoder().decode(OrderResponse.self, from: data) 就完事。原本对着 25 个字段敲十几分钟、还得反复核对 key 拼写的活,压缩到几十秒,而且推断出来的可选性和类型比我手敲的更准。如果响应本身格式乱、想先看清结构,我会先用 JSON 格式化工具 把它缩进对齐,再丢去转模型。
省下来的时间和省下来的运行时崩溃,才是这套流程真正的价值。
Made by Toolora · Updated 2026-06-13