import Foundation public class HolderManager { public enum Error: Swift.Error { case codingKeyNotFound case encoderError case decoderError case decoderErrorModelNotMapped } fileprivate typealias HoldableDecoder = (SingleValueDecodingContainer) throws -> Holdable fileprivate typealias HoldableEncoder = (Holdable, inout SingleValueEncodingContainer) throws -> Void fileprivate static var decoders: [String: HoldableDecoder] = [:] fileprivate static var encoders: [String: HoldableEncoder] = [:] public static func register(_ type: A.Type) { decoders[A.identifier] = { container in try container.decode(A.self) } encoders[A.identifier] = { name, container in guard let obj = name as? A else { throw HolderManager.Error.encoderError } try container.encode(obj) } } } private struct DynamicKey: CodingKey { var stringValue: String init?(stringValue: String) { self.stringValue = stringValue } var intValue: Int? { return nil } init?(intValue: Int) { return nil } } //MARK: - Holder Extension extension Holder { public init(from decoder: Decoder) throws { //get the CodingKey String name that tells the type of holdable guard let key = DynamicKey(stringValue: Self.codingKeyName) else { throw HolderManager.Error.decoderError } //get the type let typeContainer = try decoder.container(keyedBy: DynamicKey.self) let type = try typeContainer.decode(String.self, forKey: key) //get the container to decode let container = try decoder.singleValueContainer() //get the decoder for the type found guard let decoder = HolderManager.decoders[type] else { throw HolderManager.Error.decoderErrorModelNotMapped } //decode the type and ensure it is of HoldableType guard let obj = try decoder(container) as? HoldableType else { throw HolderManager.Error.decoderError } //instantiate Self self = Self(name: type, object: obj) } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() guard let encode = HolderManager.encoders[self.name], let holdable = self.object as? Holdable else{ throw HolderManager.Error.encoderError } try encode(holdable, &container) } }