This commit is contained in:
Suresh, Kamlesh 2019-12-18 12:14:19 -05:00
commit 04dcde96f7
2 changed files with 90 additions and 95 deletions

View File

@ -12,33 +12,33 @@ public protocol Model: Codable {
static var identifier: String { get } static var identifier: String { get }
static func mvmdecode<K:CodingKey>(keyedContainer: KeyedDecodingContainer<K>, codingKey: K) throws -> Self? /// Convenience function to decode to model using a keyed container.
static func mvmdecode(unkeyedContainer: inout UnkeyedDecodingContainer) throws -> Self? static func decode<K:CodingKey>(keyedContainer: KeyedDecodingContainer<K>, codingKey: K) throws -> Self?
/// Convenience function to decode to model using an unkeyed container.
static func decode(unkeyedContainer: inout UnkeyedDecodingContainer) throws -> Self?
/// Convenience function to encode model using a keyed container.
func encode<K:CodingKey>(keyedContainer: inout KeyedEncodingContainer<K>, codingKey: K) throws
/// Convenience function to encode model using an unkeyed container.
func encode(unkeyedContainer: inout UnkeyedEncodingContainer) throws
} }
extension Model { extension Model {
static public func mvmdecode<K:CodingKey>(keyedContainer: KeyedDecodingContainer<K>, codingKey: K) throws -> Self? { static public func decode<K:CodingKey>(keyedContainer: KeyedDecodingContainer<K>, codingKey: K) throws -> Self? {
let m = try keyedContainer.decodeIfPresent(self, forKey: codingKey) let m = try keyedContainer.decodeIfPresent(self, forKey: codingKey)
return m return m
} }
static public func mvmdecode(unkeyedContainer: inout UnkeyedDecodingContainer) throws -> Self? { static public func decode(unkeyedContainer: inout UnkeyedDecodingContainer) throws -> Self? {
let m = try unkeyedContainer.decodeIfPresent(self) let m = try unkeyedContainer.decodeIfPresent(self)
return m return m
} }
static public func mvmencode<K:CodingKey>(keyedContainer: inout KeyedEncodingContainer<K>, codingKey: K, model: Model) throws { public func encode<K:CodingKey>(keyedContainer: inout KeyedEncodingContainer<K>, codingKey: K) throws {
guard let typedModel = model as? Self else { try keyedContainer.encode(self, forKey: codingKey)
throw ModelRegistry.Error.encoderError
}
try keyedContainer.encode(typedModel, forKey: codingKey)
} }
static public func mvmencode(unkeyedContainer: inout UnkeyedEncodingContainer, model: Model) throws{ public func encode(unkeyedContainer: inout UnkeyedEncodingContainer) throws{
guard let typedModel = model as? Self else { try unkeyedContainer.encode(self)
throw ModelRegistry.Error.encoderError
}
try unkeyedContainer.encode(typedModel)
} }
} }

View File

