Расшифруйте тип Swift, который является обернутым типом Codable, с дополнительным свойством Codable - PullRequest
0 голосов
/ 10 апреля 2019

У меня есть тип Codable, скажем, Car, который определяется как:

struct Car: Codable {
    let age: Int
    let color: String
}

Я могу кодировать / декодировать это просто отлично.

С моей системой персистентности, когда объект сохраняется, ему присваивается свойство _id, которое является String, например. 5cae04b533376609456d40ed.

Таким образом, когда я читаю Data из постоянного хранилища и затем пытаюсь декодировать его, в нем появляются дополнительные байты, которые представляют свойство _id и связанное с ним значение String.

Я не контролирую различные типы, которые могут быть закодированы и сохранены в магазине. Единственное ограничение на них заключается в том, что они Codable.

То, что я хочу сделать, это декодировать Data, который я получаю при чтении из магазина (с включенным материалом _id), в тип, похожий на Wrapped<T: Codable>, который будет определяться как что-то вроде (в простейшем виде):

struct Wrapped<T: Codable> {
    let _id: String
    let value: T
}

Однако я не уверен, что буду об этом.

Одна попытка, которую я предпринял, состояла в том, чтобы определить пользовательскую функцию decode, но это не зашло слишком далеко, поскольку я не могу получить доступ к CodingKeys типа T, который делает вещи настолько, насколько я могу сказать, что невозможно при таком подходе.

Может быть, есть другой подход, который заставил бы все работать так, как мне хотелось бы?

Ответы [ 2 ]

1 голос
/ 10 апреля 2019

Вы можете написать пользовательскую функцию декодирования для вашего типа Wrapped, которая анализирует _id, а затем передает декодер в упакованный тип, чтобы он мог декодировать свои собственные свойства:

struct Wrapped<T: Codable>: Decodable {
    let _id: String
    let value: T

    private enum CodingKeys: String, CodingKey {
        case _id
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        _id = try container.decode(String.self, forKey: ._id)
        value = try T(from: decoder)
    }
}
0 голосов
/ 10 апреля 2019

Вы можете просто объявить, что свойство _id не должно декодироваться, определив свой пользовательский CodingKeys и пропустив оттуда _id. Вам также необходимо назначить значение по умолчанию для не декодированных свойств (_id в вашем случае), если вы хотите использовать автоматически синтезированный инициализатор.

Для конкретного типа:

struct Car: Codable {
    let age: Int
    let color: String
    let _id:Int = 0

    enum CodingKeys: String, CodingKey {
        case age, color
    }
}

Вы можете достичь этого для всех ваших постоянных типов.

Если вы не хотите создавать перечисление CodingKeys для всех сохраняемых типов, вы можете следовать принятому вами подходу универсального типа-оболочки, но вам нужно будет создать собственные методы init(from:) и encode(to:).

struct Persisted<T: Codable>: Codable {
    let _id:Int = 0
    let value:T

    init(from decoder:Decoder) throws {
        value = try decoder.singleValueContainer().decode(T.self)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(value)
    }
}
...