I have an enum something like:
enum Response: Codable {
case error(message: String)
case hello(username: String, version: String)
case list([String: [String: ListUser]]) // struct ListUser is Codable and defined elsewhere
// more cases but that doesn't matter
enum CodingKeys: String, CodingKey {
case error = "Error"
case hello = "Hello"
case list = "List"
}
// no custom init
}
The JSON I'm trying to decode would be something like:
{"Error": {"message": ""}}
{"Hello": {"username": "", "version": ""}}
{"List": {"room": {"user": {"version": ""}}}}
Note how the .list
case involves a JSON object that maps as a dictionary, not an struct/associated values.
The default synthesized initializer works fine for for .hello
and .error
, but if I try to decode .list
, I get an error like:
â–ż Swift.DecodingError.keyNotFound
â–ż keyNotFound: (2 elements)
- .0: ListCodingKeys(stringValue: "_0", intValue: nil)
â–ż .1: Swift.DecodingError.Context
- codingPath: 0 elements
- debugDescription: "No value associated with key ListCodingKeys(stringValue: \"_0\", intValue: nil) (\"_0\")."
- underlyingError: nil
I think it's because with just a single value in the enum's associated value, Swift tries to use position-based naming (not well documented, I could only find the evolution proposal). I think it's expecting a structure like this:
{"List": {"_0": /* my [String: [String: ListUser]} */}}
...which does not seem terribly intuitive to me. Is there a way I can get it to do the right thing, without having to involve a custom initializer which means an all-or-nothing "I have to initialize every case manually" situation? (In this simple example, it doesn't seem too bad, but in my real enum, that would be even more complicated.)