Skip to content

Instantly share code, notes, and snippets.

@saiday
Created April 30, 2020 10:31
Show Gist options
  • Save saiday/81a6f9bc2971032d3137453e2a33f345 to your computer and use it in GitHub Desktop.
Save saiday/81a6f9bc2971032d3137453e2a33f345 to your computer and use it in GitHub Desktop.
import Foundation
let json = """
{
"results":[
{
"blah":"blah",
"nested_object":{
"type":"a",
"id":69,
"aVar":"aaa"
}
},
{
"blah":"blah",
"nested_object":{
"type":"b",
"id":42,
"bVar":"bbb"
}
}
]
}
"""
protocol HeterogeneousClassFamily: Decodable {
associatedtype BaseType: Decodable
static var discriminator: Discriminator { get }
static func decodeHeterogeneousItem<CodingKey>(container: KeyedDecodingContainer<CodingKey>, key: CodingKey) throws -> BaseType
}
enum Discriminator: String, CodingKey {
case type
}
enum SuperClassFamily: String, HeterogeneousClassFamily {
typealias BaseType = Superclass
case a
case b
static var discriminator: Discriminator { return .type }
static func decodeHeterogeneousItem<CodingKey>(container: KeyedDecodingContainer<CodingKey>, key: CodingKey) throws -> Superclass {
let keyContainer = try container.nestedContainer(keyedBy: Discriminator.self, forKey: key)
let family = try keyContainer.decode(SuperClassFamily.self, forKey: SuperClassFamily.discriminator)
switch family {
case .a:
return try container.decode(SubclassA.self, forKey: key)
case .b:
return try container.decode(SubclassB.self, forKey: key)
}
}
}
class Superclass: Codable {
var id: Int
var type: String
}
class SubclassA: Superclass {
var aVar: String
enum CodingKeys: String, CodingKey {
case id
case type
case aVar
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.aVar = try container.decode(String.self, forKey: .aVar)
try super.init(from: decoder)
self.id = try container.decode(Int.self, forKey: .id)
self.type = try container.decode(String.self, forKey: .type)
}
}
class SubclassB: Superclass {
var bVar: String
enum CodingKeys: String, CodingKey {
case id
case type
case bVar
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.bVar = try container.decode(String.self, forKey: .bVar)
try super.init(from: decoder)
self.id = try container.decode(Int.self, forKey: .id)
self.type = try container.decode(String.self, forKey: .type)
}
}
class HeterogeneousContainerEntity: Codable {
enum CodingKeys: String, CodingKey {
case nestedObject = "nested_object"
}
var nestedObject: Superclass?
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
nestedObject = try SuperClassFamily.decodeHeterogeneousItem(container: container, key: .nestedObject)
}
}
struct Page<T: Codable>: Codable {
enum CodingKeys: String, CodingKey {
case results
}
var results: [T]
}
// execution
let decoder = JSONDecoder()
let decodedRoot = try decoder.decode(Page<HeterogeneousContainerEntity>.self, from: json.data(using: .utf8)!)
let isA = decodedRoot.results[0].nestedObject is SubclassA // true
let isB = decodedRoot.results[1].nestedObject is SubclassB // true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment