diff --git a/MVMCore/MVMCore.xcodeproj/project.pbxproj b/MVMCore/MVMCore.xcodeproj/project.pbxproj index fc17f77..12d7ab4 100644 --- a/MVMCore/MVMCore.xcodeproj/project.pbxproj +++ b/MVMCore/MVMCore.xcodeproj/project.pbxproj @@ -21,6 +21,11 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 016FF6EE259A4E6300F5E4AA /* ClientParameterModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016FF6ED259A4E6300F5E4AA /* ClientParameterModelProtocol.swift */; }; + 016FF6F2259A4FCC00F5E4AA /* ClientParameterModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016FF6F1259A4FCC00F5E4AA /* ClientParameterModel.swift */; }; + 016FF6F6259B9AED00F5E4AA /* ClientParameterRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016FF6F5259B9AED00F5E4AA /* ClientParameterRegistry.swift */; }; + 016FF6FC259BA27E00F5E4AA /* ClientParameterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016FF6FB259BA27E00F5E4AA /* ClientParameterProtocol.swift */; }; + 01934FE725A4FFC2003DCD67 /* ClientParameterActionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01934FE625A4FFC2003DCD67 /* ClientParameterActionProtocol.swift */; }; 01C851CF23CF7B260021F976 /* JSONMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C851CE23CF7B260021F976 /* JSONMap.swift */; }; 01C851D123CF97FE0021F976 /* ActionBackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C851D023CF97FE0021F976 /* ActionBackModel.swift */; }; 01DF561421F90ADC00CC099B /* Dictionary+MFConvenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DF561321F90ADC00CC099B /* Dictionary+MFConvenience.swift */; }; @@ -135,6 +140,9 @@ AFFCFA671FCCC0D700FD0730 /* MVMCoreLoadingOverlayHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AFFCFA631FCCC0D600FD0730 /* MVMCoreLoadingOverlayHandler.m */; }; AFFCFA681FCCC0D700FD0730 /* MVMCoreLoadingViewControllerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFFCFA641FCCC0D600FD0730 /* MVMCoreLoadingViewControllerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; BB780ADF250F8C890030BD2F /* ActionNoopModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB780ADE250F8C890030BD2F /* ActionNoopModel.swift */; }; + D27073B725BB45C4001C7246 /* ActionActionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27073B625BB45C4001C7246 /* ActionActionsModel.swift */; }; + D27073CD25BB4CEF001C7246 /* MVMCoreActionHandler+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27073CC25BB4CEF001C7246 /* MVMCoreActionHandler+Extension.swift */; }; + D27073D125BB844B001C7246 /* MVMCoreActionDelegateProtocol+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27073D025BB844B001C7246 /* MVMCoreActionDelegateProtocol+Extension.swift */; }; D282AAB62240085300C46919 /* MVMCoreGetterUtility+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB52240085300C46919 /* MVMCoreGetterUtility+Extension.swift */; }; D282AAB82240342D00C46919 /* NSNumber+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB72240342D00C46919 /* NSNumber+Extension.swift */; }; D2DEDCB723C63F3B00C44CC4 /* Clamping.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2DEDCB623C63F3B00C44CC4 /* Clamping.swift */; }; @@ -144,6 +152,11 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 016FF6ED259A4E6300F5E4AA /* ClientParameterModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientParameterModelProtocol.swift; sourceTree = ""; }; + 016FF6F1259A4FCC00F5E4AA /* ClientParameterModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientParameterModel.swift; sourceTree = ""; }; + 016FF6F5259B9AED00F5E4AA /* ClientParameterRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientParameterRegistry.swift; sourceTree = ""; }; + 016FF6FB259BA27E00F5E4AA /* ClientParameterProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientParameterProtocol.swift; sourceTree = ""; }; + 01934FE625A4FFC2003DCD67 /* ClientParameterActionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientParameterActionProtocol.swift; sourceTree = ""; }; 01C851CE23CF7B260021F976 /* JSONMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONMap.swift; sourceTree = ""; }; 01C851D023CF97FE0021F976 /* ActionBackModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBackModel.swift; sourceTree = ""; }; 01DF561321F90ADC00CC099B /* Dictionary+MFConvenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+MFConvenience.swift"; sourceTree = ""; }; @@ -266,6 +279,9 @@ AFFCFA631FCCC0D600FD0730 /* MVMCoreLoadingOverlayHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreLoadingOverlayHandler.m; sourceTree = ""; }; AFFCFA641FCCC0D600FD0730 /* MVMCoreLoadingViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadingViewControllerProtocol.h; sourceTree = ""; }; BB780ADE250F8C890030BD2F /* ActionNoopModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionNoopModel.swift; sourceTree = ""; }; + D27073B625BB45C4001C7246 /* ActionActionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionActionsModel.swift; sourceTree = ""; }; + D27073CC25BB4CEF001C7246 /* MVMCoreActionHandler+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreActionHandler+Extension.swift"; sourceTree = ""; }; + D27073D025BB844B001C7246 /* MVMCoreActionDelegateProtocol+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreActionDelegateProtocol+Extension.swift"; sourceTree = ""; }; D282AAB52240085300C46919 /* MVMCoreGetterUtility+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreGetterUtility+Extension.swift"; sourceTree = ""; }; D282AAB72240342D00C46919 /* NSNumber+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNumber+Extension.swift"; sourceTree = ""; }; D2DEDCB623C63F3B00C44CC4 /* Clamping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clamping.swift; sourceTree = ""; }; @@ -286,6 +302,18 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 016FF6EC259A4E3E00F5E4AA /* Client Parameters */ = { + isa = PBXGroup; + children = ( + 016FF6FB259BA27E00F5E4AA /* ClientParameterProtocol.swift */, + 016FF6ED259A4E6300F5E4AA /* ClientParameterModelProtocol.swift */, + 016FF6F1259A4FCC00F5E4AA /* ClientParameterModel.swift */, + 016FF6F5259B9AED00F5E4AA /* ClientParameterRegistry.swift */, + 01934FE625A4FFC2003DCD67 /* ClientParameterActionProtocol.swift */, + ); + path = "Client Parameters"; + sourceTree = ""; + }; 8876D5BF1FB50A9E00EB2E3D = { isa = PBXGroup; children = ( @@ -405,6 +433,7 @@ 946EE1B6237B66630036751F /* ActionType */ = { isa = PBXGroup; children = ( + 016FF6EC259A4E3E00F5E4AA /* Client Parameters */, 01F2A03523A80A7300D954D8 /* ActionModelProtocol.swift */, 946EE1BB237B691A0036751F /* ActionOpenPageModel.swift */, 01F2A03823A812DD00D954D8 /* ActionOpenUrlModel.swift */, @@ -416,6 +445,7 @@ 94C014D424211AF0005811A9 /* ActionCancelModel.swift */, 94C014D824212360005811A9 /* ActionSettingModel.swift */, BB780ADE250F8C890030BD2F /* ActionNoopModel.swift */, + D27073B625BB45C4001C7246 /* ActionActionsModel.swift */, ); path = ActionType; sourceTree = ""; @@ -558,8 +588,10 @@ isa = PBXGroup; children = ( AFBB96B51FBA3CEC0008D868 /* MVMCoreActionDelegateProtocol.h */, + D27073D025BB844B001C7246 /* MVMCoreActionDelegateProtocol+Extension.swift */, AFBB96B61FBA3CEC0008D868 /* MVMCoreActionHandler.h */, AFBB96B71FBA3CEC0008D868 /* MVMCoreActionHandler.m */, + D27073CC25BB4CEF001C7246 /* MVMCoreActionHandler+Extension.swift */, ); path = ActionHandling; sourceTree = ""; @@ -813,6 +845,7 @@ D2DEDCB923C6400600C44CC4 /* UnitInterval.swift in Sources */, 94C014D3242119E6005811A9 /* ActionPreviousSubmitModel.swift in Sources */, 8876D5E91FB50AB000EB2E3D /* NSArray+MFConvenience.m in Sources */, + D27073B725BB45C4001C7246 /* ActionActionsModel.swift in Sources */, 946EE1B2237B5F260036751F /* JSONValue.swift in Sources */, AFBB96971FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.m in Sources */, AF43A5781FBA5B7C008E9347 /* MVMCoreJSONConstants.m in Sources */, @@ -820,16 +853,19 @@ AFED77A31FCCA29400BAE689 /* MVMCoreViewControllerNibMappingObject.m in Sources */, 8876D5EB1FB50AB000EB2E3D /* NSDecimalNumber+MFConvenience.m in Sources */, 01F2A03923A812DD00D954D8 /* ActionOpenUrlModel.swift in Sources */, + 01934FE725A4FFC2003DCD67 /* ClientParameterActionProtocol.swift in Sources */, AFBB96351FBA34310008D868 /* MVMCoreErrorConstants.m in Sources */, AF43A5881FBB67D6008E9347 /* MVMCoreActionUtility.m in Sources */, AFED77A61FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.m in Sources */, AF43A57C1FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.m in Sources */, 0AFF597A23FC6E60005C24E8 /* ActionShareModel.swift in Sources */, AFEEE81F1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.m in Sources */, + D27073CD25BB4CEF001C7246 /* MVMCoreActionHandler+Extension.swift in Sources */, 01F2A05223A8325100D954D8 /* ModelMapping.swift in Sources */, 8876D5F51FB50AB000EB2E3D /* UILabel+MFCustom.m in Sources */, AFBB96B31FBA3B590008D868 /* MVMCoreGetterUtility.m in Sources */, AF43A7071FC4D7A2008E9347 /* MVMCoreObject.m in Sources */, + 016FF6F6259B9AED00F5E4AA /* ClientParameterRegistry.swift in Sources */, 94C014D924212360005811A9 /* ActionSettingModel.swift in Sources */, D2DEDCB723C63F3B00C44CC4 /* Clamping.swift in Sources */, 01DF561421F90ADC00CC099B /* Dictionary+MFConvenience.swift in Sources */, @@ -839,16 +875,20 @@ AF43A70A1FC4F415008E9347 /* MVMCoreCache.m in Sources */, AF43A6FF1FBE3252008E9347 /* Reachability.m in Sources */, 01C851D123CF97FE0021F976 /* ActionBackModel.swift in Sources */, + D27073D125BB844B001C7246 /* MVMCoreActionDelegateProtocol+Extension.swift in Sources */, AFBB96921FBA3A9A0008D868 /* MVMCoreNavigationOperation.m in Sources */, AFBB96611FBA3A570008D868 /* MVMCoreLoadObject.m in Sources */, 946EE1B4237B619D0036751F /* Encoder.swift in Sources */, AFBB96941FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.m in Sources */, 94C014D524211AF0005811A9 /* ActionCancelModel.swift in Sources */, AF43A5841FBB66DE008E9347 /* MVMCoreConstants.m in Sources */, + 016FF6F2259A4FCC00F5E4AA /* ClientParameterModel.swift in Sources */, D2DEDCBB23C65BC300C44CC4 /* Percent.swift in Sources */, AFBB966A1FBA3A570008D868 /* MVMCoreLoadRequestOperation.m in Sources */, + 016FF6FC259BA27E00F5E4AA /* ClientParameterProtocol.swift in Sources */, 8876D5F31FB50AB000EB2E3D /* UIFont+MFSpacing.m in Sources */, D282AAB82240342D00C46919 /* NSNumber+Extension.swift in Sources */, + 016FF6EE259A4E6300F5E4AA /* ClientParameterModelProtocol.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionDelegateProtocol+Extension.swift b/MVMCore/MVMCore/ActionHandling/MVMCoreActionDelegateProtocol+Extension.swift new file mode 100644 index 0000000..1308ee5 --- /dev/null +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionDelegateProtocol+Extension.swift @@ -0,0 +1,16 @@ +// +// MVMCoreActionDelegateProtocolExtension.swift +// MVMCore +// +// Created by Scott Pfeil on 1/22/21. +// Copyright © 2021 myverizon. All rights reserved. +// + +import Foundation + +public extension MVMCoreActionDelegateProtocol { + /// Handles any action errors. + func handleAction(error: MVMCoreErrorObject, action: ActionModelProtocol, additionalData: [AnyHashable: Any]?) { + MVMCoreActionHandler.shared()?.defaultHandleActionError(error, additionalData: additionalData) + } +} diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler+Extension.swift b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler+Extension.swift new file mode 100644 index 0000000..5acdbca --- /dev/null +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler+Extension.swift @@ -0,0 +1,40 @@ +// +// MVMCoreActionHandler+Extension.swift +// MVMCore +// +// Created by Scott Pfeil on 1/22/21. +// Copyright © 2021 myverizon. All rights reserved. +// + +import Foundation + +public extension MVMCoreActionHandler { + + /// Converts the action to json for old action handler to handle. + func convertActionToJSON(_ model: ActionModelProtocol, delegateObject: DelegateObject?) -> [AnyHashable: Any]? { + do { + let data = try model.encode() + guard let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [AnyHashable: Any] else { + throw ModelRegistry.Error.decoderError + } + return json + } catch { + if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "") { + delegateObject?.actionDelegate?.handleAction(error: errorObject, action: model, additionalData: model.extraParameters) ?? MVMCoreActionHandler.shared()?.defaultHandleActionError(errorObject, additionalData: model.extraParameters) + } + return nil + } + } + + /// Start action on current thread. + func syncHandleAction(with model: ActionModelProtocol, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { + guard let json = convertActionToJSON(model, delegateObject: delegateObject) else { return } + synchronouslyHandleAction(model.actionType, actionInformation: json, additionalData: additionalData, delegateObject: delegateObject) + } + + /// Start action on dispatched background thread. + func asyncHandleAction(with model: ActionModelProtocol, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { + guard let json = convertActionToJSON(model, delegateObject: delegateObject) else { return } + handleAction(model.actionType, actionInformation: json, additionalData: additionalData, delegateObject: delegateObject) + } +} diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h index c677e53..2f41e93 100644 --- a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h @@ -19,143 +19,95 @@ extern NSString * _Nonnull const KeyActionTypeOpen; @interface MVMCoreActionHandler : NSObject -// Returns the shared action handler +/// Returns the shared action handler + (nullable instancetype)sharedActionHandler; -// Convenience function for handling actions. This will pull action and pageInfo out of the dictionary and call handleAction: actionInformation: with those values +/// Convenience function for handling actions. This will pull action and pageInfo out of the dictionary and call handleAction: actionInformation: with those values - (void)handleActionWithDictionary:(nullable NSDictionary *)dictionary additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Handles actions. Used by server driven user actions.. +/// Asynchronously handles action (dispatches and calls below function). Used by server driven user actions.. - (void)handleAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; +/// Synchronously handles action. Used by server driven user actions.. +- (void)synchronouslyHandleAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; + +/// Iterates through the clientParameters list. Gets values from the individual handlers and attaches the parameters to extraParameters. +- (void)setClientParameter:(nullable NSDictionary *)actionInformation completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler; + #pragma mark - Actions -// by default, returns the original RequestParameter that passed in. Can be overriden for some generic updates to the RequestParameter before handle open page action gets called. +/// by default, returns the original RequestParameter that passed in. Can be overriden for some generic updates to the RequestParameter before handle open page action gets called. - (void)updateRequestParametersBeforeHandleOpenPageAction:(nonnull MVMCoreRequestParameters *)requestParameters callBack:(void (^_Nonnull)(MVMCoreRequestParameters * _Nonnull requestParameters))callback; -// Logs the action. Currently is not action information driven... depends on delegate. +/// Logs the action. Currently is not action information driven... depends on delegate. - (void)logAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Tries to open a page +/// Tries to open a page - (void)openPageAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// restarts the app +/// restarts the app - (void)restartAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Goes back +/// Goes back - (void)backAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Makes a phone call +/// Makes a phone call - (void)callAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Makes the previous request, needs the delegate for this +/// Makes the previous request, needs the delegate for this - (void)previousSubmitAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Redirects to another experience +/// Redirects to another experience - (void)redirectAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Cancels (like in a popup) +/// Cancels (like in a popup) - (void)cancelAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Goes to settings app +/// Goes to settings app - (void)settingsAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Subclass this to handle other custom actions. Return YES if handled, and NO if not. +/// Performs multiple actions +- (void)actions:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; + +/// Subclass this to handle other custom actions. Return YES if handled, and NO if not. - (BOOL)handleOtherActions:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Last chance to handle unknown actions before throwing an error +/// Last chance to handle unknown actions before throwing an error - (void)unknownAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Handles action errors. +/// Handles action errors. - (void)handleActionError:(nullable MVMCoreErrorObject *)error actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; #pragma mark - Link away action -// Links away to app or browser +/// Links away to app or browser - (void)linkAwayAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Can subclass to add to urls if needed at global level (delegate is also called) +/// Can subclass to add to urls if needed at global level (delegate is also called) - (void)prepareLinkAwayWithURL:(nullable NSURL *)url appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Opens the url +/// Opens the url - (void)openURL:(nullable NSURL *)url appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// opens the url in a webview. +/// opens the url in a webview. - (void)openURLInWebView:(nullable NSURL *)url actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; #pragma mark - Default Action Protocol Functions -// Currently no default log action but this will eventually be server driven. +/// Currently no default log action but this will eventually be server driven. + (void)defaultLogAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Sends the request to the load handler. +/// Sends the request to the load handler. + (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// By default, throws an error, calling defaultHandleActionError. +/// By default, throws an error, calling defaultHandleActionError. + (void)defaultHandleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; -// Logs the error. +/// Logs the error. - (void)defaultHandleActionError:(nonnull MVMCoreErrorObject *)error additionalData:(nullable NSDictionary *)additionalData; #pragma mark - Deprecated -// Convenience function for handling actions. This will pull action and pageInfo out of the dictionary and call handleAction: actionInformation: with those values +/// Convenience function for handling actions. This will pull action and pageInfo out of the dictionary and call handleAction: actionInformation: with those values - (void)handleActionWithDictionary:(nullable NSDictionary *)dictionary additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; -// Handles actions. Used by server driven user actions.. -- (void)handleAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Logs the action. Currently is not action information driven... depends on delegate. -- (void)logAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Tries to open a page -- (void)openPageAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// restarts the app -- (void)restartAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Goes back -- (void)backAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Makes a phone call -- (void)callAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Makes the previous request, needs the delegate for this -- (void)previousSubmitAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Redirects to another experience -- (void)redirectAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Cancels (like in a popup) -- (void)cancelAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Goes to settings app -- (void)settingsAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Subclass this to handle other custom actions. Return YES if handled, and NO if not. -- (BOOL)handleOtherActions:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Last chance to handle unknown actions before throwing an error -- (void)unknownAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Handles action errors. -- (void)handleActionError:(nullable MVMCoreErrorObject *)error actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Links away to app or browser -- (void)linkAwayAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Can subclass to add to urls if needed at global level (delegate is also called) -- (void)prepareLinkAwayWithURL:(nullable NSURL *)url appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Opens the url -- (void)openURL:(nullable NSURL *)url appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// opens the url in a webview. -- (void)openURLInWebView:(nullable NSURL *)url actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - -// Sends the request to the load handler. -+ (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject*)delegate __deprecated; - -// By default, throws an error, calling defaultHandleActionError. -+ (void)defaultHandleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate __deprecated; - @end diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m index ee539d0..f4c389d 100644 --- a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m @@ -26,6 +26,7 @@ #import "MVMCorePresentationDelegateProtocol.h" #import #import +#import "MVMCoreLoadingOverlayHandler.h" NSString * const KeyActionType = @"actionType"; NSString * const KeyActionTypeLinkAway = @"openURL"; @@ -44,50 +45,102 @@ NSString * const KeyActionTypeOpen = @"openPage"; } - (void)handleAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - - // Logs the action. - [self logAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - - if ([actionType isEqualToString:KeyActionTypeOpen]) { - [self openPageAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - - } else if ([actionType isEqualToString:KeyActionTypeLinkAway]) { - [self linkAwayAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - - } else if ([actionType isEqualToString:KeyActionTypeRestart]) { - [self restartAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - - } else if ([actionType isEqualToString:KeyActionTypeBack]) { - [self backAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - - } else if ([actionType isEqualToString:KeyActionTypeCall]) { - [self callAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - - } else if ([actionType isEqualToString:KeyActionTypeShare]) { - [self shareAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - - } else if ([actionType isEqualToString:KeyActionTypePreviousSubmit]) { - [self previousSubmitAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - - } else if ([actionType isEqualToString:KeyActionTypeRedirect]) { - [self redirectAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - - } else if ([actionType isEqualToString:KeyActionTypeCancel]) { - [self cancelAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - - } else if ([actionType isEqualToString:KeyActionTypeSettings]) { - [self settingsAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; - - } else if ([actionType isEqualToString:KeyActionTypeNoop]){ - } else if (![self handleOtherActions:actionType actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]) { - // not a known action type. - [self unknownAction:actionType actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]; - } + [self synchronouslyHandleAction:actionType actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]; }); } +- (void)synchronouslyHandleAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { + // Logs the action. + [self logAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + if ([actionType isEqualToString:KeyActionTypeOpen]) { + [self openPageAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + } else if ([actionType isEqualToString:KeyActionTypeLinkAway]) { + [self linkAwayAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + } else if ([actionType isEqualToString:KeyActionTypeRestart]) { + [self restartAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + } else if ([actionType isEqualToString:KeyActionTypeBack]) { + [self backAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + } else if ([actionType isEqualToString:KeyActionTypeCall]) { + [self callAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + } else if ([actionType isEqualToString:KeyActionTypeShare]) { + [self shareAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + } else if ([actionType isEqualToString:KeyActionTypePreviousSubmit]) { + [self previousSubmitAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + } else if ([actionType isEqualToString:KeyActionTypeRedirect]) { + [self redirectAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + } else if ([actionType isEqualToString:KeyActionTypeCancel]) { + [self cancelAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + } else if ([actionType isEqualToString:KeyActionTypeSettings]) { + [self settingsAction:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + } else if ([actionType isEqualToString:KeyActionTypeNoop]) { + } else if ([actionType isEqualToString:KeyActionTypeActions]) { + [self actions:actionInformation additionalData:additionalData delegateObject:delegateObject]; + } else if (![self handleOtherActions:actionType actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]) { + // not a known action type. + [self unknownAction:actionType actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]; + } +} + +- (void)setClientParameter:(nullable NSDictionary *)actionInformation completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler { + + NSDictionary *clientParametersMap = [actionInformation dict:KeyClientParameters]; + if (!clientParametersMap) { + completionHandler(actionInformation); + return; + } + + BOOL isBackgroudRequest = [actionInformation boolForKey:@"background"]; + + if (!isBackgroudRequest) { + [[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] startLoading]; + } + + void (^stopLoadingOverlay)(void) = ^(void) { + if (!isBackgroudRequest) { + [[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:true]; + } + }; + + NSError *error = nil; + [MVMCoreLoggingHandler logDebugMessageWithDelegate:@"Fetching client parameters"]; + [[[MVMCoreObject sharedInstance] clientParameterRegistry] getParametersWith:clientParametersMap + error:&error + completionHandler:^(NSDictionary * _Nullable clientParameters) { + [MVMCoreLoggingHandler logDebugMessageWithDelegate:@"Finshed fetching client parameters"]; + if (clientParameters) { + NSMutableDictionary *actionWithClientParameters = [actionInformation mutableCopy]; + NSMutableDictionary *extraParameters = [clientParameters mutableCopy]; + [extraParameters addEntriesFromDictionary:[actionWithClientParameters dictionaryForKey:KeyExtraParameters]]; + actionWithClientParameters[KeyExtraParameters] = extraParameters; + + stopLoadingOverlay(); + completionHandler(actionWithClientParameters); + } else { + [MVMCoreLoggingHandler logDebugMessageWithDelegate:@"No client parameters"]; + stopLoadingOverlay(); + completionHandler(actionInformation); + } + }]; + + if (error) { + stopLoadingOverlay(); + completionHandler(actionInformation); + [MVMCoreLoggingHandler addErrorToLog:[MVMCoreErrorObject createErrorObjectForNSError:error location:@"MVMCoreActionHandler->setClientParameter"]]; + } +} + #pragma mark - Actions - (void)updateRequestParametersBeforeHandleOpenPageAction:(nonnull MVMCoreRequestParameters *)requestParameters callBack:(void (^_Nonnull)(MVMCoreRequestParameters * _Nonnull requestParameters))callback { @@ -107,22 +160,28 @@ NSString * const KeyActionTypeOpen = @"openPage"; // Loads the given page type. NSString *pageType = [actionInformation stringForKey:KeyPageType]; - if (pageType.length > 0) { - MVMCoreRequestParameters *requestParameters = [[MVMCoreRequestParameters alloc] initWithActionMap:actionInformation]; - - [self updateRequestParametersBeforeHandleOpenPageAction:requestParameters callBack:^(MVMCoreRequestParameters * _Nonnull requestParameters) { + + if (pageType.length == 0) { + // No page type to load, show error. + MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeNoPageType domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@_%@",NSStringFromClass([delegateObject.actionDelegate class]),KeyActionTypeOpen]]; + [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]; + return; + } + + __weak typeof(self) weakSelf = self; + void (^performAction)(NSDictionary*) = ^(NSDictionary* actionMap) { + MVMCoreRequestParameters *requestParameters = [[MVMCoreRequestParameters alloc] initWithActionMap:actionMap]; + + [weakSelf updateRequestParametersBeforeHandleOpenPageAction:requestParameters callBack:^(MVMCoreRequestParameters * _Nonnull requestParameters) { if ([delegateObject.actionDelegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) { [delegateObject.actionDelegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:additionalData]; } else { [MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters additionalData:additionalData delegateObject:delegateObject]; } }]; - } else { - - // No page type to load, show error. - MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeNoPageType domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@_%@",NSStringFromClass([delegateObject.actionDelegate class]),KeyActionTypeOpen]]; - [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]; - } + }; + + [self setClientParameter:actionInformation completionHandler:performAction]; } - (void)shareAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { @@ -241,6 +300,19 @@ NSString * const KeyActionTypeOpen = @"openPage"; [MVMCoreActionUtility linkAway:UIApplicationOpenSettingsURLString appURLString:nil]; } +- (void)actions:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { + NSArray *actions = [actionInformation array:@"actions"]; + BOOL concurrent = [actionInformation boolForKey:@"concurrent"]; + for (NSDictionary *action in actions) { + NSString *actionType = [action string:KeyActionType]; + if (concurrent) { + [self handleAction:actionType actionInformation:action additionalData:additionalData delegateObject:delegateObject]; + } else { + [self synchronouslyHandleAction:actionType actionInformation:action additionalData:additionalData delegateObject:delegateObject]; + } + } +} + - (BOOL)handleOtherActions:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { return NO; } @@ -266,23 +338,28 @@ NSString * const KeyActionTypeOpen = @"openPage"; #pragma mark - open url functions - (void)linkAwayAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - - // Gets the app url - NSURL *appURL = nil; - NSString *appURLString = [actionInformation string:KeyLinkAwayAppURL]; - if (appURLString.length > 0) { - appURL = [NSURL URLWithString:appURLString]; - } - - // Gets the browser url - NSURL *otherURL = nil; - NSString *otherURLString = [actionInformation string:KeyLinkAwayURL]; - if (otherURLString.length > 0) { - otherURL = [NSURL URLWithString:otherURLString]; - } - - // Provide the URL and App URL to be modified if needed by a subclass or delegate. - [self prepareLinkAwayWithURL:otherURL appURL:appURL actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]; + + __weak typeof(self) weakSelf = self; + void (^performAction)(NSDictionary*) = ^(NSDictionary* actionMap) { + // Gets the app url + NSURL *appURL = nil; + NSString *appURLString = [actionMap string:KeyLinkAwayAppURL]; + if (appURLString.length > 0) { + appURL = [NSURL URLWithString:appURLString]; + } + + // Gets the browser url + NSURL *otherURL = nil; + NSString *otherURLString = [actionMap string:KeyLinkAwayURL]; + if (otherURLString.length > 0) { + otherURL = [NSURL URLWithString:otherURLString]; + } + + // Provide the URL and App URL to be modified if needed by a subclass or delegate. + [weakSelf prepareLinkAwayWithURL:otherURL appURL:appURL actionInformation:actionMap additionalData:additionalData delegateObject:delegateObject]; + }; + + [self setClientParameter:actionInformation completionHandler:performAction]; } - (void)prepareLinkAwayWithURL:(nullable NSURL *)url appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { @@ -363,248 +440,11 @@ NSString * const KeyActionTypeOpen = @"openPage"; - (void)handleActionWithDictionary:(nullable NSDictionary *)dictionary additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - NSString *action = [dictionary stringForKey:KeyActionType]; - [self handleAction:action actionInformation:dictionary additionalData:additionalData delegate:delegate]; -} - -- (void)handleAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - - // Logs the action. - [self logAction:actionInformation additionalData:additionalData delegate:delegate]; - - if ([actionType isEqualToString:KeyActionTypeOpen]) { - [self openPageAction:actionInformation additionalData:additionalData delegate:delegate]; - } else if ([actionType isEqualToString:KeyActionTypeLinkAway]) { - [self linkAwayAction:actionInformation additionalData:additionalData delegate:delegate]; - } else if ([actionType isEqualToString:KeyActionTypeRestart]) { - [self restartAction:actionInformation additionalData:additionalData delegate:delegate]; - } else if ([actionType isEqualToString:KeyActionTypeBack]) { - [self backAction:actionInformation additionalData:additionalData delegate:delegate]; - } else if ([actionType isEqualToString:KeyActionTypeCall]) { - [self callAction:actionInformation additionalData:additionalData delegate:delegate]; - } else if ([actionType isEqualToString:KeyActionTypePreviousSubmit]) { - [self previousSubmitAction:actionInformation additionalData:additionalData delegate:delegate]; - } else if ([actionType isEqualToString:KeyActionTypeRedirect]) { - [self redirectAction:actionInformation additionalData:additionalData delegate:delegate]; - } else if ([actionType isEqualToString:KeyActionTypeCancel]) { - [self cancelAction:actionInformation additionalData:additionalData delegate:delegate]; - } else if ([actionType isEqualToString:KeyActionTypeSettings]) { - [self settingsAction:actionInformation additionalData:additionalData delegate:delegate]; - } else if (![self handleOtherActions:actionType actionInformation:actionInformation additionalData:additionalData delegate:delegate]) { - // not a known action type. - [self unknownAction:actionType actionInformation:actionInformation additionalData:additionalData delegate:delegate]; - } - }); -} - -- (void)logAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - if ([delegate respondsToSelector:@selector(logActionWithActionInformation:additionalData:)]) { - [delegate logActionWithActionInformation:actionInformation additionalData:additionalData]; - } else { - [MVMCoreActionHandler defaultLogAction:actionInformation additionalData:additionalData delegate:delegate]; - } -} - -- (void)openPageAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - - // Loads the given page type. - NSString *pageType = [actionInformation stringForKey:KeyPageType]; - if (pageType.length > 0) { - MVMCoreRequestParameters *requestParameters = [[MVMCoreRequestParameters alloc] initWithActionMap:actionInformation]; - - [self updateRequestParametersBeforeHandleOpenPageAction:requestParameters callBack:^(MVMCoreRequestParameters * _Nonnull requestParameters) { - if ([delegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) { - [delegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:additionalData]; - } else { - [MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters additionalData:additionalData delegate:delegate]; - } - }]; - } else { - - // No page type to load, show error. - MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeNoPageType domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@_%@",NSStringFromClass([delegate class]),KeyActionTypeOpen]]; - [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegate:delegate]; - } -} - -- (void)restartAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - - // Invalidates the session before restarting. - [[MVMCoreSessionTimeHandler sharedSessionHandler] invalidateSession:^(MVMCoreErrorObject * _Nullable error) { - - // Restarts the app (forcing any passed in page types). - if (error.code != NSURLErrorCancelled) { - - if (error) { - - // Error invalidating. - [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegate:delegate]; - } else { - - // Restart the application with the page type. - NSString *pageType = [actionInformation string:KeyPageType]; - NSDictionary *parameters = [actionInformation dict:KeyExtraParameters]; - [[MVMCoreSessionObject sharedGlobal] restartSessionWithPageType:pageType parameters:parameters clearAllVariables:YES]; - } - } - }]; -} - -- (void)backAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - // Go back. - if ([delegate respondsToSelector:@selector(handleBackAction:additionalData:)]) { - [delegate handleBackAction:actionInformation additionalData:additionalData]; - } else { - [[MVMCoreNavigationHandler sharedNavigationHandler] removeCurrentViewController]; - } -} - -- (void)callAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - // Call - NSString *callNumber = [actionInformation stringForKey:KeyCallNumber]; - [MVMCoreActionUtility linkAway:[@"tel://" stringByAppendingString:callNumber] appURLString:nil]; -} - -- (void)previousSubmitAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - // Perform the previous submission. - __weak typeof(self) weakSelf = self; - if ([delegate respondsToSelector:@selector(prepareRequestForPreviousSubmission:additionalData:submit:)]) { - [delegate prepareRequestForPreviousSubmission:actionInformation additionalData:additionalData submit:^(MVMCoreRequestParameters * _Nonnull requestParameters, NSDictionary * _Nullable dataForPage) { - - [weakSelf updateRequestParametersBeforeHandleOpenPageAction:requestParameters callBack:^(MVMCoreRequestParameters * _Nonnull requestParameters) { - // Give the delegate a chance to alter the request parameters - if ([delegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) { - [delegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:dataForPage]; - } else { - [MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters additionalData:additionalData delegate:delegate]; - } - }]; - }]; - } -} - -- (void)redirectAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - // Have delegate redirect. - [[MVMCoreSessionObject sharedGlobal] redirectWithInfo:actionInformation]; -} - -- (void)cancelAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - if ([delegate respondsToSelector:@selector(handleCancel:additionalData:)]) { - [delegate handleCancel:actionInformation additionalData:additionalData]; - } -} - -- (void)settingsAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - // Opens the settings. - [MVMCoreActionUtility linkAway:UIApplicationOpenSettingsURLString appURLString:nil]; -} - -- (BOOL)handleOtherActions:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - return NO; -} - -- (void)unknownAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - if ([delegate respondsToSelector:@selector(handleUnknownActionType:actionInformation:additionalData:)]) { - [delegate handleUnknownActionType:actionType actionInformation:actionInformation additionalData:additionalData]; - } else { - [MVMCoreActionHandler defaultHandleUnknownActionType:actionType actionInformation:actionInformation additionalData:additionalData delegate:delegate]; - } -} - -- (void)handleActionError:(nullable MVMCoreErrorObject *)error actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - if (error) { - if ([delegate respondsToSelector:@selector(handleActionError:additionalData:)]) { - [delegate handleActionError:error additionalData:additionalData]; - } else { - [self defaultHandleActionError:error additionalData:additionalData]; - } - } -} - -- (void)linkAwayAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - - // Gets the app url - NSURL *appURL = nil; - NSString *appURLString = [actionInformation string:KeyLinkAwayAppURL]; - if (appURLString.length > 0) { - appURL = [NSURL URLWithString:appURLString]; - } - - // Gets the browser url - NSURL *otherURL = nil; - NSString *otherURLString = [actionInformation string:KeyLinkAwayURL]; - if (otherURLString.length > 0) { - otherURL = [NSURL URLWithString:otherURLString]; - } - - // Provide the URL and App URL to be modified if needed by a subclass or delegate. - [self prepareLinkAwayWithURL:otherURL appURL:appURL actionInformation:actionInformation additionalData:additionalData delegate:delegate]; -} - -- (void)prepareLinkAwayWithURL:(nullable NSURL *)url appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - - void(^openURL)(NSURL *, NSURL *, NSDictionary *, NSDictionary *) = ^(NSURL *appURL, NSURL *URL, NSDictionary *actionInformation, NSDictionary *additionalData) { - [self openURL:url appURL:appURL actionInformation:actionInformation additionalData:additionalData delegate:delegate]; - }; - - // Allow delegate to modify before opening the url. - if ([delegate respondsToSelector:@selector(shouldLinkAwayWithURL:appURL:actionInformation:additionalData:linkAwayBlock:)]) { - [delegate shouldLinkAwayWithURL:url appURL:appURL actionInformation:actionInformation additionalData:additionalData linkAwayBlock:openURL]; - } else { - openURL(appURL,url,actionInformation,additionalData); - } -} - -- (void)openURL:(nullable NSURL *)url appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - - [MVMCoreDispatchUtility performBlockOnMainThread:^{ - - // First try to open the application. - if (appURL && [[UIApplication sharedApplication] canOpenURL:appURL]) { - [[UIApplication sharedApplication] openURL:appURL options:@{} completionHandler:NULL]; - } else if (url && [[UIApplication sharedApplication] canOpenURL:url]) { - - // Check if we should load in webview - BOOL openInWebview = [actionInformation boolForKey:@"openInWebview"]; - if (openInWebview) { - [self openURLInWebView:url actionInformation:actionInformation additionalData:additionalData delegate:delegate]; - } else { - [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:NULL]; - } - } else { - [MVMCoreDispatchUtility performBlockInBackground:^{ - // Cannot linkaway, show error. - MVMCoreErrorObject *error = error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeLinkawayFailed domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@_%@",NSStringFromClass([delegate class]),KeyActionTypeLinkAway]]; - [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegate:delegate]; - }]; - } - }]; -} - -- (void)openURLInWebView:(nullable NSURL *)url actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - - // Presents standard webview. - SFSafariViewController *safariViewController = [[SFSafariViewController alloc] initWithURL:url]; - //safariViewController.delegate = self; - safariViewController.preferredBarTintColor = [UIColor whiteColor]; - safariViewController.preferredControlTintColor = [UIColor blackColor]; - [[MVMCoreNavigationHandler sharedNavigationHandler] presentViewController:safariViewController animated:YES]; -} - -+ (void)defaultLogAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate{ - // Currently no default log action but this will eventually be server driven. -} - -+ (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject*)delegate { - [[MVMCoreLoadHandler sharedGlobal] loadRequest:requestParameters dataForPage:additionalData delegate:delegate]; -} - -+ (void)defaultHandleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { - - MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeUnknownActionType domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@Requests_%@",NSStringFromClass([delegate class]),actionType]]; - [[self sharedActionHandler] defaultHandleActionError:error additionalData:additionalData]; + DelegateObject *delegateObject = [[DelegateObject alloc] init]; + delegateObject.actionDelegate = delegate; + delegateObject.presentationDelegate = delegate; + delegateObject.loadDelegate = delegate; + [self handleActionWithDictionary:dictionary additionalData:additionalData delegateObject:delegateObject]; } @end diff --git a/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.h b/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.h index f3d84ac..c2d7f8e 100644 --- a/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.h +++ b/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.h @@ -40,6 +40,7 @@ extern NSString * const KeyActionTypeCancel; extern NSString * const KeyActionTypeRedirect; extern NSString * const KeyActionTypeSettings; extern NSString * const KeyActionTypeNoop; +extern NSString * const KeyActionTypeActions; extern NSString * const KeyActionInformation; extern NSString * const KeyLinkAwayAppURL; extern NSString * const KeyLinkAwayURL; @@ -51,6 +52,8 @@ extern NSString * const KeyContextRoot; extern NSString * const KeyType; extern NSString * const KeyMVMRC; +extern NSString * const KeyClientParameters; + #pragma mark - JSON Values // Server driven response type diff --git a/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.m b/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.m index 71cccac..80317ea 100644 --- a/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.m +++ b/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.m @@ -40,6 +40,7 @@ NSString * const KeyActionTypeCancel = @"cancel"; NSString * const KeyActionTypeRedirect = @"switchApp"; NSString * const KeyActionTypeSettings = @"openSettings"; NSString * const KeyActionTypeNoop = @"noop"; +NSString * const KeyActionTypeActions = @"actions"; NSString * const KeyActionInformation = @"actionInformation"; NSString * const KeyLinkAwayAppURL = @"appURL"; NSString * const KeyLinkAwayURL = @"browserUrl"; @@ -51,6 +52,8 @@ NSString * const KeyContextRoot = @"appContext"; NSString * const KeyType = @"type"; NSString * const KeyMVMRC = @"LaunchMVMRC"; +NSString * const KeyClientParameters = @"clientParameters"; + #pragma mark - JSON Values // Server driven response type diff --git a/MVMCore/MVMCore/Models/ActionType/ActionActionsModel.swift b/MVMCore/MVMCore/Models/ActionType/ActionActionsModel.swift new file mode 100644 index 0000000..0638f3e --- /dev/null +++ b/MVMCore/MVMCore/Models/ActionType/ActionActionsModel.swift @@ -0,0 +1,67 @@ +// +// ActionActionsModel.swift +// MVMCore +// +// Created by Scott Pfeil on 1/22/21. +// Copyright © 2021 myverizon. All rights reserved. +// + +import Foundation + +@objcMembers open class ActionActionsModel: ActionModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "actions" + public var actionType: String = ActionActionsModel.identifier + public var actions: [ActionModelProtocol] + public var concurrent = true + public var extraParameters: JSONValueDictionary? + public var analyticsData: JSONValueDictionary? + + //-------------------------------------------------- + // MARK: - Initialzier + //-------------------------------------------------- + + public init(actions: [ActionModelProtocol], extraParameters: JSONValueDictionary? = nil, analyticsData: JSONValueDictionary? = nil) { + self.actions = actions + self.extraParameters = extraParameters + self.analyticsData = analyticsData + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case actionType + case actions + case concurrent + case extraParameters + case analyticsData + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + actions = try typeContainer.decodeModels(codingKey: .actions) + if let concurrent = try typeContainer.decodeIfPresent(Bool.self, forKey: .concurrent) { + self.concurrent = concurrent + } + extraParameters = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .extraParameters) + analyticsData = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .analyticsData) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(actionType, forKey: .actionType) + try container.encodeModels(actions, forKey: .actions) + try container.encode(concurrent, forKey: .concurrent) + try container.encodeIfPresent(extraParameters, forKey: .extraParameters) + try container.encodeIfPresent(analyticsData, forKey: .analyticsData) + } +} diff --git a/MVMCore/MVMCore/Models/ActionType/ActionOpenPageModel.swift b/MVMCore/MVMCore/Models/ActionType/ActionOpenPageModel.swift index 40e7312..a20d343 100644 --- a/MVMCore/MVMCore/Models/ActionType/ActionOpenPageModel.swift +++ b/MVMCore/MVMCore/Models/ActionType/ActionOpenPageModel.swift @@ -7,7 +7,7 @@ // -@objcMembers open class ActionOpenPageModel: ActionModelProtocol { +@objcMembers open class ActionOpenPageModel: ActionModelProtocol, ClientParameterActionProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -20,7 +20,8 @@ public var presentationStyle: String? public var tabBarIndex: Int? public var background: Bool? - + public var clientParameters: ClientParameterModel? + //-------------------------------------------------- // MARK: - Initialzier //-------------------------------------------------- diff --git a/MVMCore/MVMCore/Models/ActionType/ActionOpenUrlModel.swift b/MVMCore/MVMCore/Models/ActionType/ActionOpenUrlModel.swift index 03a1e95..bf7c020 100644 --- a/MVMCore/MVMCore/Models/ActionType/ActionOpenUrlModel.swift +++ b/MVMCore/MVMCore/Models/ActionType/ActionOpenUrlModel.swift @@ -7,7 +7,7 @@ // -@objcMembers public class ActionOpenUrlModel: ActionModelProtocol { +@objcMembers public class ActionOpenUrlModel: ActionModelProtocol, ClientParameterActionProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -18,6 +18,7 @@ public var browserUrl: String public var extraParameters: JSONValueDictionary? public var analyticsData: JSONValueDictionary? + public var clientParameters: ClientParameterModel? public var appURL: String? //TODO: Should be removed in future releases. This should be MF specific. @@ -30,6 +31,7 @@ public var dontShowProgress: Bool? public var headerParameters: JSONValueDictionary? public var enableNativeScroll: Bool? + public var hideUrl: Bool? //-------------------------------------------------- // MARK: - Initialzier diff --git a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterActionProtocol.swift b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterActionProtocol.swift new file mode 100644 index 0000000..f969a95 --- /dev/null +++ b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterActionProtocol.swift @@ -0,0 +1,13 @@ +// +// ClientParameterActionProtocol.swift +// MVMCore +// +// Created by Suresh, Kamlesh on 1/5/21. +// Copyright © 2021 myverizon. All rights reserved. +// + +import Foundation + +public protocol ClientParameterActionProtocol { + var clientParameters: ClientParameterModel? { get set } +} diff --git a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModel.swift b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModel.swift new file mode 100644 index 0000000..37940d7 --- /dev/null +++ b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModel.swift @@ -0,0 +1,31 @@ +// +// ClientParameterModel.swift +// MVMCore +// +// Created by Suresh, Kamlesh on 12/28/20. +// Copyright © 2020 myverizon. All rights reserved. +// + +import Foundation + +public class ClientParameterModel: Codable { + var timeout: Double? + var list: [ClientParameterModelProtocol] + + private enum CodingKeys: String, CodingKey { + case timeout + case list + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + timeout = try typeContainer.decodeIfPresent(Double.self, forKey: .timeout) + list = try typeContainer.decodeModels(codingKey: .list) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(timeout, forKey: .timeout) + try container.encodeModels(list, forKey: .list) + } +} diff --git a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModelProtocol.swift b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModelProtocol.swift new file mode 100644 index 0000000..b742170 --- /dev/null +++ b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterModelProtocol.swift @@ -0,0 +1,28 @@ +// +// ClientParameterProtocol.swift +// MVMCore +// +// Created by Suresh, Kamlesh on 12/28/20. +// Copyright © 2020 myverizon. All rights reserved. +// + +import Foundation + +public protocol ClientParameterModelProtocol: ModelProtocol { + var type: String { get } +} + +public extension ClientParameterModelProtocol { + + var type: String { + get { Self.identifier } + } + + static var categoryCodingKey: String { + return "type" + } + + static var categoryName: String { + return "\(ClientParameterModelProtocol.self)" + } +} diff --git a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterProtocol.swift b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterProtocol.swift new file mode 100644 index 0000000..2c689ec --- /dev/null +++ b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterProtocol.swift @@ -0,0 +1,15 @@ +// +// ClientParameterProtocol.swift +// MVMCore +// +// Created by Suresh, Kamlesh on 12/29/20. +// Copyright © 2020 myverizon. All rights reserved. +// + +import Foundation + +public protocol ClientParameterProtocol { + init() + static var name: String { get } + func fetchClientParameters(for paramModel: ClientParameterModelProtocol, timingOutIn timeout: Double, completionHandler:@escaping (AnyHashable?) -> ()) +} diff --git a/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterRegistry.swift b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterRegistry.swift new file mode 100644 index 0000000..1a4160d --- /dev/null +++ b/MVMCore/MVMCore/Models/ActionType/Client Parameters/ClientParameterRegistry.swift @@ -0,0 +1,127 @@ +// +// ClientParameterRegistry.swift +// MVMCore +// +// Created by Suresh, Kamlesh on 12/29/20. +// Copyright © 2020 myverizon. All rights reserved. +// + +import Foundation + + +@objcMembers open class ClientParameterRegistry: NSObject { + + private var mapping: [String: ClientParameterProtocol.Type] = [:] + + + public override init() { + super.init() + registerParameters() + } + + open func register(_ handler: T.Type) { + mapping[T.name] = handler + } + + open func createParametersHandler(_ actionType: String) -> ClientParameterProtocol? { + guard let parameterType = mapping[actionType] else { return nil } + return parameterType.init() + } + + static func getClientParameterModel(_ clientParameters: [String: Any]) throws -> ClientParameterModel? { + let data = try JSONSerialization.data(withJSONObject: clientParameters) + return try JSONDecoder().decode(ClientParameterModel.self, from: data) + } + + /// Sample clientParameters + ///{ + /// "timeout": 30, + /// "list": [ + /// { + /// "type": "currentLocation", + /// "inputParameters": { + /// "accuracy": 10, + /// "timeThreshold": 600 + /// } + /// } + /// ] + ///} + /// completionHandler can return flat dictinary or a map. It depends on the paramters handler + open func getParameters(with clientParameters: [String: Any], completionHandler:@escaping ([String: Any]?) -> ()) throws { + + guard let clientParameterModel = try ClientParameterRegistry.getClientParameterModel(clientParameters) else { + completionHandler(nil) + return + } + + var parametersList: [String: Any] = [:] + let timeout = clientParameterModel.timeout ?? 30.0 + + let parametersWorkQueue = DispatchQueue(label: "com.mva.clientparameter") + let group = DispatchGroup() + let defaultErrorString = "failed_to_collect." + + // Dispatch setup on queue to ensure setup is complete before completion callbacks. + parametersWorkQueue.async(group: group, qos: .userInitiated) { [weak self] in + guard let self = self else { return } + // Setup completion handlers. Barriered to ensure one happens after the other. + var complete = false + let timeoutWorkItem = DispatchWorkItem(qos: .userInitiated) { + completionHandler(parametersList); + complete = true + } + let completionWorkItem = DispatchWorkItem(qos: .userInitiated) { + timeoutWorkItem.cancel() + if !complete { // In the case of firing after timeout. + completionHandler(parametersList); + complete = true + } + } + + // Setup timeout. + parametersWorkQueue.asyncAfter(deadline: .now() + .seconds(Int(timeout)), execute: timeoutWorkItem) + + // Setup the parameter execution. + for parameterModel in clientParameterModel.list { + // Setup default timeout / nil error. This will be replaced as parameters are collected. + parametersList[parameterModel.type] = ["error": defaultErrorString] + group.enter() + // Dispatch asynchronous injection. + self.getParameterFromHandler(parameterModel, before: timeout) { (receivedParameter) in + // Queue the results for merge. + parametersWorkQueue.async { + if let receivedParameter = receivedParameter { + parametersList[parameterModel.type] = receivedParameter + } + group.leave() // Leaving is only done after setup (barriered). + } + } + } + + // Callback when all parameters have been merged. + group.notify(queue: parametersWorkQueue, work: completionWorkItem); + } + } + + func getParameterFromHandler( _ parameterModel: ClientParameterModelProtocol, before timeout: Double, completionHandler:@escaping (AnyHashable?) -> ()) { + guard let parameterHandler = createParametersHandler(parameterModel.type) else { + return completionHandler(nil) + } + parameterHandler.fetchClientParameters(for: parameterModel, timingOutIn: timeout, completionHandler: completionHandler) + } + + /// Add all registry here. + open func registerParameters() { } + + /// Register Default Core Client Paramter Objects + public func register(handler: T.Type, for model: M.Type) throws { + try ModelRegistry.register(model) + register(handler) + } + + /// Register Default Core Client Paramter Objects + public static func register(handler: T.Type, for model: M.Type) throws { + try ModelRegistry.register(model) + MVMCoreObject.sharedInstance()?.clientParameterRegistry?.register(handler) + } +} diff --git a/MVMCore/MVMCore/Models/ModelMapping.swift b/MVMCore/MVMCore/Models/ModelMapping.swift index 74acf80..6c3df04 100644 --- a/MVMCore/MVMCore/Models/ModelMapping.swift +++ b/MVMCore/MVMCore/Models/ModelMapping.swift @@ -21,5 +21,6 @@ import Foundation try? ModelRegistry.register(ActionCancelModel.self) try? ModelRegistry.register(ActionSettingModel.self) try? ModelRegistry.register(ActionNoopModel.self) + try? ModelRegistry.register(ActionActionsModel.self) } } diff --git a/MVMCore/MVMCore/Singletons/MVMCoreObject.h b/MVMCore/MVMCore/Singletons/MVMCoreObject.h index 03a8411..023ad63 100644 --- a/MVMCore/MVMCore/Singletons/MVMCoreObject.h +++ b/MVMCore/MVMCore/Singletons/MVMCoreObject.h @@ -18,6 +18,8 @@ #import #import +@class ClientParameterRegistry; + @interface MVMCoreObject : NSObject @property (nullable, strong, nonatomic) MVMCoreSessionObject *session; @@ -26,6 +28,7 @@ @property (nullable, strong, nonatomic) MVMCoreActionHandler *actionHandler; @property (nullable, strong, nonatomic) MVMCoreSessionTimeHandler *sessionHandler; @property (nullable, strong, nonatomic) MVMCoreLoadHandler *loadHandler; +@property (nullable, strong, nonatomic) ClientParameterRegistry *clientParameterRegistry; // The delegates @property (nullable, strong, nonatomic) id globalLoadDelegate; diff --git a/MVMCore/MVMCore/Singletons/MVMCoreObject.m b/MVMCore/MVMCore/Singletons/MVMCoreObject.m index 8ad4cb5..6b60d65 100644 --- a/MVMCore/MVMCore/Singletons/MVMCoreObject.m +++ b/MVMCore/MVMCore/Singletons/MVMCoreObject.m @@ -7,6 +7,7 @@ // #import "MVMCoreObject.h" +#import @implementation MVMCoreObject @@ -27,6 +28,7 @@ self.actionHandler = [[MVMCoreActionHandler alloc] init]; self.loggingDelegate = [[MVMCoreLoggingHandler alloc] init]; self.loadHandler = [[MVMCoreLoadHandler alloc] init]; + self.clientParameterRegistry = [[ClientParameterRegistry alloc] init]; } @end