@ -1,3 +1,4 @@
// //
// ModelRegistry.swift // ModelRegistry.swift
// MVMCore // MVMCore
@ -16,9 +17,10 @@ public struct ModelRegistry {
case decoderErrorObjectNotPresent case decoderErrorObjectNotPresent
case decoderErrorModelNotMapped case decoderErrorModelNotMapped
} }
fileprivate static var types = [String: Model.Type]() fileprivate static var types = [String: Model.Type]()
/// Register the model.
public static func register<M: Model>(_ type: M.Type) { public static func register<M: Model>(_ type: M.Type) {
types[M.identifier] = type types[M.identifier] = type
} }
@ -31,119 +33,112 @@ public struct ModelRegistry {
extension KeyedDecodingContainer where Key: CodingKey { extension KeyedDecodingContainer where Key: CodingKey {
//MARK: - Decode //MARK: - Decode
public func decode<T, C:CodingKey>(codingKey: KeyedDecodingContainer<K>.Key, typeCodingKey: C) throws -> T { /// Decodes to a registered model based on the identifier
guard let model: Model = try decodeIfPresent(codingKey: codingKey, typeCodingKey: typeCodingKey) else { public func decodeModel<T, C: CodingKey>(codingKey: KeyedDecodingContainer<K>.Key, typeCodingKey: C) throws -> T {
guard let model: Model = try decodeModelIfPresent(codingKey: codingKey, typeCodingKey: typeCodingKey) else {
throw ModelRegistry.Error.decoderErrorObjectNotPresent throw ModelRegistry.Error.decoderErrorObjectNotPresent
} }
guard let m = model as? T else { guard let m = model as? T else {
throw ModelRegistry.Error.decoderError throw ModelRegistry.Error.decoderError
} }
return m return m
} }
//MARK: - DecodeIfPresent //MARK: - DecodeIfPresent
public func decodeIfPresent<T, C:CodingKey>(codingKey: KeyedDecodingContainer<K>.Key, typeCodingKey: C) throws -> T? { /// Decodes to a registered model based on the identifier, optional.
public func decodeModelIfPresent<T, C: CodingKey>(codingKey: KeyedDecodingContainer<K>.Key, typeCodingKey: C) throws -> T? {
//get the identifier string
var container: KeyedDecodingContainer<C>?
do { do {
let meta = try self.nestedContainer(keyedBy: C.self, forKey: codingKey) container = try nestedContainer(keyedBy: C.self, forKey: codingKey)
guard let type = try meta.decodeIfPresent(String.self, forKey: typeCodingKey) else {
return nil
}
//get the type
guard let found = ModelRegistry.types[type] else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "Model not mapped: \(type)")
throw ModelRegistry.Error.decoderErrorModelNotMapped
}
//decode the type using the decoder
let model = try found.mvmdecode(keyedContainer: self, codingKey: codingKey)
guard let m = model as? T else {
throw ModelRegistry.Error.decoderError
}
return m
} catch { } catch {
return nil return nil
} }
guard let identifier = try container?.decodeIfPresent(String.self, forKey: typeCodingKey) else {
return nil
}
//get the type
guard let type = ModelRegistry.types[identifier] else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "Model not mapped: \(identifier)")
throw ModelRegistry.Error.decoderErrorModelNotMapped
}
//decode the type using the decoder
let model = try type.decode(keyedContainer: self, codingKey: codingKey)
guard let m = model as? T else {
throw ModelRegistry.Error.decoderError
}
return m
} }
public func decodeArrayIfPresent<C:CodingKey>(codingKey: KeyedDecodingContainer<K>.Key, typeCodingKey: C) throws -> [Model]? {
var unkeyedContainer: UnkeyedDecodingContainer?
do {
unkeyedContainer = try nestedUnkeyedContainer(forKey: codingKey)
} catch {
return nil
}
var otherUnkeyedContainer = try nestedUnkeyedContainer(forKey: codingKey)
var attributes = [Model.Type]()
while !unkeyedContainer!.isAtEnd {
let meta = try unkeyedContainer!.nestedContainer(keyedBy: C.self) /// Decodes an array of registered model based on the identifiers, optional.
guard let type = try meta.decodeIfPresent(String.self, forKey: typeCodingKey) else { public func decodeModelsIfPresent<C: CodingKey>(codingKey: KeyedDecodingContainer<K>.Key, typeCodingKey: C) throws -> [Model]? {
return nil
}
//get the type
guard let found = ModelRegistry.types[type] else {
throw ModelRegistry.Error.decoderErrorModelNotMapped
}
attributes.append(found)
}
var i = 0
var models = [Model]() var models = [Model]()
while !otherUnkeyedContainer.isAtEnd { var container = try nestedUnkeyedContainer(forKey: codingKey)
let foundModel = attributes[i] var containerCopy = container
if let model = try foundModel.mvmdecode(unkeyedContainer: &otherUnkeyedContainer) {
models.append(model) var i = 0
let count = container.count ?? 0
while !container.isAtEnd {
if i < count {
let nestedContainer = try container.nestedContainer(keyedBy: C.self)
if let identifier = try nestedContainer.decodeIfPresent(String.self, forKey: typeCodingKey) {
//get the type
guard let type = ModelRegistry.types[identifier] else {
throw ModelRegistry.Error.decoderErrorModelNotMapped
}
//now get the decoder to use for the type
let decoder = try containerCopy.superDecoder()
let model = try type.init(from: decoder)
models.append(model)
}
i+=1
} }
i += 1
} }
return models return models.count > 0 ? models : nil
} }
public func decodeArray<C:CodingKey>(codingKey: KeyedDecodingContainer<K>.Key, typeCodingKey: C) throws -> [Model] { /// Decodes an array of registered model based on the identifiers.
guard let models: [Model] = try decodeArrayIfPresent(codingKey: codingKey, typeCodingKey: typeCodingKey) else { public func decodeModels<C: CodingKey>(codingKey: KeyedDecodingContainer<K>.Key, typeCodingKey: C) throws -> [Model] {
guard let models: [Model] = try decodeModelsIfPresent(codingKey: codingKey, typeCodingKey: typeCodingKey) else {
throw ModelRegistry.Error.decoderErrorObjectNotPresent throw ModelRegistry.Error.decoderErrorObjectNotPresent
} }
return models return models
} }
} }
extension KeyedEncodingContainer where Key: CodingKey { extension KeyedEncodingContainer where Key: CodingKey {
public mutating func encodeIfPresent(_ value: Model?, forKey key: KeyedEncodingContainer<K>.Key) throws { /// Encodes a model, optional.
public mutating func encodeModelIfPresent(_ value: Model?, forKey key: KeyedEncodingContainer<K>.Key) throws {
if let v = value { if let v = value {
let encoder = self.superEncoder(forKey: key) let encoder = self.superEncoder(forKey: key)
try v.encode(to: encoder) try v.encode(to: encoder)
} }
} }
public mutating func encode(_ value: Model, forKey key: KeyedEncodingContainer<K>.Key) throws { /// Encodes a model.
public mutating func encodeModel(_ value: Model, forKey key: KeyedEncodingContainer<K>.Key) throws {
let encoder = self.superEncoder(forKey: key) let encoder = self.superEncoder(forKey: key)
try value.encode(to: encoder) try value.encode(to: encoder)
} }
///need instance type as input paramaeter list /// Encodes an array of models
public mutating func encodeArray(_ list:[Model], forKey key:KeyedEncodingContainer<K>.Key) throws { public mutating func encodeModels(_ list: [Model], forKey key:KeyedEncodingContainer<K>.Key) throws {
try encodeModelsIfPresent(list, forKey: key)
}
/// Encodes an array of models, optional, need instance type as input paramaeter list
public mutating func encodeModelsIfPresent(_ list: [Model]?, forKey key:KeyedEncodingContainer<K>.Key) throws {
guard let list = list else { return }
var unkeyedContainer = self.nestedUnkeyedContainer(forKey: key) var unkeyedContainer = self.nestedUnkeyedContainer(forKey: key)
for model in list { for model in list {
let typeString = type(of: model).identifier try model.encode(unkeyedContainer: &unkeyedContainer)
let type = ModelRegistry.getType(for: typeString)
try type?.mvmencode(unkeyedContainer: &unkeyedContainer, model: model)
}
}
public mutating func encodeArrayIfPresent(_ list:[Model]?, forKey key:KeyedEncodingContainer<K>.Key) throws {
guard let models = list else { return }
do {
try self.encodeArray(models, forKey: key)
} catch {
return
} }
} }
} }