Swift Decodable - come evitare il tipo generico?

Aug 19 2020

Sto recuperando un oggetto nidificato complesso dalla mia 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

Suppongo che il modo corretto per affrontare questo problema sia introdurre un tipo generico.

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

... ma anche così, valuespotrebbe essere un array misto, quindi non vedo come Vsarebbe utile dichiarare ciò che è.

Ma poi, ovviamente, il tipo generico si propaga lungo la gerarchia, DocumentDraftall'oggetto, all'editore, alle mie chiamate API, ecc. Inquinando l'intera catena di chiamate e oggetti altrimenti molto puliti e leggibili. Vorrei occuparmi di questo solo a livello di Value, e lasciare che JSONDecoder restituisca semplicemente uno dei due in qualche modo.

C'è un altro modo per trattare le due possibilità dell'opzionale valcome uno Stringo [String]senza cambiare l'intero oggetto genitore?

Risposte

4 gcharita Aug 19 2020 at 12:31

Puoi ottenerlo utilizzando solo il [String]tipo e implementando manualmente la init(from:)funzione del Decodableprotocollo in questo modo:

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)
        }
    }
}

Quando la decodifica in Stringvalore ha esito positivo, creare un array di stringhe con un solo elemento. Quando la decodifica in Stringvalore non riesce, prova a decodificare come[String]