Swift Decodable: ¿cómo evitar el tipo genérico?

Aug 19 2020

Estoy recuperando un objeto anidado complejo de mi API REST JSON.

DocumentDraft
 - uuid: String
 - schema: Schema // Very complicated object with many variations
 - url: String
 - values: [Value]
 - successors: [String]
 - predecessors: [String]

Value
 - key: String
 - val: String? OR [String]?   // <-- This is the problem

Supongo que la forma correcta de lidiar con esto es introducir un tipo genérico.

struct Value<V: Decodable>: Decodable {
  let key: String
  let val: V?
}

... pero aún así, valuespodría ser una matriz mixta, por lo que no veo cómo declarar lo que Ves ayudaría.

Pero luego, por supuesto, el tipo genérico se propaga hacia arriba en la jerarquía, al DocumentDraftobjeto, al editor, a mis llamadas a la API, etc. contaminando toda la cadena de llamadas y objetos que de otro modo serían muy limpios y legibles. Me gustaría lidiar con esto solo en el nivel de Value, y dejar que JSONDecoder simplemente devuelva uno de los dos de alguna manera.

¿Hay otra forma de tratar las dos posibilidades del opcional valcomo una Stringo [String]sin cambiar todo el objeto principal?

Respuestas

4 gcharita Aug 19 2020 at 12:31

Puede lograrlo usando solo el [String]tipo e implementando manualmente la init(from:)función del Decodableprotocolo como este:

struct Value: Decodable {
    let key: String
    let val: [String]?
    
    enum CodingKeys: String, CodingKey {
        case key, val
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        key = try container.decode(String.self, forKey: .key)
        do {
            if let string = try container.decodeIfPresent(String.self, forKey: .val) {
                val = [string]
            } else {
                val = nil
            }
        } catch DecodingError.typeMismatch {
            val = try container.decodeIfPresent([String].self, forKey: .val)
        }
    }
}

Cuando la decodificación a Stringvalor tenga éxito, cree una matriz de cadenas con un solo elemento. Cuando la decodificación a Stringvalor falla, intente decodificar como[String]