I have defined a enum like this:
enum ClubLevel: Int, Codable {
case golden = 1, silver, bronze
}
in my struct I have an optional property of type ClubLevel
and when I decode this property in init(from decoder: Decoder)
:
self.clubLevel = try container.decode(ClubLevel?.self, forKey: .clubLevel)
I face this error:
debugDescription: "Cannot initialize ClubLevel from invalid Int value 0", underlyingError: nil"
I'm wondering even this property is optional, decoder won't continue
any idea?
The line
self.clubLevel = try container.decode(ClubLevel?.self, forKey: .clubLevel)
doesn't try to decode ClubLevel
, assigning nil
if unsuccessful. What it does is:
nil
(represented in JSON as null
) for the clubLevel
key. If unsuccessful,ClubLevel
for the clubLevel
key. If unsuccessful,So if the value for the clubLevel
key is neither nil
nor a valid ClubLevel
representation, you'll get an error thrown. You'll note that this also means you'll get an error thrown if the clubLevel
key is missing entirely (rather than being present with a value of nil
).
Ignoring missing keys is done with decodeIfPresent
:
self.clubLevel = try container.decodeIfPresent(ClubLevel.self, forKey: .clubLevel)
This will now:
nil
if the clubLevel
key is missing from the container. If they key is present,nil
(represented in JSON as null
) for the clubLevel
key. If unsuccessful,ClubLevel
for the clubLevel
key. If unsuccessful,This is the default behaviour for decoding optionals in a compiler-generated implementation of init(from:)
. It will still throw an error in your case as the value for the clubLevel
key is not a valid ClubLevel
.
If you want to just try and decode a ClubLevel
, assigning nil
on the decoding failing for any reason (key missing, invalid value, etc.), then you want to use try?
:
self.clubLevel = try? container.decode(ClubLevel.self, forKey: .clubLevel)
I came across the same problem and thought I'd add my solution for anyone interested.
The idea is to wrap the enum inside the following struct
:
struct OptionalDecodableEnum<T>: Decodable where T: RawRepresentable, T.RawValue: Decodable {
let value: T?
init(from decoder: Decoder) throws {
value = T(rawValue: try decoder.singleValueContainer().decode(T.RawValue.self))
}
}
The main benefit is that you don't have to implement Decodable
every time you want an optional enum. You also don't need the optional chaining with extra parentheses.
However, you will have to return the inner value
property when you want to use it. E.g.
struct Foo: Decodable {
enum ClubLevel: Int, Codable {
case golden = 1, silver, bronze
}
let clubLevel: OptionalDecodableEnum<ClubLevel>
}
let foo = try jsonDecoder.decode(Foo.self, from: data)
print(String(describing: foo.clubLevel.value))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With