I have the following JSON returned by my backend:
[
{
"id":8,
"title":"Title 1",
"componentCategoryIcon":{
"id":34,
"name":"icon_name_1",
"type":"IMAGE"
}
},
{
"id":8,
"title":"Title 2",
"componentCategoryIcon":{
"id":35,
"name":"icon_name_2",
"type":"IMAGE"
}
},
{
"id":8,
"title":"Title 3",
"componentCategoryIcon": null
}
]
And the following Codable
structs:
struct ComponentCategory: Codable {
let id: Int
let title: String
let componentCategoryIcon: Image?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let id = try container.decode(Int.self, forKey: .id)
self.id = id
self.title = try container.decode(String.self, forKey: .title)
let savedComponentCategory = (decoder.userInfo[.savedComponentCategories] as? [ComponentCategory])?.first(where: { $0.id == id })
// (1)
if let nestedDecoder = try? container.superDecoder(forKey: .componentCategoryIcon) {
self.componentCategoryIcon = try Image(from: nestedDecoder, savedImage: savedComponentCategory?.componentCategoryIcon)
} else {
self.componentCategoryIcon = nil
}
}
}
struct Image: Codable {
let id: Int
let name: String
let type: String?
let path: String?
let downloaded: Bool
required init(from decoder: Decoder, savedImage: Image?) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.name = try container.decode(String.self, forKey: .name)
self.type = try container.decodeIfPresent(String.self, forKey: .type)
self.path = try container.decodeIfPresent(String.self, forKey: .path)
self.downloaded = try container.decodeIfPresent(Bool.self, forKey: .downloaded) ?? savedImage?.downloaded ?? false
}
Now I noted a behaviour different from documentation for superDecoder(forKey:)
func. Documentation says:
Throws
DecodingError.valueNotFound
ifself
has a null entry for the given key.
During third ComponentCategory
decoding (componentCategoryIcon
is null
), I expected to go to else branch of the (1) if-else
. Instead I obtain an apparently valid nestedContainer
and I have an exception in the Image
init.
Am I misunderstanding superDecoder(forKey:)
documentation? Or is it wrong?
The current implementation indeed does not throw an error if there is no value for the given key, and instead returns an empty container; the documentation does not match this implementation. It appears that the upcoming swift-foundation
package also maintains this behavior, so the documentation stands to get updated.
This is likely worth filing an issue for (whether for a bug fix, or a documentation fix).
In the meantime, you can work around this by checking for the explicit existence of the key you need, and use that as the condition for your if-statement.
This being said, like the comments above mention, it seems surprising you would try to use a superDecoder
for this. You may want to either add information to your question about why you need to do this, or open a new question asking for suggestions on what approach you might want to take instead.