Skip to content

Instantly share code, notes, and snippets.

@nicklockwood
Last active March 17, 2021 17:57
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nicklockwood/833fabacbc4b2d11ae7c7d4752b8fd18 to your computer and use it in GitHub Desktop.
Save nicklockwood/833fabacbc4b2d11ae7c7d4752b8fd18 to your computer and use it in GitHub Desktop.
POC for serializing heterogeneous array of structs using Codable in Swift
import Foundation
enum FooType: String, Codable {
case bar, baz
}
protocol Foo: Codable {
var type: FooType { get }
}
extension Foo {
func encode(to container: inout SingleValueEncodingContainer) throws {
try container.encode(self)
}
}
struct AnyFoo: Codable {
let value: Foo
init(_ value: Foo) {
self.value = value
}
enum CodingKeys: CodingKey {
case type
}
init(from decoder: Decoder) throws {
var container = try decoder.container(keyedBy: CodingKeys.self)
switch try container.decode(FooType.self, forKey: .type) {
case .bar:
self.init(try Bar(from: decoder))
case .baz:
self.init(try Baz(from: decoder))
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try value.encode(to: &container)
}
}
struct Bar: Foo {
let type = FooType.bar
var bar: Int
}
struct Baz: Foo {
let type = FooType.baz
var baz: String
}
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
@nicklockwood
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment