Итак, в нашем API есть два типа ответов JSON:
{
"data": { } // some object in here
"meta": { } // an object in here
}
и
{
"data": [ ] // array of objects
"meta": { } // an object in here
}
Для их декодирования мы используем JSONDecoder () и следующую общую структуру ответа:
public struct Response<T: Codable>: Codable {
public let data: T
public let meta: Meta?
}
Это отлично работает со Swift 4.0 с использованием .map(Response<[MyObject]>.self)
или .map(Response<MySingleObject>.self)
но по какой-то причине это больше не работает со Swift 4.1 и Xcode 9.3. Похоже, он вообще не отображает "данные" и поэтому считает, что список [MyObject]
находится на первом уровне.
dataCorrupted: Swift.DecodingError.Context
▿ codingPath: 3 elements
- CodingKeys(stringValue: "data", intValue: nil)
▿ _JSONKey(stringValue: "Index 0", intValue: 0)
- stringValue: "Index 0"
▿ intValue: Optional(0)
- some: 0
- CodingKeys(stringValue: "creationDate", intValue: nil)
- debugDescription: "Date string does not match format expected by formatter."
Обратите внимание, что «creationDate» является свойством MyObject
. Формат даты определенно правильный (установлен в .formatted(customFormatter)
в декодере), поскольку он работает со Swift 4.0 в Xcode 9.2.
Как мы можем сохранить такое же поведение со Swift 4.1? Цель здесь заключалась в том, чтобы не создавать типизированный Response
Тип для каждого ответа API, а использовать вместо него универсальный, потому что единственное различие заключается в типе объекта ответа и в том, что иногда он возвращает список, а иногда один объект под data
.
Также по теме: есть ли способ обеспечить соблюдение этого условия, если мы используем Response<[MyObject]>.self
, который также MyObject
должен соответствовать Codable
?
Заранее спасибо.
Редактировать:
Приведенный ниже код правильно отображается в Xcode 9.2 и Swift 4, но не отображается (создает ноль) в Xcode 9.3 и Swift 4.1.
public struct MyObject: Codable {
public let creationDate: Date
enum CodingKeys: String, CodingKey {
case creationDate = "creation_date"
}
}
public struct Response<T: Codable>: Codable {
public let data: T
public let meta: Meta?
}
public struct Meta: Codable {
public let count: Int?
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-mm-dd HH:mm:ss"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
let jsonDataSingle: Data = """
{
"data": { "creation_date": "2018-04-29 18:00:11" },
"meta": null
}
""".data(using: .utf8)!
let jsonDataList: Data = """
{
"data": [{ "creation_date": "2018-04-10 17:00:11" }, { "creation_date": "2018-04-29 18:00:11" }],
"meta": null
}
""".data(using: .utf8)!
let singleObject = try? decoder.decode(Response<MyObject>.self, from: jsonDataSingle)
dump(singleObject)
let listOfObjects = try? decoder.decode(Response<[MyObject]>.self, from: jsonDataList)
dump(listOfObjects)