Search code examples
jsonswiftswift4codable

With JSONDecoder in Swift 4, can missing keys use a default value instead of having to be optional properties?


Swift 4 added the new Codable protocol. When I use JSONDecoder it seems to require all the non-optional properties of my Codable class to have keys in the JSON or it throws an error.

Making every property of my class optional seems like an unnecessary hassle since what I really want is to use the value in the json or a default value. (I don't want the property to be nil.)

Is there a way to do this?

class MyCodable: Codable {
    var name: String = "Default Appleseed"
}

func load(input: String) {
    do {
        if let data = input.data(using: .utf8) {
            let result = try JSONDecoder().decode(MyCodable.self, from: data)
            print("name: \(result.name)")
        }
    } catch  {
        print("error: \(error)")
        // `Error message: "Key not found when expecting non-optional type
        // String for coding key \"name\""`
    }
}

let goodInput = "{\"name\": \"Jonny Appleseed\" }"
let badInput = "{}"
load(input: goodInput) // works, `name` is Jonny Applessed
load(input: badInput) // breaks, `name` required since property is non-optional

Solution

  • Approach that I prefer is using so called DTOs - data transfer object. It is a struct, that conforms to Codable and represents the desired object.

    struct MyClassDTO: Codable {
        let items: [String]?
        let otherVar: Int?
    }
    

    Then you simply init the object that you want to use in the app with that DTO.

     class MyClass {
        let items: [String]
        var otherVar = 3
        init(_ dto: MyClassDTO) {
            items = dto.items ?? [String]()
            otherVar = dto.otherVar ?? 3
        }
    
        var dto: MyClassDTO {
            return MyClassDTO(items: items, otherVar: otherVar)
        }
    }
    

    This approach is also good since you can rename and change final object however you wish to. It is clear and requires less code than manual decoding. Moreover, with this approach you can separate networking layer from other app.