Last active
September 18, 2019 00:00
-
-
Save nicklockwood/3f99995a9273b9e3746ec1579276efd9 to your computer and use it in GitHub Desktop.
POC for serializing heterogeneous array of classes using Codable in Swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
enum FooType: String, Codable { | |
case bar, baz | |
} | |
protocol Foo: AnyObject, Codable { | |
var type: FooType { get } | |
} | |
extension Foo { | |
func encode(to container: inout UnkeyedEncodingContainer) throws { | |
try container.encode(self) | |
} | |
} | |
struct AnyFoo: Codable { | |
let value: Foo | |
init(_ value: Foo) { | |
self.value = value | |
} | |
init(from decoder: Decoder) throws { | |
var container = try decoder.unkeyedContainer() | |
switch try container.decode(FooType.self) { | |
case .bar: | |
self.init(try container.decode(Bar.self)) | |
case .baz: | |
self.init(try container.decode(Baz.self)) | |
} | |
} | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.unkeyedContainer() | |
try container.encode(value.type) | |
try value.encode(to: &container) | |
} | |
} | |
class Bar: Foo { | |
let type = FooType.bar | |
var bar: Int | |
init(bar: Int) { | |
self.bar = bar | |
} | |
private enum CodingKeys: CodingKey { | |
case bar | |
} | |
required init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CodingKeys.self) | |
bar = try container.decode(Int.self, forKey: .bar) | |
} | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.container(keyedBy: CodingKeys.self) | |
try container.encode(bar, forKey: .bar) | |
} | |
} | |
class Baz: Foo { | |
let type = FooType.baz | |
var baz: String | |
init(baz: String) { | |
self.baz = baz | |
} | |
private enum CodingKeys: CodingKey { | |
case baz | |
} | |
required init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CodingKeys.self) | |
baz = try container.decode(String.self, forKey: .baz) | |
} | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.container(keyedBy: CodingKeys.self) | |
try container.encode(baz, forKey: .baz) | |
} | |
} | |
let coder = JSONEncoder() | |
let decoder = JSONDecoder() | |
let values: [Foo] = [ | |
Bar(bar: 5), | |
Baz(baz: "Hello") | |
] | |
let typeErasedValues = values.map(AnyFoo.init) | |
let data = coder.encode(typeErasedValues) | |
let decodedErasedValues = decoder.decode([AnyFoo].self, from: data) | |
let decodedValues = decodedErasedValues.map { $0.value } | |
print((decodedValues[0] as! Bar).bar) // 5 | |
print((decodedValues[1] as! Baz).baz) // Hello |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For comparison, here is the struct version: https://gist.github.com/nicklockwood/833fabacbc4b2d11ae7c7d4752b8fd18