Swift Decodable Optional Key
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 } }}
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.