diff --git a/MVMCore/MVMCore/ActionHandling/ActionActionsModel.swift b/MVMCore/MVMCore/ActionHandling/ActionActionsModel.swift index c7e00fc..b2c8233 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionActionsModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionActionsModel.swift @@ -64,4 +64,19 @@ public struct ActionActionsModel: ActionModelProtocol { try container.encodeIfPresent(extraParameters, forKey: .extraParameters) try container.encodeIfPresent(analyticsData, forKey: .analyticsData) } + + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return actions.isEqual(to: model.actions) + && concurrent == model.concurrent + && extraParameters == model.extraParameters + && analyticsData == model.analyticsData + } +} + +extension ActionActionsModel: CustomDebugStringConvertible { + + public var debugDescription: String { + return "\(Self.self) [\(actions)]" + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionBackModel.swift b/MVMCore/MVMCore/ActionHandling/ActionBackModel.swift index fea8eea..2ec606c 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionBackModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionBackModel.swift @@ -25,4 +25,12 @@ public struct ActionBackModel: ActionModelProtocol { self.extraParameters = extraParameters self.analyticsData = analyticsData } + + // Default + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return model.actionType == actionType + && model.extraParameters == extraParameters + && model.analyticsData == analyticsData + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionCallModel.swift b/MVMCore/MVMCore/ActionHandling/ActionCallModel.swift index dc6a32e..78c02f1 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionCallModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionCallModel.swift @@ -28,4 +28,11 @@ public struct ActionCallModel: ActionModelProtocol { self.extraParameters = extraParameters self.analyticsData = analyticsData } + + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return extraParameters == model.extraParameters + && analyticsData == model.analyticsData + && callNumber == model.callNumber + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionCancelModel.swift b/MVMCore/MVMCore/ActionHandling/ActionCancelModel.swift index 83ae013..b1a7c37 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionCancelModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionCancelModel.swift @@ -25,4 +25,12 @@ public struct ActionCancelModel: ActionModelProtocol { self.extraParameters = extraParameters self.analyticsData = analyticsData } + + // Default + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return model.actionType == actionType + && model.extraParameters == extraParameters + && model.analyticsData == analyticsData + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionContactModel.swift b/MVMCore/MVMCore/ActionHandling/ActionContactModel.swift index 9b975fc..12e918b 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionContactModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionContactModel.swift @@ -42,4 +42,14 @@ public struct ActionContactModel: ActionModelProtocol { self.extraParameters = extraParameters self.analyticsData = analyticsData } + + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return extraParameters == model.extraParameters + && analyticsData == model.analyticsData + && phoneNumber == model.phoneNumber + && firstName == model.firstName + && lastName == model.lastName + && approach == model.approach + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionNoopModel.swift b/MVMCore/MVMCore/ActionHandling/ActionNoopModel.swift index 941e80a..c0c075d 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionNoopModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionNoopModel.swift @@ -7,6 +7,7 @@ // public struct ActionNoopModel: ActionModelProtocol { + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -24,4 +25,12 @@ public struct ActionNoopModel: ActionModelProtocol { self.extraParameters = extraParameters self.analyticsData = analyticsData } + + // Default + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return model.actionType == actionType + && model.extraParameters == extraParameters + && model.analyticsData == analyticsData + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionOpenPageModel.swift b/MVMCore/MVMCore/ActionHandling/ActionOpenPageModel.swift index c8362a8..e899d8c 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionOpenPageModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionOpenPageModel.swift @@ -115,4 +115,31 @@ public struct ActionOpenPageModel: ActionModelProtocol, ActionOpenPageProtocol, try container.encodeIfPresent(analyticsData, forKey: .analyticsData) try container.encodeIfPresent(fallbackResponse, forKey: .fallbackResponse) } + + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return pageType == model.pageType + && baseURL == model.baseURL + && appContext == model.appContext + && requestURL == model.requestURL + && modules == model.modules + && presentationStyle == model.presentationStyle + && tabBarIndex == model.tabBarIndex + && background == model.background + && clientParameters == model.clientParameters + && customTimeoutTime == model.customTimeoutTime + && extraParameters == model.extraParameters + && analyticsData == model.analyticsData + && fallbackResponse == model.fallbackResponse + } +} + +extension ActionOpenPageModel: CustomDebugStringConvertible { + public var debugDescription: String { + if let requestURL { + return "\(Self.self) for \(pageType) @ \(requestURL)" + } else { + return "\(Self.self) for \(pageType)" + } + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionOpenSMSModel.swift b/MVMCore/MVMCore/ActionHandling/ActionOpenSMSModel.swift index 88f709d..372c942 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionOpenSMSModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionOpenSMSModel.swift @@ -30,4 +30,13 @@ public struct ActionOpenSMSModel: ActionModelProtocol { self.extraParameters = extraParameters self.analyticsData = analyticsData } + + // Default + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return model.extraParameters == extraParameters + && model.analyticsData == analyticsData + && model.phoneNumber == phoneNumber + && model.message == message + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionOpenUrlModel.swift b/MVMCore/MVMCore/ActionHandling/ActionOpenUrlModel.swift index c9f6f8a..57daae8 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionOpenUrlModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionOpenUrlModel.swift @@ -60,4 +60,13 @@ open class ActionOpenUrlModel: ActionModelProtocol { try container.encodeIfPresent(extraParameters, forKey: .extraParameters) try container.encodeIfPresent(analyticsData, forKey: .analyticsData) } + + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return extraParameters == model.extraParameters + && analyticsData == model.analyticsData + && browserUrl == model.browserUrl + && appURL == model.appURL + && appURLOptions == model.appURLOptions + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionPreviousSubmitModel.swift b/MVMCore/MVMCore/ActionHandling/ActionPreviousSubmitModel.swift index 31290dc..489aaf7 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionPreviousSubmitModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionPreviousSubmitModel.swift @@ -25,4 +25,12 @@ public struct ActionPreviousSubmitModel: ActionModelProtocol { self.extraParameters = extraParameters self.analyticsData = analyticsData } + + // Default + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return model.actionType == actionType + && model.extraParameters == extraParameters + && model.analyticsData == analyticsData + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionRestartModel.swift b/MVMCore/MVMCore/ActionHandling/ActionRestartModel.swift index e64a6d3..4cde79c 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionRestartModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionRestartModel.swift @@ -32,4 +32,12 @@ public struct ActionRestartModel: ActionModelProtocol { self.extraParameters = extraParameters self.analyticsData = analyticsData } + + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return extraParameters == model.extraParameters + && analyticsData == model.analyticsData + && requestURL == model.requestURL + && pageType == model.pageType + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionSettingModel.swift b/MVMCore/MVMCore/ActionHandling/ActionSettingModel.swift index b2fd687..0a87349 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionSettingModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionSettingModel.swift @@ -25,4 +25,10 @@ public struct ActionSettingModel: ActionModelProtocol { self.extraParameters = extraParameters self.analyticsData = analyticsData } + + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return extraParameters == model.extraParameters + && analyticsData == model.analyticsData + } } diff --git a/MVMCore/MVMCore/ActionHandling/ActionShareModel.swift b/MVMCore/MVMCore/ActionHandling/ActionShareModel.swift index ddeb7d8..813dd92 100644 --- a/MVMCore/MVMCore/ActionHandling/ActionShareModel.swift +++ b/MVMCore/MVMCore/ActionHandling/ActionShareModel.swift @@ -6,7 +6,7 @@ // Copyright © 2020 myverizon. All rights reserved. // -public struct ActionShareItemModel: Codable { +public struct ActionShareItemModel: Codable, Equatable { public enum SharedType: String, Codable { case text @@ -14,14 +14,14 @@ public struct ActionShareItemModel: Codable { } public var type: SharedType - public var value: Any + public var value: AnyHashable // Common Equatable type between String and URL. private enum CodingKeys: String, CodingKey { case type case value } - public init(type: SharedType, value: Any) { + public init(type: SharedType, value: AnyHashable) { self.type = type self.value = value } @@ -47,6 +47,11 @@ public struct ActionShareItemModel: Codable { try container.encode(value as! URL, forKey: .value) } } + + public static func == (lhs: ActionShareItemModel, rhs: ActionShareItemModel) -> Bool { + return lhs.type == rhs.type + && lhs.value == rhs.value + } } public struct ActionShareModel: ActionModelProtocol { @@ -101,7 +106,7 @@ public struct ActionShareModel: ActionModelProtocol { private init(deprecatedFrom decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: DeprecatedCodingKeys.self) let type = try typeContainer.decode(ActionShareItemModel.SharedType.self, forKey: .sharedType) - var value: Any + var value: AnyHashable switch type { case .url: value = try typeContainer.decode(URL.self, forKey: .sharedText) @@ -116,4 +121,11 @@ public struct ActionShareModel: ActionModelProtocol { try container.encode(actionType, forKey: .actionType) try container.encode(items, forKey: .items) } + + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return extraParameters == model.extraParameters + && analyticsData == model.analyticsData + && items == model.items + } } diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.swift b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.swift index ff2378e..8d20194 100644 --- a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.swift +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.swift @@ -85,9 +85,9 @@ public protocol MVMCoreJSONActionHandlerProtocol: MVMCoreActionHandlerProtocol { open func handleAction(with model: ActionModelProtocol, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) async throws { try Task.checkCancellation() var (additionalData, uuid) = MVMCoreActionHandler.setUUID(additionalData: additionalData) - MVMCoreActionHandler.log(string: "Begin Action: \(model.actionType)", additionalData: additionalData) + MVMCoreActionHandler.log(string: "Begin Action: \(model)", additionalData: additionalData) defer { - MVMCoreActionHandler.log(string: "End Action: \(model.actionType)", additionalData: additionalData) + MVMCoreActionHandler.log(string: "End Action: \(model)", additionalData: additionalData) } let json = try additionalData.removeValue(forKey: jsonKey) as? [AnyHashable : Any] ?? MVMCoreActionHandler.convertActionToJSON(model) @@ -148,7 +148,7 @@ public protocol MVMCoreJSONActionHandlerProtocol: MVMCoreActionHandlerProtocol { } static public func log(string: String, additionalData: [AnyHashable: Any]?) { - MVMCoreLoggingHandler.logDebugMessage(withDelegate: "ActionHandler: UUID: \(String(describing: getUUID(additionalData: additionalData))), \(string)") + MVMCoreLoggingHandler.shared()?.handleDebugMessage("ActionHandler: UUID: \(getUUID(additionalData: additionalData) ?? "untracked"), \(string)", category: String(describing: Self.self)) } fileprivate func logActionError(_ error: Error, _ actionType: String?, _ additionalData: [AnyHashable: Any]?, _ delegateObject: DelegateObject?) { diff --git a/MVMCore/MVMCore/ActionHandling/OpenURLOptionsModel.swift b/MVMCore/MVMCore/ActionHandling/OpenURLOptionsModel.swift index 09911c8..ec4e08a 100644 --- a/MVMCore/MVMCore/ActionHandling/OpenURLOptionsModel.swift +++ b/MVMCore/MVMCore/ActionHandling/OpenURLOptionsModel.swift @@ -9,7 +9,8 @@ import Foundation /// A model for UIApplication.OpenExternalURLOptionsKey -open class OpenUrlOptionsModel: Codable { +open class OpenUrlOptionsModel: Codable, Equatable { + public var options: [UIApplication.OpenExternalURLOptionsKey: Any] //-------------------------------------------------- @@ -42,4 +43,16 @@ open class OpenUrlOptionsModel: Codable { try container.encode(universalLinksValue, forKey: .universalLinksOnly) } } + + public static func == (lhs: OpenUrlOptionsModel, rhs: OpenUrlOptionsModel) -> Bool { + return lhs.options.allSatisfy { pair in + switch(pair.key) { + case .universalLinksOnly: + return rhs.options[pair.key] as? Bool == pair.value as? Bool + default: + return true + } + } + } + } diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.m b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.m index 3446798..a4baf05 100644 --- a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.m +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.m @@ -232,7 +232,7 @@ return; } NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - MVMCoreLog(@"Request Parameters for URL %@:\n%@", [url absoluteString], jsonString); + MVMCoreNetworkLog(@"Request Parameters for URL %@:\n%@", [url absoluteString], jsonString); #endif // Standard condensed to send to the server. @@ -309,15 +309,15 @@ return nil; } - MVMCoreLog(@"********************************* Cookie Sent *********************************"); + MVMCoreNetworkLog(@"********************************* Cookie Sent *********************************"); [[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:request.URL] enumerateObjectsUsingBlock:^(NSHTTPCookie * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { - MVMCoreLog(@"Cookie Name: %@, Cookie Value: %@, Domain: %@", obj.name, obj.value, obj.domain); + MVMCoreNetworkLog(@"Cookie Name: %@, Cookie Value: %@, Domain: %@", obj.name, obj.value, obj.domain); }]; NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate]; NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - MVMCoreLog(@"Request Time %f", [NSDate timeIntervalSinceReferenceDate] - startTime); + MVMCoreNetworkLog(@"Request Time %f", [NSDate timeIntervalSinceReferenceDate] - startTime); NSDate *startTimeDate = [NSDate dateWithTimeIntervalSinceReferenceDate:startTime]; @@ -330,7 +330,7 @@ [trackInfo setObject:error.localizedDescription forKey:@"error"]; } - MVMCoreLog(@"Set-Cookie %@ Value: %@", requestParameters.pageType, [(NSHTTPURLResponse *)response allHeaderFields][@"Set-Cookie"]); + MVMCoreNetworkLog(@"Set-Cookie %@ Value: %@", requestParameters.pageType, [(NSHTTPURLResponse *)response allHeaderFields][@"Set-Cookie"]); id jsonObject = nil; MVMCoreErrorObject *errorObject = nil; @@ -354,7 +354,7 @@ // Log the response pretty. NSData *prettyData = [NSJSONSerialization dataWithJSONObject:jsonObject options:NSJSONWritingPrettyPrinted error:&error]; NSString *responseString = [[NSString alloc] initWithData:prettyData encoding:NSUTF8StringEncoding]; - MVMCoreLog(@"Response for Request Page Type %@:\n%@",requestParameters.pageType, responseString); + MVMCoreNetworkLog(@"Response for Request Page Type %@:\n%@",requestParameters.pageType, responseString); } } else { // Empty response. diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.m b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.m index 699df7b..801f2d0 100644 --- a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.m +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.m @@ -86,7 +86,7 @@ // stop any loading animation we may have started [self stopLoadingAnimationIfNeeded]; - MVMCoreLog(@"Load Operation finished for page type %@, background load %@", self.requestParameters.pageType, @(self.backgroundLoad)); + MVMCoreNetworkLog(@"Load Operation finished for page type %@, background load %@", self.requestParameters.pageType, @(self.backgroundLoad)); [super markAsFinished]; } @@ -107,7 +107,7 @@ } - (void)main { - MVMCoreLog(@"Load Operation begun for page type %@, background load %@, delegate %@", self.requestParameters.pageType, @(self.backgroundLoad),self.delegateObject.loadDelegate); + MVMCoreNetworkLog(@"Load Operation begun for page type %@, background load %@, delegate %@", self.requestParameters.pageType, @(self.backgroundLoad),self.delegateObject.loadDelegate); [self.requestParameters resolveURL:[MVMCoreSessionObject sharedGlobal]]; @@ -139,10 +139,10 @@ // Log if loaded from cache. if (pageFromCache) { - MVMCoreLog(@"loaded from cache page %@",[MVMCoreActionUtility formatDictionaryAsJSONString:pageFromCache]); + MVMCoreNetworkLog(@"loaded from cache page %@",[MVMCoreActionUtility formatDictionaryAsJSONString:pageFromCache]); } if (modulesFromCache) { - MVMCoreLog(@"loaded from cache modules %@",[MVMCoreActionUtility formatDictionaryAsJSONString:modulesFromCache]); + MVMCoreNetworkLog(@"loaded from cache modules %@",[MVMCoreActionUtility formatDictionaryAsJSONString:modulesFromCache]); } // Create a load object from any data we fetched. @@ -822,7 +822,7 @@ return; } - MVMCoreLog(@"Error: %@ %@ %@ %@ %@",[error stringErrorCode], error.domain, error.location,error.messageToDisplay, error.messageToLog); + MVMCoreNetworkLog(@"Error: %@ %@ %@ %@ %@",[error stringErrorCode], error.domain, error.location,error.messageToDisplay, error.messageToLog); if (showAlertForErrorIfApplicable && (!loadObject.operation.backgroundLoad || loadObject.requestParameters.allowAlertsIfBackgroundRequest) && !loadObject.requestParameters.handleErrorsSilently && !error.silentError && !error.errorScreenError) { diff --git a/MVMCore/MVMCore/MainProtocols/MVMCoreLoggingDelegateProtocol.swift b/MVMCore/MVMCore/MainProtocols/MVMCoreLoggingDelegateProtocol.swift index eb6f223..b903a09 100644 --- a/MVMCore/MVMCore/MainProtocols/MVMCoreLoggingDelegateProtocol.swift +++ b/MVMCore/MVMCore/MainProtocols/MVMCoreLoggingDelegateProtocol.swift @@ -8,13 +8,16 @@ public protocol MVMCoreLoggingDelegateProtocol { - // Can be used to log different actions performed by the core. + /// Can be used to log different actions performed by the core. func handleDebugMessage(_ message: String?) + + /// Can be used to log a message under a particular cagetory. + func handleDebugMessage(_ message: String, category: String?) - // Can be used to choose how to log error objects. + /// Can be used to choose how to log error objects. func addError(toLog errorObject: MVMCoreErrorObject) - // Log that the load has finished. + /// Log that the load has finished. func logLoadFinished(_ loadObject: MVMCoreLoadObject?, loadedViewController: MVMCoreViewControllerProtocol?, error: MVMCoreErrorObject?) } diff --git a/MVMCore/MVMCore/Models/ActionType/ActionModelProtocol.swift b/MVMCore/MVMCore/Models/ActionType/ActionModelProtocol.swift index 028af05..3ff3173 100644 --- a/MVMCore/MVMCore/Models/ActionType/ActionModelProtocol.swift +++ b/MVMCore/MVMCore/Models/ActionType/ActionModelProtocol.swift @@ -7,7 +7,7 @@ // -public protocol ActionModelProtocol: ModelProtocol { +public protocol ActionModelProtocol: ModelProtocol, CustomDebugStringConvertible { var actionType: String { get } var extraParameters: JSONValueDictionary? { get set } @@ -33,4 +33,15 @@ public extension ActionModelProtocol { static var categoryName: String { return "\(ActionModelProtocol.self)" } + + var debugDescription: String { + return actionType + } + + func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return model.actionType == actionType + && model.extraParameters == extraParameters + && model.analyticsData == analyticsData + } } diff --git a/MVMCore/MVMCore/Models/ActionType/ActionRunJavaScriptModel.swift b/MVMCore/MVMCore/Models/ActionType/ActionRunJavaScriptModel.swift index d457793..f14ffa2 100644 --- a/MVMCore/MVMCore/Models/ActionType/ActionRunJavaScriptModel.swift +++ b/MVMCore/MVMCore/Models/ActionType/ActionRunJavaScriptModel.swift @@ -29,4 +29,11 @@ public class ActionRunJavaScriptModel: ActionModelProtocol { self.extraParameters = extraParameters self.analyticsData = analyticsData } + + public func isEqual(to model: any ModelComparisonProtocol) -> Bool { + guard let model = model as? Self else { return false } + return jsCallback == model.jsCallback + && extraParameters == model.extraParameters + && analyticsData == model.analyticsData + } } diff --git a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModel.swift b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModel.swift index 9e63c27..ec8cf40 100644 --- a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModel.swift +++ b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModel.swift @@ -8,7 +8,8 @@ import Foundation -public class ClientParameterModel: Codable { +public class ClientParameterModel: Equatable, Codable { + var timeout: Double? var list: [ClientParameterModelProtocol] @@ -33,4 +34,9 @@ public class ClientParameterModel: Codable { try container.encodeIfPresent(timeout, forKey: .timeout) try container.encodeModels(list, forKey: .list) } + + public static func == (lhs: ClientParameterModel, rhs: ClientParameterModel) -> Bool { + return lhs.list.isEqual(to: rhs.list) + && lhs.timeout == rhs.timeout + } } diff --git a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModelProtocol.swift b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModelProtocol.swift index bbde12c..8ba8950 100644 --- a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModelProtocol.swift +++ b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModelProtocol.swift @@ -30,4 +30,9 @@ public extension ClientParameterModelProtocol { static var categoryName: String { return "\(ClientParameterModelProtocol.self)" } + + func isEqual(to model: any ModelProtocol) -> Bool { + guard let model = model as? Self else { return false } + return type == model.type + } } diff --git a/MVMCore/MVMCore/Models/Model/ModelProtocol.swift b/MVMCore/MVMCore/Models/Model/ModelProtocol.swift index 960f593..8c85a64 100644 --- a/MVMCore/MVMCore/Models/Model/ModelProtocol.swift +++ b/MVMCore/MVMCore/Models/Model/ModelProtocol.swift @@ -8,7 +8,7 @@ import Foundation -public protocol ModelProtocol: Codable { +public protocol ModelProtocol: ModelComparisonProtocol, Codable { /// The key name of the molecule static var identifier: String { get } @@ -50,3 +50,62 @@ extension ModelProtocol { try unkeyedContainer.encode(self) } } + +public protocol ModelComparisonProtocol { + /// Shallow checks if the current model is equal to another model. Defaults to false unless implemented otherwise. + func isEqual(to model: ModelComparisonProtocol) -> Bool +} + +extension ModelComparisonProtocol { + public func isEqual(to model: ModelComparisonProtocol) -> Bool { + return false + } +} + +public extension Optional { + /// Checks if the current model is equal to another model. + func isEqual(to model: ModelComparisonProtocol?) -> Bool { + guard let self = self as? ModelComparisonProtocol else { + return model == nil + } + guard let model = model else { + return false + } + return self.isEqual(to: model) + } +} + +public extension Collection { + /// Checks if all the models in the given collection match another given collection. + func isEqual(to models: [ModelComparisonProtocol]) -> Bool { + guard count == models.count, let self = self as? [ModelComparisonProtocol] else { return false } + return models.indices.allSatisfy { index in + self[index].isEqual(to: models[index]) + } + } +} + +public extension Optional where Wrapped: Collection { + /// Checks if the current model is equal to another model. + func isEqual(to models: [ModelComparisonProtocol]?) -> Bool { + guard let self = self as? [ModelComparisonProtocol] else { + return models == nil + } + guard let models = models else { + return false + } + return self.isEqual(to: models) + } +} + +public extension Optional { + /// Checks if + func matchExistence(with anotherOptional: Optional) -> Bool { + switch(self) { + case .none: + return anotherOptional == nil + case .some(_): + return anotherOptional != nil + } + } +} diff --git a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler+Extension.swift b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler+Extension.swift index d74201e..e322f37 100644 --- a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler+Extension.swift +++ b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler+Extension.swift @@ -7,9 +7,31 @@ // import Foundation +import os -@objc public extension MVMCoreLoggingHandler { - @objc func print(with message: String) { - Swift.print(message) +public protocol CoreLogging { + static var loggingCategory: String? { get } + + var loggingPrefix: String { get } +} + +public extension CoreLogging { + + static var loggingCategory: String? { return nil } + + var loggingPrefix: String { + return "" + } + + static func debugLog(_ string: String) { + #if LOGGING + MVMCoreLoggingHandler.shared()?.handleDebugMessage(string, category: loggingCategory) + #endif + } + + func debugLog(_ string: String) { + #if LOGGING + MVMCoreLoggingHandler.shared()?.handleDebugMessage("\(loggingPrefix)\(string)", category: Self.loggingCategory) + #endif } } diff --git a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.swift b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.swift index 1732fd5..e562854 100644 --- a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.swift +++ b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.swift @@ -7,9 +7,28 @@ // import Foundation +import os @objc open class MVMCoreLoggingHandler: NSObject, MVMCoreLoggingDelegateProtocol { + public static let standardCategory = "General" + + private let logger = Logger(subsystem: "MVMCoreLogging", category: standardCategory) + private var loggerCache = [String: Logger]() + + open func getLogger(category: String?) -> Logger { + if let category { + if let logger = loggerCache[category] { + return logger + } else { + let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: category) + loggerCache[category] = logger + return logger + } + } + return logger + } + @objc(sharedLoggingHandler) public static func shared() -> Self? { return MVMCoreActionUtility.initializerClassCheck(MVMCoreObject.sharedInstance()?.loggingDelegate as? NSObject, classToVerify: self) as? Self @@ -29,8 +48,25 @@ import Foundation // MARK: - logging delegate @objc open func handleDebugMessage(_ message: String?) { #if LOGGING - guard let message = message else { return } - self.print(with: message) + guard let message = message else { return } + guard message.count < 1024 else { + logger.debug("\(message.prefix(300), privacy: .public)... ") // Send initial log to console. + print(message) // Print the whole on stdout. + return + } + logger.debug("\(message, privacy: .public)") // Assume that becaues this is a LOGGING build we want these messages to be unmasked. + // TODO: How do we split the messaging by Library and Subsystem? + #endif + } + + @objc open func handleDebugMessage(_ message: String, category: String?) { + #if LOGGING + guard message.count < 1024 else { + getLogger(category: category).debug("\(message.prefix(300), privacy: .public)... ") // Send initial log to console. + print(message) // Print the whole on stdout. + return + } + getLogger(category: category).debug("\(message, privacy: .public)") // Assume that becaues this is a LOGGING build we want these messages to be unmasked. #endif } diff --git a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandlerHelper.h b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandlerHelper.h index bb0fe9b..1ba0edf 100644 --- a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandlerHelper.h +++ b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandlerHelper.h @@ -8,7 +8,11 @@ #ifndef MVMCoreLoggingHandlerHelper_h #define MVMCoreLoggingHandlerHelper_h + #define MVMCoreLog(fmt, ...) \ [MVMCoreLoggingHandler logDebugMessageWithDelegate:[NSString stringWithFormat:(@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__]]; -#endif +#define MVMCoreNetworkLog(fmt, ...) \ +[MVMCoreLoggingHandler.sharedLoggingHandler handleDebugMessage:[NSString stringWithFormat:(@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__] category: @"Network"]; + +#endif