Swift Decodable Optional Key

I had this issue and I found this solution, just in case is helpful to somebody else:

let ageContainer = try? values.nestedContainer(keyedBy: AgeKeys.self, forKey: .age)
self.age = try ageContainer?.decodeIfPresent(Int.self, forKey: .realAge)

If you have an optional container, using try? values.nestedContainer(keyedBy:forKey) you don't need to check if the container exist using contains(.


Can you try pasting your sample JSON into quicktype to see what types it infers? Based on your question, I pasted your samples and got:

struct UserInfo: Codable {
    let firstname: String
    let age: Age?
    let lastname: String
}

struct Age: Codable {
    let realage: Int?
}

Making UserInfo.age and Age.realage optionals works, if that's what you're trying to accomplish.


You can use the following KeyedDecodingContainer function:

func contains(_ key: KeyedDecodingContainer.Key) -> Bool

Returns a Bool value indicating whether the decoder contains a value associated with the given key. The value associated with the given key may be a null value as appropriate for the data format.

For instance, to check if the "age" key exists before requesting the corresponding nested container:

struct Person: Decodable {
    let firstName, lastName: String
    let age: Int?

    enum CodingKeys: String, CodingKey {
        case firstName = "firstname"
        case lastName = "lastname"
        case age
    }

    enum AgeKeys: String, CodingKey {
        case realAge = "realage"
        case fakeAge = "fakeage"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.firstName = try values.decode(String.self, forKey: .firstName)
        self.lastName = try values.decode(String.self, forKey: .lastName)

        if values.contains(.age) {
            let age = try values.nestedContainer(keyedBy: AgeKeys.self, forKey: .age)
            self.age = try age.decodeIfPresent(Int.self, forKey: .realAge)
        } else {
            self.age = nil
        }
    }
}