同时解码来自 API 调用和模型文件的 Codable 结构
decode a Codable struct from API call and a mockup file simultaneously
我目前正在做一个项目,我正在调用一个网络服务 returns 我使用 Codable
解析的 JSON,就像这样:
我的结构:
struct User: Codable {
var name: String
var age: Int
}
API 响应:
{ "name": "Romiro", "age": 27 }
解码代码:
let decoded = try! JSONDecoder().decode(User.self, from: data)
我们决定通过添加新字段来扩展 User
信息:
struct User: Codable {
var name: String
var age: Int
var detail: Detail
}
struct Detail: Codable {
var id: Int
var dob: Date
}
但是后端还没有开发,所以API响应仍然是
{ "name": "Romiro", "age": 27 }
是否有一种正确的方法来仅模拟 var detail: Detail
部分,方法是从项目资源中与 Detail
结构匹配的 detail-mock.json
文件加载它,但在保持 API 调用预先存在的 User
部分的平均时间?
通过这样做,我将能够保留调用端点的所有逻辑,并分流唯一正在开发的部分,仍然通过调用
let decoded = try! JSONDecoder().decode(User.self, from: data)
此外,有没有办法在不改变 API 的 json 响应的情况下做到这一点?我不想手动将 detail 部分附加到 je json response.
注意:显然,User
结构是一个示例,在我的项目中,这是一个复杂得多的结构
首先设置detail
为Detail?
类型,即
struct User: Codable {
var name: String
var age: Int
var detail: Detail?
}
您可以为 User
和 Detail
创建 2 个单独的对象,并将 detail
对象设置为 user.detail
,即
do {
var user = try JSONDecoder().decode(User.self, from: userData)
let detailData = Data() //replace this with the data obtained from Detail api
let detail = try JSONDecoder().decode(Detail.self, from: detailData)
user.detail = detail
} catch {
print(error)
}
您可以在 User
上实现自定义解码,如下所示:
struct User: Codable {
var name: String
var age: Int
var detail: Detail
enum CodingKeys: CodingKey {
case name, age, detail
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
age = try container.decode(Int.self, forKey: .age)
if let detail = try container.decodeIfPresent(Detail.self, forKey: .detail) {
self.detail = detail
} else {
let data = try Data(contentsOf: Bundle.main.url(forResource: "mockupDetail", withExtension: "json")!)
self.detail = try JSONDecoder().decode(Detail.self, from: data)
}
}
}
注意 init
中的 if
语句。这就是我决定是从实际的 json 还是模拟的 json.
中读取 detail
的地方
这样,您不需要将 detail
设为可选,但您需要手动解码其他属性。
我目前正在做一个项目,我正在调用一个网络服务 returns 我使用 Codable
解析的 JSON,就像这样:
我的结构:
struct User: Codable {
var name: String
var age: Int
}
API 响应:
{ "name": "Romiro", "age": 27 }
解码代码:
let decoded = try! JSONDecoder().decode(User.self, from: data)
我们决定通过添加新字段来扩展 User
信息:
struct User: Codable {
var name: String
var age: Int
var detail: Detail
}
struct Detail: Codable {
var id: Int
var dob: Date
}
但是后端还没有开发,所以API响应仍然是
{ "name": "Romiro", "age": 27 }
是否有一种正确的方法来仅模拟 var detail: Detail
部分,方法是从项目资源中与 Detail
结构匹配的 detail-mock.json
文件加载它,但在保持 API 调用预先存在的 User
部分的平均时间?
通过这样做,我将能够保留调用端点的所有逻辑,并分流唯一正在开发的部分,仍然通过调用
let decoded = try! JSONDecoder().decode(User.self, from: data)
此外,有没有办法在不改变 API 的 json 响应的情况下做到这一点?我不想手动将 detail 部分附加到 je json response.
注意:显然,User
结构是一个示例,在我的项目中,这是一个复杂得多的结构
首先设置detail
为Detail?
类型,即
struct User: Codable {
var name: String
var age: Int
var detail: Detail?
}
您可以为 User
和 Detail
创建 2 个单独的对象,并将 detail
对象设置为 user.detail
,即
do {
var user = try JSONDecoder().decode(User.self, from: userData)
let detailData = Data() //replace this with the data obtained from Detail api
let detail = try JSONDecoder().decode(Detail.self, from: detailData)
user.detail = detail
} catch {
print(error)
}
您可以在 User
上实现自定义解码,如下所示:
struct User: Codable {
var name: String
var age: Int
var detail: Detail
enum CodingKeys: CodingKey {
case name, age, detail
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
age = try container.decode(Int.self, forKey: .age)
if let detail = try container.decodeIfPresent(Detail.self, forKey: .detail) {
self.detail = detail
} else {
let data = try Data(contentsOf: Bundle.main.url(forResource: "mockupDetail", withExtension: "json")!)
self.detail = try JSONDecoder().decode(Detail.self, from: data)
}
}
}
注意 init
中的 if
语句。这就是我决定是从实际的 json 还是模拟的 json.
detail
的地方
这样,您不需要将 detail
设为可选,但您需要手动解码其他属性。