1

I've read read many tutorials and searched for answers here on SO;

Decoding Nested JSON with Optional Values Swift 4

Swift 4 Not decoding JSON optional properly

Decoding optionals doesn't seem problematic using below code. Obviously I'm doing something wrong, but what is it!?

Part of my code:

struct ProductInfo: Codable {
    var name: String?
    let images: [Image]
    var ingredients: String?
    let origin: [Origin]
    let producer: Producer

    enum CodingKeys: String, CodingKey {
        case name = "Artikelbenamning"
        case images = "Bilder"
        case ingredients = "Ingrediensforteckning"
        case origin = "Tillverkningslander"
        case producer = "Varumarke"
    }

    init(from decoder: Decoder) throws {

        let values = try decoder.container(keyedBy: CodingKeys.self)

        name = try? values.decodeIfPresent(String.self, forKey: .name)!
        images = try values.decodeIfPresent([Image].self, forKey: .images)!
        ingredients = try? values.decodeIfPresent(String.self, forKey: .ingredients)!
        origin = try values.decodeIfPresent([Origin].self, forKey: .origin)!
        producer = try values.decodeIfPresent(Producer.self, forKey: .producer)!
    }
}

The app queries a products database. Following json is returned when a product doesn't exist. App crashes when trying to decode nil name ("Artikelbenamning in json) in init(from:).

{
    "GTIN": null,
    "TillverkarensArtikelnummer": null,
    "Artikelbenamning": null,
    "RegleratProduktnamn": null,
    "Forvaringsinstruktion": null,
    "Variabelmattsindikator": false,
    "Bruttovikt": null,
    "Bredd": null,
    "Djup": null,
    "Hojd": null,
    "Returemballage": false,
    "FarligtGodsKod": null,
    "FarligtGodsKlass": null,
    "FarligtGodsForpackningsgrupp": null,
    "GPCKod": null,
    "GiltigFROM": null,
    "Publiceringsdatum": null,
    "FakturerbarEnhet": false,
    "Slutdatum": null,
    "GiltighetsdatumPris": null,
    "Tillganglighetstidpunkt": null,
    "SistaTillganglighetstidpunkt": null,
    "SkapadDatum": null,
    "SenastAndradDatum": null,
    "Flampunkt": null,
    "KodBegransadMangd": null,
    "OfficiellTransportbenamning": null,
    "OspecificeradTransportbenamning": null,
    "TunnelrestriktionADR": null,
    "KlassificeringskodFarligtgods": null,
    "Transportkategori": null,
    "Konsumentartikel": false,
    "BestallningsbarForpackning": false,
    "RabattOlaglig": null,
    "Garantiloptid": 0,
    "Konsumentdatum": null,
    "Tjanst": false,
    "Sasongsindikator": null,
    "Engangskop": null,
    "AntalReturnerbaraEnheter": 0,
    "Staplingsriktning": null,
    "Staplingstyp": null,
    "MaxTransportTemperatur": 0.0,
    "MinTransportTemperatur": 0.0,
    "Anvandningsinstruktioner": null,
    "HallbarhetEfterOppning": 0,
    "Riskfras": null,
    "KodlistutgivareRiskfras": null,
    "Klassificeringssystem": null,
    "FarligtGodsBegransadMangd": null,
    "FarligtGodsOvrigInfo": null,
    "FarligtGodsSarbestammelser": null,
    "T3495_Artikelavisering": null,
    "T4032_TypAvUtgangsdatum": null,
    "T3742_ForstaLeveransdatum": null,
    "Undervarumarke": null,
    "Niva": null,
    "Produktbladslank": null,
    "KompletterandeProduktklass": null,
    "T4200_AllmänPubliceringstidpunkt": null,
    "T3848_TypAvTryckkanslighet": null,
    "Varningsetiketter": [],
    "Sasongskoder": [],
    "Produktklasser": [],
    "MaskinellMarkningar": [],
    "Bilder": [],
    "ReferenserTillAndraArtiklar": [],
    "MSRKritierier": [],
    "Kravspecifikationer": [],
    "Receptlinks": [],
    "Allergener": [],
    "Markningar": [],
    "Ingredienser": [],
    "Tillagningsinformation": [],
    "Tillverkningslander": [],
    "Naringsinfo": [],
    "Serveringsforslag": [],
    "Diettyper": [],
    "Tillagningsmetoder": [],
    "Farger": [],
    "VillkorForsaljning": [],
    "Varumarke": {
        "Varumarke": null,
        "AgareGLN": null,
        "AgareNamn": null,
        "Tillverkare": {
            "Namn": null,
            "EAN": null
        }
    },
    "Nettoinnehall": [],
    "Kontakter": [],
    "Faroangivelser": [],
    "Sakerhet": [],
    "Forpackningar": [],
    "Tillsatser": [],
    "Substanser": [],
    "Fangstzoner": [],
    "Marknadsbudskap": null,
    "KortMarknadsbudskap": null,
    "Komponenter": null
}

1 Answer 1

2

The API decodeIfPresent is pointless in this case because the key does exist.

An existing key with null value and a missing key are two different situations.

And never force unwrap in the init(from decoder method.

There are two options:

  1. Delete the entire init method then the optional struct members handle the null values.
  2. Use regular decode and ignore the error

    name = try? values.decode(String.self, forKey: .name)
    
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for your response. Sorry, newbie here, what exactly do you mean bt your option 1?
I mean what I wrote: Delete the init(from decoder method. The protocol extension provides a synthesized one.
Ok. I will try and get back to you.
Curious, can you tell me why it crashed?
Because try? returns an optional and if there is is no string (or one of the other types) and you force unwrap it, it crashes. Please read the third paragraph once more.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.