mvm_core_ui/MVMCoreUI/Models/HolderManager.swift
Pfeil, Scott Robert 347ebbed8c typo fixes
2019-11-07 10:05:17 -05:00

80 lines
2.5 KiB
Swift

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<A: Holdable>(_ 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[name],
let holdable = object as? Holdable else {
throw HolderManager.Error.encoderError
}
try encode(holdable, &container)
}
}