Skip to content

Instantly share code, notes, and snippets.

@MaximKotliar
Created April 2, 2021 14:31
Show Gist options
  • Save MaximKotliar/5f10a1d87832364ef3b027e6e7a0a0d4 to your computer and use it in GitHub Desktop.
Save MaximKotliar/5f10a1d87832364ef3b027e6e7a0a0d4 to your computer and use it in GitHub Desktop.
import UIKit
import SwiftUI
protocol AnyNode: AnyCodableType {
var id: UUID { get }
}
protocol AnyNodeContent: AnyCodableType {}
extension AnyNodeContent {
static func register() {
Node<Self>.register()
}
}
struct Node<Content: AnyNodeContent>: AnyNode {
var id: UUID = UUID()
var content: Content
}
struct Project: Codable {
let nodes: [AnyNode]
enum CodingKeys: String, CodingKey {
case nodes
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
nodes = try container.decode([AnyCodable].self, forKey: .nodes).map { $0.value as! AnyNode }
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(nodes.map { $0.wrapInAnyCodable() }, forKey: .nodes)
}
}
protocol AnyCodableType: Codable {
static var codingIdentifier: String { get }
func wrapInAnyCodable() -> AnyCodable
}
extension AnyCodableType {
static var codingIdentifier: String { String(describing: self) }
func wrapInAnyCodable() -> AnyCodable { .init(self) }
}
struct AnyCodable: Codable {
let typeIdentifier: String
let value: Any
enum CodingKeys: String, CodingKey {
case type
case value
}
init<T: AnyCodableType>(_ value: T) {
self.typeIdentifier = T.codingIdentifier
self.value = value
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
typeIdentifier = try container.decode(String.self, forKey: .type)
guard let decode = AnyCodable.codingFunctionsStorage[typeIdentifier]?.decode else {
throw Error.unregisteredType
}
var valueContainer = try container.nestedUnkeyedContainer(forKey: .value)
value = try decode(&valueContainer)
}
func encode(to encoder: Encoder) throws {
guard let encode = AnyCodable.codingFunctionsStorage[typeIdentifier]?.encode else {
throw Error.unregisteredType
}
var container = encoder.container(keyedBy: CodingKeys.self)
var typeContainer = container.nestedUnkeyedContainer(forKey: .type)
var valueContainer = container.nestedUnkeyedContainer(forKey: .value)
try typeContainer.encode(typeIdentifier)
try encode(value, &valueContainer)
}
}
extension AnyCodable {
enum Error: Swift.Error {
case unregisteredType
}
static var codingFunctionsStorage: [String: CapturedCodingFunctions] = [:]
}
typealias CapturedCodingFunctions = (encode: (Any, inout UnkeyedEncodingContainer) throws -> Void,
decode: (inout UnkeyedDecodingContainer) throws -> Any)
extension AnyCodableType {
static func register() {
AnyCodable.codingFunctionsStorage[codingIdentifier] = (encode: { try $1.encode($0 as! Self) },
decode: { try $0.decode(Self.self) })
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment