Updating registry for logging
This commit is contained in:
parent
856b55b48b
commit
6e8c464097
@ -10,93 +10,192 @@
|
||||
|
||||
public struct ModelRegistry {
|
||||
|
||||
/// Error object for the Model Registry.
|
||||
public enum Error: Swift.Error {
|
||||
case keyNotFound
|
||||
case encoderError
|
||||
case decoderError
|
||||
case decoderOther(message: String)
|
||||
case decoderErrorObjectNotPresent(codingKey: CodingKey, codingPath: [CodingKey])
|
||||
case decoderErrorObjectNotPresent(codingKey: CodingKey, codingPath: [CodingKey])
|
||||
case decoderErrorModelNotMapped(identifer: String? = nil, codingKey: CodingKey? = nil, codingPath: [CodingKey]? = nil)
|
||||
case duplicateRegistration(message: String)
|
||||
case other(message: String)
|
||||
case handlerNotMapped(identifer: String? = nil, categoryName: String? = nil)
|
||||
}
|
||||
|
||||
private struct Category {
|
||||
public struct Category {
|
||||
var name: String
|
||||
var codingKey: String
|
||||
var instanceTypes: [String: ModelProtocol.Type] = [:]
|
||||
var handlerTypes: [String: ModelHandlerProtocol.Type] = [:]
|
||||
}
|
||||
|
||||
private static var categories: [String: Category] = [:]
|
||||
|
||||
/// Registers models for Atomic use.
|
||||
public static func register<H: ModelHandlerProtocol, M: ModelProtocol>(handler: H.Type, for model: M.Type) throws {
|
||||
//register the type
|
||||
try self.register(model)
|
||||
public static var categories: [String: Category] = [:]
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Convenient Registration
|
||||
//--------------------------------------------------
|
||||
|
||||
/// A convenience wrapping function where error handling is managed within the class.
|
||||
/// - Parameters:
|
||||
/// - handler: The handling class taking an object of ModelHandlerProtocol.self which is used to register
|
||||
/// - model: The data object of ModelProtocol.self which is used to register
|
||||
public static func register<H: ModelHandlerProtocol, M: ModelProtocol>(handler: H.Type, for model: M.Type) {
|
||||
do {
|
||||
try throwable_register(handler: handler, model: model)
|
||||
} catch {
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience wrapping function where error handling is managed within the class.
|
||||
/// - Parameter type: Takes an object of ModelProtocol.self which is used to register
|
||||
public static func register<M: ModelProtocol>(_ type: M.Type) {
|
||||
do {
|
||||
try throwable_register(type: type)
|
||||
} catch {
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Error Handling
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Handles an error if thrown when registering a ModelProtocol object.
|
||||
/// - Parameter error: The error object produced by a thrown exception.
|
||||
public static func handleError(_ error: Swift.Error) {
|
||||
|
||||
//get the key for the handler
|
||||
guard let errorObject = MVMCoreErrorObject(title: nil,
|
||||
messageToLog: nil,
|
||||
code: (error as NSError).code,
|
||||
domain: ErrorDomainNative,
|
||||
location: #file) else { return }
|
||||
|
||||
switch error as? ModelRegistry.Error {
|
||||
case .keyNotFound:
|
||||
errorObject.title = "Key Not Found"
|
||||
case .encoderError:
|
||||
errorObject.title = "Encoder Error"
|
||||
case .decoderError:
|
||||
errorObject.title = "Decoder Error"
|
||||
case .decoderOther(let message):
|
||||
errorObject.title = "Decoder Error: Other"
|
||||
errorObject.messageToLog = message
|
||||
case .decoderErrorObjectNotPresent(let codingKey, let codingPath):
|
||||
errorObject.title = "Decoder Error: Object Not Present"
|
||||
let codingPathConcat = codingPath.compactMap { $0.stringValue }.joined(separator: " ")
|
||||
errorObject.messageToLog = codingKey.stringValue + codingPathConcat
|
||||
case .decoderErrorModelNotMapped(let identifer, let codingKey, let codingPath):
|
||||
errorObject.title = "Decoder Error: Model Not Mapped"
|
||||
let codingPathConcat = codingPath?.compactMap { $0.stringValue }.joined(separator: " ") ?? ""
|
||||
errorObject.messageToLog = (identifer ?? "") + (codingKey?.stringValue ?? "") + codingPathConcat
|
||||
case .handlerNotMapped(let identifer, let categoryName):
|
||||
errorObject.title = "Handler Not Mapped"
|
||||
errorObject.messageToLog = (identifer ?? "") + (categoryName ?? "")
|
||||
case .other(let message):
|
||||
errorObject.title = "Other Registry Error"
|
||||
errorObject.messageToLog = message
|
||||
case .duplicateRegistration(let message):
|
||||
errorObject.title = "Duplicate Registration"
|
||||
errorObject.messageToLog = message
|
||||
default:
|
||||
errorObject.title = "Unknown Model Registry Error"
|
||||
}
|
||||
|
||||
MVMCoreLoggingHandler.addError(toLog: errorObject)
|
||||
#if DEBUG
|
||||
triggerCrashInDebug()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private static func triggerCrashInDebug() {
|
||||
fatalError()
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Throwable Registration
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Registers models for Atomic use.
|
||||
/// - Parameters:
|
||||
/// - handler: The handling class taking an object of ModelHandlerProtocol.self which is used to register
|
||||
/// - model: The data object of ModelProtocol.self which is used to register
|
||||
/// - Throws: An Error object of `ModelRegistry.Error`
|
||||
public static func throwable_register<H: ModelHandlerProtocol, M: ModelProtocol>(handler: H.Type, model: M.Type) throws {
|
||||
// Register the type
|
||||
try self.throwable_register(type: model)
|
||||
|
||||
// Get the key for the handler
|
||||
let key = model.identifier
|
||||
|
||||
//get the category for the ModelProtocol
|
||||
// Get the category for the ModelProtocol
|
||||
var category = getCategory(for: model)
|
||||
|
||||
// Check to ensure the Category/Container combination doesn't exist.
|
||||
if category.handlerTypes[key] != nil {
|
||||
throw ModelRegistry.Error.other(message: "ModelHandlerProtocol: \(String(describing: handler)) already exists in Category: \(category.name)")
|
||||
throw ModelRegistry.Error.duplicateRegistration(message: "ModelHandlerProtocol: \(String(describing: handler)) already exists in Category: \(category.name)")
|
||||
} else {
|
||||
category.handlerTypes[key] = handler
|
||||
}
|
||||
|
||||
categories[category.name] = category
|
||||
}
|
||||
|
||||
/// Registers models for Atomic use.
|
||||
public static func register<M: ModelProtocol>(_ type: M.Type) throws {
|
||||
//get the category for the ModelProtocol
|
||||
/// - Parameter type: Takes an object of ModelProtocol.self which is used to register
|
||||
/// - Throws: An Error object of `ModelRegistry.Error`
|
||||
public static func throwable_register<M: ModelProtocol>(type: M.Type) throws {
|
||||
// Get the category for the ModelProtocol
|
||||
var category = getCategory(for: type)
|
||||
|
||||
// Check to ensure the Category/Type combination doesn't exist.
|
||||
if category.instanceTypes[M.identifier] != nil {
|
||||
throw ModelRegistry.Error.other(message: "ModelProtocol: \(M.identifier) already exists in Category: \(M.categoryName)")
|
||||
throw ModelRegistry.Error.duplicateRegistration(message: "ModelProtocol: \(M.identifier) already exists in Category: \(M.categoryName)")
|
||||
}
|
||||
|
||||
category.instanceTypes[M.identifier] = type
|
||||
categories[M.categoryName] = category
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Functions
|
||||
//--------------------------------------------------
|
||||
|
||||
public static func getHandler(_ model: ModelProtocol) throws -> ModelHandlerProtocol.Type {
|
||||
//get the modelType
|
||||
// Get the modelType
|
||||
let modelType = type(of: model)
|
||||
|
||||
//get the category for the ModelProtocol
|
||||
|
||||
// Get the category for the ModelProtocol
|
||||
guard let category = categories[modelType.categoryName] else {
|
||||
throw ModelRegistry.Error.other(message: "Category not found: \(modelType.categoryName)")
|
||||
}
|
||||
|
||||
//get the containerProtocol for this ModelProtocol you had registered
|
||||
|
||||
// Get the containerProtocol for this ModelProtocol you had registered
|
||||
guard let handlerType = category.handlerTypes[modelType.identifier] else {
|
||||
throw ModelRegistry.Error.handlerNotMapped(identifer: modelType.identifier, categoryName: category.name)
|
||||
}
|
||||
|
||||
return handlerType
|
||||
}
|
||||
|
||||
private static func getCategory<M: ModelProtocol>(for type: M.Type) -> Category {
|
||||
return categories[type.categoryName] ?? Category(name: type.categoryName, codingKey: type.categoryCodingKey)
|
||||
|
||||
public static func getCategory<M: ModelProtocol>(for type: M.Type) -> Category {
|
||||
categories[type.categoryName] ?? Category(name: type.categoryName, codingKey: type.categoryCodingKey)
|
||||
}
|
||||
|
||||
private static func getCategory<T>(for type: T.Type) -> Category? {
|
||||
|
||||
public static func getCategory<T>(for type: T.Type) -> Category? {
|
||||
// Temporary code till we find a better solution.
|
||||
//if this is a protocol composotion, loop through each protocol for a category lookup
|
||||
let protocols = String(describing: T.self).components(separatedBy: "&").compactMap{$0.trimmingCharacters(in: .whitespaces)}
|
||||
// if this is a protocol composotion, loop through each protocol for a category lookup
|
||||
let protocols = String(describing: T.self).components(separatedBy: "&").compactMap{ $0.trimmingCharacters(in: .whitespaces)}
|
||||
return protocols.compactMap({ (p) -> Category? in
|
||||
guard let c = categories[p] else {
|
||||
return nil
|
||||
}
|
||||
return c
|
||||
categories[p] ?? nil
|
||||
}).first
|
||||
}
|
||||
|
||||
public static func getType<T>(for name: String, with type: T.Type) -> ModelProtocol.Type? {
|
||||
return getCategory(for: type)?.instanceTypes[name]
|
||||
getCategory(for: type)?.instanceTypes[name]
|
||||
}
|
||||
|
||||
public static func getCodingKey<T>(for type: T.Type) throws -> AnyCodingKey {
|
||||
@ -108,12 +207,13 @@ public struct ModelRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
extension KeyedDecodingContainer where Key: CodingKey {
|
||||
|
||||
public extension KeyedDecodingContainer where Key: CodingKey {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Decode
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Decodes to a registered model based on the identifier
|
||||
public func decodeModel<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> T {
|
||||
func decodeModel<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> T {
|
||||
guard let model: T = try decodeModelIfPresent(codingKey: codingKey) else {
|
||||
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "ModelRegistry Error decoderErrorObjectNotPresent: \(codingKey.stringValue)")
|
||||
throw ModelRegistry.Error.decoderErrorObjectNotPresent(codingKey: codingKey, codingPath: codingPath)
|
||||
@ -122,7 +222,7 @@ extension KeyedDecodingContainer where Key: CodingKey {
|
||||
}
|
||||
|
||||
/// Decodes an array of registered model based on the identifiers.
|
||||
public func decodeModels<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> [T] {
|
||||
func decodeModels<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> [T] {
|
||||
guard let model: [T] = try decodeModelsIfPresent(codingKey: codingKey) else {
|
||||
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "ModelRegistry Error decoderErrorObjectNotPresent: \(codingKey.stringValue)")
|
||||
throw ModelRegistry.Error.decoderErrorObjectNotPresent(codingKey: codingKey, codingPath: codingPath)
|
||||
@ -131,7 +231,7 @@ extension KeyedDecodingContainer where Key: CodingKey {
|
||||
}
|
||||
|
||||
/// Decodes an array with arrays of models based on the identifiers.
|
||||
public func decodeModels2D<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> [[T]] {
|
||||
func decodeModels2D<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> [[T]] {
|
||||
guard let models: [[T]] = try decodeModels2DIfPresent(codingKey: codingKey) else {
|
||||
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "ModelRegistry Error decoderErrorObjectNotPresent: \(codingKey.stringValue)")
|
||||
throw ModelRegistry.Error.decoderErrorObjectNotPresent(codingKey: codingKey, codingPath: codingPath)
|
||||
@ -139,25 +239,27 @@ extension KeyedDecodingContainer where Key: CodingKey {
|
||||
return models
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - DecodeIfPresent
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Decodes to a registered model based on the identifier, optional.
|
||||
public func decodeModelIfPresent<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> T? {
|
||||
//create coding key
|
||||
func decodeModelIfPresent<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> T? {
|
||||
// Create coding key
|
||||
let typeCodingKey = try ModelRegistry.getCodingKey(for: T.self)
|
||||
|
||||
//get the container that holds the identifier value
|
||||
// Get the container that holds the identifier value
|
||||
guard contains(codingKey),
|
||||
let container = try? self.nestedContainer(keyedBy: AnyCodingKey.self, forKey: codingKey),
|
||||
let identifier = try container.decodeIfPresent(String.self, forKey: typeCodingKey) else { return nil }
|
||||
let container = try? self.nestedContainer(keyedBy: AnyCodingKey.self, forKey: codingKey),
|
||||
let identifier = try container.decodeIfPresent(String.self, forKey: typeCodingKey) else { return nil }
|
||||
|
||||
//get the type from the identifier value in the Registry
|
||||
// Get the type from the identifier value in the Registry
|
||||
guard let type = ModelRegistry.getType(for: identifier, with: T.self) else {
|
||||
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "ModelProtocol not mapped: \(identifier)")
|
||||
throw ModelRegistry.Error.decoderErrorModelNotMapped(identifer: identifier, codingKey: typeCodingKey, codingPath: container.codingPath)
|
||||
}
|
||||
|
||||
//decode the type using the decoder
|
||||
// Decode the type using the decoder
|
||||
let model = try type.decode(keyedContainer: self, codingKey: codingKey)
|
||||
|
||||
if let model = model as? T {
|
||||
@ -169,19 +271,19 @@ extension KeyedDecodingContainer where Key: CodingKey {
|
||||
}
|
||||
|
||||
/// Decodes an array of registered model based on the identifiers, optional.
|
||||
public func decodeModelsIfPresent<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> [T]? {
|
||||
func decodeModelsIfPresent<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> [T]? {
|
||||
guard contains(codingKey),
|
||||
var container = try? self.nestedUnkeyedContainer(forKey: codingKey)
|
||||
else { return nil }
|
||||
var container = try? self.nestedUnkeyedContainer(forKey: codingKey)
|
||||
else { return nil }
|
||||
|
||||
return try container.decodeModelsIfPresent()
|
||||
}
|
||||
|
||||
/// Decodes an array with arrays of models based on the identifiers, optional.
|
||||
public func decodeModels2DIfPresent<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> [[T]]? {
|
||||
func decodeModels2DIfPresent<T>(codingKey: KeyedDecodingContainer<K>.Key) throws -> [[T]]? {
|
||||
guard contains(codingKey),
|
||||
var container = try? nestedUnkeyedContainer(forKey: codingKey)
|
||||
else { return nil }
|
||||
var container = try? nestedUnkeyedContainer(forKey: codingKey)
|
||||
else { return nil }
|
||||
|
||||
return try container.decodeModels2DIfPresent()
|
||||
}
|
||||
@ -259,6 +361,7 @@ public extension UnkeyedDecodingContainer {
|
||||
throw ModelRegistry.Error.decoderError
|
||||
}
|
||||
}
|
||||
|
||||
return models
|
||||
}
|
||||
|
||||
@ -277,6 +380,7 @@ public extension UnkeyedDecodingContainer {
|
||||
}
|
||||
modelLists.append(models)
|
||||
}
|
||||
|
||||
return modelLists
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,22 +8,21 @@
|
||||
|
||||
@objcMembers open class ModelMapping: NSObject {
|
||||
|
||||
open class func registerObjects() {}
|
||||
open class func registerObjects() { }
|
||||
|
||||
open class func registerActions() {
|
||||
try? ModelRegistry.register(ActionOpenPageModel.self)
|
||||
try? ModelRegistry.register(ActionOpenUrlModel.self)
|
||||
try? ModelRegistry.register(ActionCallModel.self)
|
||||
try? ModelRegistry.register(ActionBackModel.self)
|
||||
try? ModelRegistry.register(ActionShareModel.self)
|
||||
try? ModelRegistry.register(ActionRestartModel.self)
|
||||
try? ModelRegistry.register(ActionPreviousSubmitModel.self)
|
||||
try? ModelRegistry.register(ActionCancelModel.self)
|
||||
try? ModelRegistry.register(ActionSettingModel.self)
|
||||
try? ModelRegistry.register(ActionNoopModel.self)
|
||||
try? ModelRegistry.register(ActionActionsModel.self)
|
||||
try? ModelRegistry.register(ActionOpenSMSModel.self)
|
||||
try? ModelRegistry.register(ActionContactModel.self)
|
||||
ModelRegistry.register(ActionOpenPageModel.self)
|
||||
ModelRegistry.register(ActionOpenUrlModel.self)
|
||||
ModelRegistry.register(ActionCallModel.self)
|
||||
ModelRegistry.register(ActionBackModel.self)
|
||||
ModelRegistry.register(ActionShareModel.self)
|
||||
ModelRegistry.register(ActionRestartModel.self)
|
||||
ModelRegistry.register(ActionPreviousSubmitModel.self)
|
||||
ModelRegistry.register(ActionCancelModel.self)
|
||||
ModelRegistry.register(ActionSettingModel.self)
|
||||
ModelRegistry.register(ActionNoopModel.self)
|
||||
ModelRegistry.register(ActionActionsModel.self)
|
||||
ModelRegistry.register(ActionOpenSMSModel.self)
|
||||
ModelRegistry.register(ActionContactModel.self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user