diff --git a/MVMCore/MVMCore.xcodeproj/project.pbxproj b/MVMCore/MVMCore.xcodeproj/project.pbxproj index 22c445b..edfc67b 100644 --- a/MVMCore/MVMCore.xcodeproj/project.pbxproj +++ b/MVMCore/MVMCore.xcodeproj/project.pbxproj @@ -69,6 +69,7 @@ 94C014D924212360005811A9 /* ActionSettingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94C014D824212360005811A9 /* ActionSettingModel.swift */; }; A332F33F20C7231700DCD9D9 /* MVMCoreViewControllerAnimatedTransitioning.h in Headers */ = {isa = PBXBuildFile; fileRef = A332F33E20C7231600DCD9D9 /* MVMCoreViewControllerAnimatedTransitioning.h */; settings = {ATTRIBUTES = (Public, ); }; }; AF1201832108C9B400E2F592 /* MVMCoreViewManagerViewControllerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AF1201812108C9B400E2F592 /* MVMCoreViewManagerViewControllerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AF130B8E2788DF6E00C6C03C /* OpenURLOptionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF130B8D2788DF6E00C6C03C /* OpenURLOptionsModel.swift */; }; AF26DDAE1FCE6A37004E8F65 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = AF26DDB01FCE6A37004E8F65 /* Localizable.strings */; }; AF43A5771FBA5B7C008E9347 /* MVMCoreJSONConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A5751FBA5B7C008E9347 /* MVMCoreJSONConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; AF43A5781FBA5B7C008E9347 /* MVMCoreJSONConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A5761FBA5B7C008E9347 /* MVMCoreJSONConstants.m */; }; @@ -91,6 +92,7 @@ AF43A7411FC5FA6F008E9347 /* MVMCoreViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; AF43A74C1FC6109F008E9347 /* MVMCoreSessionObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; AF43A74D1FC6109F008E9347 /* MVMCoreSessionObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */; }; + AF8D13392774EA1D008AF4A9 /* ActionOpenUrlHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF8D13382774EA1D008AF4A9 /* ActionOpenUrlHandler.swift */; }; AFBB96341FBA34310008D868 /* MVMCoreErrorConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96321FBA34310008D868 /* MVMCoreErrorConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; AFBB96351FBA34310008D868 /* MVMCoreErrorConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96331FBA34310008D868 /* MVMCoreErrorConstants.m */; }; AFBB96381FBA39E70008D868 /* MVMCoreLoadDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96371FBA39E70008D868 /* MVMCoreLoadDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -212,6 +214,7 @@ 94C014D824212360005811A9 /* ActionSettingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionSettingModel.swift; sourceTree = ""; }; A332F33E20C7231600DCD9D9 /* MVMCoreViewControllerAnimatedTransitioning.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewControllerAnimatedTransitioning.h; sourceTree = ""; }; AF1201812108C9B400E2F592 /* MVMCoreViewManagerViewControllerProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewManagerViewControllerProtocol.h; sourceTree = ""; }; + AF130B8D2788DF6E00C6C03C /* OpenURLOptionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenURLOptionsModel.swift; sourceTree = ""; }; AF26DDAF1FCE6A37004E8F65 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; AF43A5751FBA5B7C008E9347 /* MVMCoreJSONConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreJSONConstants.h; sourceTree = ""; }; AF43A5761FBA5B7C008E9347 /* MVMCoreJSONConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreJSONConstants.m; sourceTree = ""; }; @@ -237,6 +240,7 @@ AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewProtocol.h; sourceTree = ""; }; AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreSessionObject.h; sourceTree = ""; }; AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreSessionObject.m; sourceTree = ""; }; + AF8D13382774EA1D008AF4A9 /* ActionOpenUrlHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionOpenUrlHandler.swift; sourceTree = ""; }; AFBB96321FBA34310008D868 /* MVMCoreErrorConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreErrorConstants.h; sourceTree = ""; }; AFBB96331FBA34310008D868 /* MVMCoreErrorConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreErrorConstants.m; sourceTree = ""; }; AFBB96371FBA39E70008D868 /* MVMCoreLoadDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadDelegateProtocol.h; sourceTree = ""; }; @@ -455,7 +459,6 @@ 01F2A03523A80A7300D954D8 /* ActionModelProtocol.swift */, 946EE1BB237B691A0036751F /* ActionOpenPageModel.swift */, 01DB1F2A26444F7F000F1AF4 /* ActionOpenPageProtocol.swift */, - 01F2A03823A812DD00D954D8 /* ActionOpenUrlModel.swift */, 01F2A04B23A82B1B00D954D8 /* ActionCallModel.swift */, 01C851D023CF97FE0021F976 /* ActionBackModel.swift */, 0AFF597923FC6E60005C24E8 /* ActionShareModel.swift */, @@ -615,6 +618,9 @@ AFBB96B61FBA3CEC0008D868 /* MVMCoreActionHandler.h */, AFBB96B71FBA3CEC0008D868 /* MVMCoreActionHandler.m */, D27073CC25BB4CEF001C7246 /* MVMCoreActionHandler+Extension.swift */, + AF130B8D2788DF6E00C6C03C /* OpenURLOptionsModel.swift */, + 01F2A03823A812DD00D954D8 /* ActionOpenUrlModel.swift */, + AF8D13382774EA1D008AF4A9 /* ActionOpenUrlHandler.swift */, ); path = ActionHandling; sourceTree = ""; @@ -844,6 +850,8 @@ 946EE1A7237B5B1C0036751F /* ModelRegistry.swift in Sources */, AFBB96641FBA3A570008D868 /* MVMCoreLoadHandler.m in Sources */, AFFCFA671FCCC0D700FD0730 /* MVMCoreLoadingOverlayHandler.m in Sources */, + AF8D13392774EA1D008AF4A9 /* ActionOpenUrlHandler.swift in Sources */, + AF130B8E2788DF6E00C6C03C /* OpenURLOptionsModel.swift in Sources */, AFBB968E1FBA3A9A0008D868 /* MVMCoreNavigationHandler.m in Sources */, BB780ADF250F8C890030BD2F /* ActionNoopModel.swift in Sources */, D2E1FAD92260C3E400AEFD8C /* DelegateObject.swift in Sources */, diff --git a/MVMCore/MVMCore/ActionHandling/ActionOpenUrlHandler.swift b/MVMCore/MVMCore/ActionHandling/ActionOpenUrlHandler.swift new file mode 100644 index 0000000..edecde4 --- /dev/null +++ b/MVMCore/MVMCore/ActionHandling/ActionOpenUrlHandler.swift @@ -0,0 +1,60 @@ +// +// ActionOpenUrlHandler.swift +// MVMCore +// +// Created by Scott Pfeil on 12/23/21. +// Copyright © 2021 myverizon. All rights reserved. +// + +import Foundation + +open class ActionOpenUrlHandler: MVMCoreActionHandlerProtocol { + required public init() {} + + open func handleAction(_ model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) { + guard let model = model as? ActionOpenUrlModel else { return } + let linkAway = { [self] (appURL: URL?, browserURL: URL?, actionInformation: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) in + MVMCoreDispatchUtility.performBlock(onMainThread: { + // Try loading the app url first, otherwise fall back to browser url. + guard let appURL = appURL else { + open(url: browserURL, model: model, additionalData: additionalData, delegateObject: delegateObject) + return + } + UIApplication.shared.open(appURL, options: model.appURLOptions?.options ?? [:]) { loaded in + guard !loaded else { return } + MVMCoreLoggingHandler.shared()?.handleDebugMessage("Failed to open app url: \(appURL)") + open(url: browserURL, model: model, additionalData: additionalData, delegateObject: delegateObject) + } + }) + } + + // Allow legacy Delegate intercept for now. + let actionInformation = model.toJSON() + delegateObject?.actionDelegate?.shouldLinkAway?(with: model.browserUrl, appURL: model.appURL, actionInformation: actionInformation, additionalData: additionalData, linkAwayBlock: linkAway) ?? linkAway(model.appURL, model.browserUrl, actionInformation, additionalData) + } + + /// Opens the url. + open func open(url: URL?, model: ActionOpenUrlModel, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) { + guard let url = url else { + handleError(url: url, model: model, additionalData: additionalData, delegateObject: delegateObject) + return + } + UIApplication.shared.open(url, options: [:]) { [self] loaded in + guard !loaded else { return } + handleError(url: url, model: model, additionalData: additionalData, delegateObject: delegateObject) + } + } + + /// Handles any url loading errors. + open func handleError(url: URL?, model: ActionOpenUrlModel, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) { + if let error = MVMCoreErrorObject(title: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorTitle), + message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), + messageToLog: "Unable to load URL: \(String(describing: url))", code: ErrorCode.linkawayFailed.rawValue, + domain: ErrorDomainNative, + location: MVMCoreActionHandler.getErrorLocation(with: delegateObject?.actionDelegate, actionType: ActionOpenUrlModel.identifier)) { + MVMCoreDispatchUtility.performBlock(inBackground: { + MVMCoreActionHandler.shared()?.handleActionError(error, actionInformation: model.toJSON(), additionalData: additionalData, delegateObject: delegateObject) + }) + } + } +} diff --git a/MVMCore/MVMCore/ActionHandling/ActionOpenUrlModel.swift b/MVMCore/MVMCore/ActionHandling/ActionOpenUrlModel.swift new file mode 100644 index 0000000..9133883 --- /dev/null +++ b/MVMCore/MVMCore/ActionHandling/ActionOpenUrlModel.swift @@ -0,0 +1,63 @@ +// +// ActionOpenUrlModel.swift +// MVMCore +// +// Created by Suresh, Kamlesh on 12/16/19. +// Copyright © 2019 myverizon. All rights reserved. +// + +import Foundation + +@objcMembers open class ActionOpenUrlModel: ActionModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "openURL" + public var actionType: String = ActionOpenUrlModel.identifier + public var browserUrl: URL + public var appURL: URL? + public var appURLOptions: OpenUrlOptionsModel? + public var extraParameters: JSONValueDictionary? + public var analyticsData: JSONValueDictionary? + + //-------------------------------------------------- + // MARK: - Initialzier + //-------------------------------------------------- + + public init(browserUrl: URL) { + self.browserUrl = browserUrl + } + + //-------------------------------------------------- + // MARK: - Codable + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case actionType + case browserUrl + case appURL + case appURLOptions + case extraParameters + case analyticsData + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + browserUrl = try typeContainer.decode(URL.self, forKey: .browserUrl) + appURL = try typeContainer.decodeIfPresent(URL.self, forKey: .appURL) + appURLOptions = try typeContainer.decodeIfPresent(OpenUrlOptionsModel.self, forKey: .appURLOptions) + extraParameters = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .extraParameters) + analyticsData = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .analyticsData) + } + + open func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(actionType, forKey: .actionType) + try container.encode(browserUrl, forKey: .browserUrl) + try container.encodeIfPresent(appURL, forKey: .appURL) + try container.encodeIfPresent(appURLOptions, forKey: .appURLOptions) + try container.encodeIfPresent(extraParameters, forKey: .extraParameters) + try container.encodeIfPresent(analyticsData, forKey: .analyticsData) + } +} diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler+Extension.swift b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler+Extension.swift index 273b8b9..8b66d82 100644 --- a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler+Extension.swift +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler+Extension.swift @@ -77,4 +77,8 @@ public extension MVMCoreActionHandler { guard let json = convertActionToJSON(model, delegateObject: delegateObject) else { return } handleAction(model.actionType, actionInformation: json, additionalData: additionalData, delegateObject: delegateObject) } + + @objc static func getErrorLocation(with delegate: MVMCoreActionDelegateProtocol?, actionType: String) -> String { + return "\(String(describing: delegate))_\(actionType)" + } } diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h index 1dc2aa5..068b018 100644 --- a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h @@ -80,20 +80,6 @@ extern NSString * _Nonnull const KeyActionTypeOpen; /// 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 -- (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) -- (void)prepareLinkAwayWithURL:(nullable NSURL *)url appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject; - -/// 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. -- (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. diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m index 5877975..cac409d 100644 --- a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m @@ -60,9 +60,6 @@ NSString * const KeyActionTypeOpen = @"openPage"; 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]; @@ -450,92 +447,6 @@ NSString * const KeyActionTypeOpen = @"openPage"; }]; } -#pragma mark - Open URL - -- (void)linkAwayAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - - __weak typeof(self) weakSelf = self; - void (^performAction)(NSDictionary*) = ^(NSDictionary* extraParamters) { - - NSMutableDictionary *actionWithClientParameters = [actionInformation mutableCopy]; - NSMutableDictionary *extraParametersT = [extraParamters mutableCopy]; - [extraParametersT addEntriesFromDictionary:[actionWithClientParameters dictionaryForKey:KeyExtraParameters]]; - actionWithClientParameters[KeyExtraParameters] = extraParametersT; - - // Gets the app url - NSURL *appURL = nil; - NSString *appURLString = [actionWithClientParameters string:KeyLinkAwayAppURL]; - if (appURLString.length > 0) { - appURL = [NSURL URLWithString:appURLString]; - } - - // Gets the browser url - NSURL *otherURL = nil; - NSString *otherURLString = [actionWithClientParameters 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:actionWithClientParameters additionalData:additionalData delegateObject:delegateObject]; - }; - - [self getClientParameter:[actionInformation dict:KeyClientParameters] - requestParameters:nil - showLoadingOverlay:true - completionHandler:performAction]; -} - -- (void)prepareLinkAwayWithURL:(nullable NSURL *)url appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - - void(^openURL)(NSURL *, NSURL *, NSDictionary *, NSDictionary *) = ^(NSURL *appURL, NSURL *URL, NSDictionary *actionInformation, NSDictionary *additionalData) { - [self openURL:url appURL:appURL actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]; - }; - - // Allow delegate to modify before opening the url. - if ([delegateObject.actionDelegate respondsToSelector:@selector(shouldLinkAwayWithURL:appURL:actionInformation:additionalData:linkAwayBlock:)]) { - [delegateObject.actionDelegate 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 delegateObject:(nullable DelegateObject *)delegateObject { - - [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 delegateObject:delegateObject]; - } else { - [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:NULL]; - } - } else { - [MVMCoreDispatchUtility performBlockInBackground:^{ - // Cannot linkaway, show error. - MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeLinkawayFailed domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@_%@",NSStringFromClass([delegateObject.actionDelegate class]),KeyActionTypeLinkAway]]; - [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject]; - }]; - } - }]; -} - -- (void)openURLInWebView:(nullable NSURL *)url actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject { - - // 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]; -} - #pragma mark - Default Action Protocol + (void)defaultLogAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject{ diff --git a/MVMCore/MVMCore/ActionHandling/OpenURLOptionsModel.swift b/MVMCore/MVMCore/ActionHandling/OpenURLOptionsModel.swift new file mode 100644 index 0000000..045aa52 --- /dev/null +++ b/MVMCore/MVMCore/ActionHandling/OpenURLOptionsModel.swift @@ -0,0 +1,38 @@ +// +// OpenURLOptionsModel.swift +// MVMCore +// +// Created by Scott Pfeil on 1/7/22. +// Copyright © 2022 myverizon. All rights reserved. +// + +import Foundation + +/// A model for UIApplication.OpenExternalURLOptionsKey +open class OpenUrlOptionsModel: Codable { + public var options: [UIApplication.OpenExternalURLOptionsKey: Any] + + //-------------------------------------------------- + // MARK: - Codable + //-------------------------------------------------- + + // TODO: add eventAttribution + private enum CodingKeys: String, CodingKey { + case universalLinksOnly // UIApplicationOpenURLOptionUniversalLinksOnly + } + + public required init(from decoder: Decoder) throws { + options = [:] + let container = try decoder.container(keyedBy: CodingKeys.self) + if let universalLinksValue = try container.decodeIfPresent(Bool.self, forKey: .universalLinksOnly) { + options[.universalLinksOnly] = universalLinksValue + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + if let universalLinksValue = options[.universalLinksOnly] as? Bool { + try container.encode(universalLinksValue, forKey: .universalLinksOnly) + } + } +} diff --git a/MVMCore/MVMCore/Models/ActionType/ActionOpenUrlModel.swift b/MVMCore/MVMCore/Models/ActionType/ActionOpenUrlModel.swift deleted file mode 100644 index 981925d..0000000 --- a/MVMCore/MVMCore/Models/ActionType/ActionOpenUrlModel.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// ActionOpenUrlModel.swift -// MVMCore -// -// Created by Suresh, Kamlesh on 12/16/19. -// Copyright © 2019 myverizon. All rights reserved. -// - - -@objcMembers public class ActionOpenUrlModel: ActionModelProtocol, ClientParameterActionProtocol { - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - - public static var identifier: String = "openURL" - public var actionType: String = ActionOpenUrlModel.identifier - // TODO: decode into url once action handler is re-written - 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. - //Missing params - public var openOauthWebView: Bool? - public var showNativeNavigation: Bool? - public var openInWebview: Bool? - public var customUserAgent: String? - public var postRequest: Bool? - public var dontShowProgress: Bool? - public var headerParameters: JSONValueDictionary? - public var enableNativeScroll: Bool? - public var hideUrl: Bool? - public var pageType: String? - - //-------------------------------------------------- - // MARK: - Initialzier - //-------------------------------------------------- - - public init(browserUrl: String) { - self.browserUrl = browserUrl - } -} diff --git a/MVMCore/MVMCore/Models/Model/ModelRegistry.swift b/MVMCore/MVMCore/Models/Model/ModelRegistry.swift index f3fbae6..f8b2510 100644 --- a/MVMCore/MVMCore/Models/Model/ModelRegistry.swift +++ b/MVMCore/MVMCore/Models/Model/ModelRegistry.swift @@ -124,7 +124,7 @@ public struct ModelRegistry { /// - handler: The handling class taking an object of ModelHandlerProtocol.self which is used to register /// - model: The data object of ModelProtocol.self which is used to register /// - Throws: An Error object of `ModelRegistry.Error` - public static func throwable_register(handler: H.Type, model: M.Type) throws { + public static func throwable_register(handler: H.Type, model: M.Type, allowsReplace: Bool = false) throws { // Register the type try self.throwable_register(type: model) @@ -136,24 +136,31 @@ public struct ModelRegistry { // Check to ensure the Category/Container combination doesn't exist. if category.handlerTypes[key] != nil { - throw ModelRegistry.Error.duplicateRegistration(message: "ModelHandlerProtocol: \(String(describing: handler)) already exists in Category: \(category.name)") - } else { - category.handlerTypes[key] = handler + if allowsReplace { + MVMCoreLoggingHandler.logDebugMessage(withDelegate: "Duplicate Registration: writing over \(category.handlerTypes[key]!) with \(handler)") + } else { + throw ModelRegistry.Error.duplicateRegistration(message: "ModelHandlerProtocol: \(String(describing: handler)) already exists in Category: \(category.name)") + } } + category.handlerTypes[key] = handler categories[category.name] = category } /// Registers models for Atomic use. /// - Parameter type: Takes an object of ModelProtocol.self which is used to register /// - Throws: An Error object of `ModelRegistry.Error` - public static func throwable_register(type: M.Type) throws { + public static func throwable_register(type: M.Type, allowsReplace: Bool = false) throws { // Get the category for the ModelProtocol var category = getCategory(for: type) // Check to ensure the Category/Type combination doesn't exist. if category.instanceTypes[M.identifier] != nil { - throw ModelRegistry.Error.duplicateRegistration(message: "ModelProtocol: \(M.identifier) already exists in Category: \(M.categoryName)") + if allowsReplace { + MVMCoreLoggingHandler.logDebugMessage(withDelegate: "Duplicate Registration: writing over \(category.instanceTypes[M.identifier]!) with \(type)") + } else { + throw ModelRegistry.Error.duplicateRegistration(message: "ModelProtocol: \(M.identifier) already exists in Category: \(M.categoryName)") + } } category.instanceTypes[M.identifier] = type diff --git a/MVMCore/MVMCore/Models/ModelMapping.swift b/MVMCore/MVMCore/Models/ModelMapping.swift index 42c3b66..da05419 100644 --- a/MVMCore/MVMCore/Models/ModelMapping.swift +++ b/MVMCore/MVMCore/Models/ModelMapping.swift @@ -13,7 +13,7 @@ open class func registerActions() { ModelRegistry.register(ActionRunJavaScriptModel.self) ModelRegistry.register(ActionOpenPageModel.self) - ModelRegistry.register(ActionOpenUrlModel.self) + ModelRegistry.register(handler: ActionOpenUrlHandler.self, for: ActionOpenUrlModel.self) ModelRegistry.register(ActionCallModel.self) ModelRegistry.register(ActionBackModel.self) ModelRegistry.register(ActionShareModel.self)