I have a service that returns an array of objects in this form:
{
result: [
{
objectId: "id",
type: "objectType",
content: { ... }
}, ...
]
}
The content depends on the object type. I've tried building up a custom decoder this way (ContentItem is a protocol for ClassA, B and so on):
struct ContentDataItem: Decodable {
var objectId: String?
var type: String?
var content: ContentItem?
private enum CodingKeys: String, CodingKey {
case objectId
case type
case content
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
objectId = try container.decode(String.self, forKey: .objectId)
type = try container.decode(String.self, forKey: .type)
if let type = type {
switch type {
case "typeA":
content = try container.decode(ClassA.self, forKey: .content)
case "typeB":
content = try container.decode(ClassB.self, forKey: .content)
...
}
}
}
}
This works, but I get a high cyclomatic complexity (I'm aiming for sub-10, but I have 14 different classes for content). So I've tried changing my approach, making ContentItem the superclass and changing the init into something like this:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
objectId = try container.decode(String.self, forKey: .objectId)
let type = try container.decode(String.self, forKey: .type)
let itemTypes: [String: ContentItem.Type] = [
"typeA": ClassA.self,
"typeB": ClassB.self,
...
]
guard let type = type, let contentItemType = itemTypes[type] else { return }
content = try container.decode(contentItemType, forKey: .content)
}
This reduces the cyclomatic complexity as I wanted, but it doesn't work anymore because the decode only returns objects of type ContentItem (the superclass), not the specific ClassA, ClassB that I want. Is there a way to make this approach work? What is wrong with it?
And more importantly, is there a more efficient way to parse this object?