Generic Decodable перестал работать с Swift 4.1

Итак, в нашем 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)

person Patrick Schneider    schedule 29.04.2018    source источник
comment
@matt Я редактирую сообщение с образцом кода, который работает в Xcode 9.2 / Swift 4, но не работает в Xcode 9.3 и Swift 4.1. Если это неправильное решение, мы ценим предложения, как лучше справиться с этим случаем (без создания модели ответа для каждой конечной точки :( ...) спасибо   -  person Patrick Schneider    schedule 29.04.2018
comment
@matt Это просто пример объекта с одним свойством. MyObject может быть различными json-объектами или списками с объектами из API. Подумайте: Response ‹[Product] .self›, Response ‹User.self›, Response ‹[Order] .self›. Интересно, как это можно было бы сопоставить с объектами Codable без общего типа Response ‹T: Codable›. Фактически решенную проблему см. В моем комментарии к ответу Прайса ниже.   -  person Patrick Schneider    schedule 29.04.2018


Ответы (1)


Я не получаю ошибок. Пожалуйста, опубликуйте это. В качестве упражнения я добавил соответствие CustomStringConvertible и заменил dump на print.

extension Response: CustomStringConvertible {
  public var description: String {
    return "data = \(data) | meta = \(meta)"
  }
}

extension MyObject: CustomStringConvertible {
  public var description: String {
    return "date = \(creationDate)"
  }
}

Я использую Xcode 9.3, Swift 4.1 версии 9E145, выпущенный через App Store.

введите описание изображения здесь

person Price Ringo    schedule 29.04.2018
comment
Итак, ваш ответ заставил меня снова это проверить. На этот раз мой телефон не был подключен, поэтому симулятор запустился. Оказывается, настоящая проблема заключалась в том, что мой телефон работает в 12-часовом формате времени, а DateFormatter перезаписывает пользовательский формат HH. См. stackoverflow.com/questions/2135267/ для справки. Спасибо за попытку и доказательство того, что отображение не является реальной проблемой. - person Patrick Schneider; 29.04.2018