Как динамически добавлять свойства к постоянному объекту Decodable в Swift? - PullRequest
0 голосов
/ 04 января 2019

Фон

В основном я получил API, который возвращает что-то вроде этого:

    "order_detail": [
        {
            "id": 6938,
            "order_id": 6404,
            "item_price": "4",
            ..
            "item": {
                "id": 12644,
                "ref": "Iced Caffe Americano",
                "short_description": "",
                ..

и в моем декодируемом объекте я получил это

public struct OrderDetail: Decodable {
    public let id: Int
    public let order_id: Int
    public let item_price: String?
    ..
    public let item: Item?

и

public struct Item: Decodable {
    public var id: Int
    public var ref: String?
    public var short_description: String?

Проблема в том, что где-то еще в коде есть метод, который ожидает, что объект Item будет иметь item_price.

Вопрос

Что я хочу сделать, так это изменить или изменить этот постоянный объект Item и динамически добавить к нему свойство item_price. Как я могу это сделать?

Обходные пути, другие решения

1. Изменить JSON

Я знаю, что есть много других решений этой же проблемы (я работаю над ней, пока мы говорим, которая просто модифицирует конечную точку API в соответствии с моими потребностями) ... но, опять же, этот вариант не всегда возможен (т.е. предположим, бэкэнд команда отдельная)

2. Изменить ожидание функции

Это также возможно, но и не недорого, поскольку эта функция используется во многих других местах приложения, которые я потенциально не могу контролировать

Ответы [ 2 ]

0 голосов
/ 04 января 2019

Это один из способов достижения этого

Взять на себя инициализацию Item в декодировании OrderDetail.

struct OrderDetail: Decodable {
    let id: Int
    let orderId: Int
    let itemPrice: String?
    let item: Item

    private enum OrderDetailCodingKey: CodingKey {
        case id
        case orderId
        case itemPrice
        case item
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: OrderDetailCodingKey.self)
        self.id = try container.decode(Int.self, forKey: .id)
        self.orderId = try container.decode(Int.self, forKey: .orderId)

        let itemPrice = try container.decode(String?.self, forKey: .itemPrice)
        self.itemPrice = itemPrice
        self.item = try Item(from: decoder, itemPrice: itemPrice)
    }
}

Использовать пользовательский инициализатор для создания вашего элемента.

struct Item: Decodable {
    let id: Int
    let ref: String?
    let shortDescription: String?
    let itemPrice: String?

    private enum ItemCodingKeys: CodingKey {
        case id
        case ref
        case shortDescription
    }

    init(from decoder: Decoder, itemPrice: String?) throws {
        let container = try decoder.container(keyedBy: ItemCodingKeys.self)
        self.id = try container.decode(Int.self, forKey: .id)
        self.ref = try? container.decode(String.self, forKey: .ref)
        self.shortDescription = try? container.decode(String.self, forKey: .shortDescription)
        self.itemPrice = itemPrice
    }
}

Вы можете вызвать следующую функцию для проверки работоспособности:

private func test() {
        let json = """
            {"id":6938,"orderId":6404,"itemPrice":"4","item":{"id":12644,"ref":"Iced Caffe Americano","shortDescription":""}}
        """
        let data = json.data(using: .utf8)
        let decoder = JSONDecoder()
        if let data = data {
            do {
                let order = try decoder.decode(OrderDetail.self, from: data)
                print(order)
            } catch let jsonError {
                os_log("JSON decoding failed [%@]", String(describing: jsonError))
            }
        } else {
            os_log("No data found")
        }
    }
0 голосов
/ 04 января 2019

Если вы хотите добавить свойство к типу Decodable, которое не является частью его представления JSON, просто нужно объявить соответствующий тип CodingKey и пропустить конкретное имя свойства, чтобы автоматически синтезировать init(from decoder:Decoder) инициализатор будет знать, что не нужно искать это значение в JSON.

Кстати, вы должны также соблюдать соглашение об именах Swift (lowerCamelCase для имен переменных) и использовать CodingKey для сопоставления ключей JSON с именами свойств.

public struct Item: Decodable {
    public var id: Int
    public var ref: String?
    public var shortDescription: String?
    public var itemPrice: String? // or whatever else its type needs to be

    private enum CodingKeys: String, CodingKey {
        case id, ref, shortDescription = "short_description"
    }
}
...