декодировать структуру Codable из вызова API и файла макета одновременно - PullRequest
2 голосов
/ 27 июня 2019

В настоящее время я работаю над проектом, в котором я вызываю веб-сервис, который возвращает мне JSON, который я анализирую, используя Codable, например:

Моя структура:

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, загружая ее из *Файл 1030 * в ресурсах проекта, который соответствует структуре Detail, но в то же время удерживает вызов API для уже существующей части User?

Сделав это, я смогу сохранить всю логику вызова конечной точки и шунтирования единственной части, которая находится в стадии разработки, все еще путем вызова

let decoded = try! JSONDecoder().decode(User.self, from: data)

Кроме того, есть лиспособ сделать это без изменения ответа json от API?Я не хочу вручную добавлять деталь detail к ответу je json.

Примечание: Очевидно, структура User является примером, в моем проекте это оченьболее сложная структура

Ответы [ 2 ]

3 голосов
/ 27 июня 2019

Вы можете реализовать пользовательское декодирование на 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)
        }
    }
}

Обратите внимание на оператор if в init.Здесь я решаю, читать ли detail с фактического json или с поддельным json.

Таким образом, вам не нужно делать detail необязательным, но вам нужно будет вручную декодироватьдругие свойства.

1 голос
/ 27 июня 2019

Прежде всего установите detail как Detail? тип, т.е.

struct User: Codable {
    var name: String
    var age: Int
    var detail: Detail?
}

Вы можете создать 2 отдельных объекта для User и Detail и установить объект 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)
}
...