Merge branch 'develop' of https://gitlab.verizon.com/BPHV_MIPS/mvm_core into bugfix/remove_no_page_error

This commit is contained in:
Khan, Arshad 2021-08-02 16:07:11 +05:30
commit b7fe17d564
53 changed files with 1590 additions and 560 deletions

75
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,75 @@
stages:
# - test
- build
- deploy
#test:
# stage: test
# script:
# - echo "This job tests something"
# tags:
# - xcode_12_2
build_project:
stage: build
script:
- xcodebuild build -project MVMCore/MVMCore.xcodeproj -scheme FatLibrary | xcpretty
- BUILD_DIR=$(xcodebuild -showBuildSettings -project MVMCore/MVMCore.xcodeproj | grep BUILD_DIR)
only:
- branches
- develop
tags:
- xcode_12_2
deploy_snapshot:
stage: deploy
script:
- cd Scripts && ./upload_core_frameworks.sh
only:
- branches
- develop
tags:
- bash_shell
environment:
name: oneartifactory
url: https://oneartifactoryprod.verizon.com/artifactory
variables:
ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory
#promote_snapshot:
# stage: go live
# # Prevent artifacts from needing to re-download. Everything we need is in Artifactory.
# dependencies: []
# script:
# # Grab the framework version from the xcode project.
# - framework_ver=$(cd RemoteView && agvtool vers -terse)
# - cd Scripts && ./promote_remote_view_frameworks.sh $framework_ver
# only:
# - tags
# tags:
# - bash_shell
# environment:
# name: oneartifactory
# url: https://oneartifactoryprod.verizon.com/artifactory
# variables:
# ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory
#
#create_version_tag:
# stage: tag
# when: manual
# # Prevent artifacts from needing to re-download.
# dependencies: []
# script:
# # Grab the framework version from the xcode project and create a tag of the version.
# - framework_ver=$(cd RemoteView && agvtool vers -terse)
# - git tag -a "v${framework_ver}" -m "Version ${framework_ver} created by gitlab-ci Build"
# # Extract the git repo url to ssh version (git@gitlab.verizon.com)
# - ci_push_repo="git@${CI_SERVER_HOST}:${CI_PROJECT_PATH}.git"
# - echo $ci_push_repo
# # Set the remote url for pushing assuming the gitlab runner has SSH access to the repo.
# - git remote set-url --push origin $ci_push_repo
# - git push origin "v${framework_ver}"
# only:
# - develop
# tags:
# - bash_shell

View File

@ -21,14 +21,22 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
016CF36925FA6DD400B82A1F /* ClientParameterHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016CF36825FA6DD400B82A1F /* ClientParameterHandler.swift */; };
016FF6EE259A4E6300F5E4AA /* ClientParameterModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016FF6ED259A4E6300F5E4AA /* ClientParameterModelProtocol.swift */; };
016FF6F2259A4FCC00F5E4AA /* ClientParameterModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016FF6F1259A4FCC00F5E4AA /* ClientParameterModel.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 */; };
01DB1F2B26444F7F000F1AF4 /* ActionOpenPageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DB1F2A26444F7F000F1AF4 /* ActionOpenPageProtocol.swift */; };
01DF561421F90ADC00CC099B /* Dictionary+MFConvenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DF561321F90ADC00CC099B /* Dictionary+MFConvenience.swift */; };
01F2A03623A80A7300D954D8 /* ActionModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F2A03523A80A7300D954D8 /* ActionModelProtocol.swift */; };
01F2A03923A812DD00D954D8 /* ActionOpenUrlModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F2A03823A812DD00D954D8 /* ActionOpenUrlModel.swift */; };
01F2A04C23A82B1B00D954D8 /* ActionCallModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F2A04B23A82B1B00D954D8 /* ActionCallModel.swift */; };
01F2A05223A8325100D954D8 /* ModelMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F2A05123A8325100D954D8 /* ModelMapping.swift */; };
0A42538F23F3414800554656 /* Codable+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A42538E23F3414800554656 /* Codable+Helpers.swift */; };
0ACC81A22613C73800A9C886 /* ActionContactModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ACC81A12613C73800A9C886 /* ActionContactModel.swift */; };
0AEBB84625FA75C000EA80EE /* ActionOpenSMSModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AEBB84525FA75C000EA80EE /* ActionOpenSMSModel.swift */; };
0AFF597A23FC6E60005C24E8 /* ActionShareModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF597923FC6E60005C24E8 /* ActionShareModel.swift */; };
30349BF11FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 30349BEF1FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
30349BF21FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 30349BF01FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m */; };
@ -70,7 +78,7 @@
AF43A5871FBB67D6008E9347 /* MVMCoreActionUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A5851FBB67D6008E9347 /* MVMCoreActionUtility.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF43A5881FBB67D6008E9347 /* MVMCoreActionUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A5861FBB67D6008E9347 /* MVMCoreActionUtility.m */; };
AF43A6F41FBCE21D008E9347 /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = AF43A6F31FBCE21D008E9347 /* libsqlite3.0.tbd */; };
AF43A6FD1FBE2F65008E9347 /* Reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A6FA1FBE2F2A008E9347 /* Reachability.h */; };
AF43A6FD1FBE2F65008E9347 /* Reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A6FA1FBE2F2A008E9347 /* Reachability.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF43A6FF1FBE3252008E9347 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A6FB1FBE2F2A008E9347 /* Reachability.m */; };
AF43A7011FC4B227008E9347 /* MVMCoreGlobalLoadProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A7001FC4B227008E9347 /* MVMCoreGlobalLoadProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF43A7061FC4D7A2008E9347 /* MVMCoreObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A7041FC4D7A2008E9347 /* MVMCoreObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -135,17 +143,29 @@
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 */; };
D268D82B26700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = D268D82926700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.h */; settings = {ATTRIBUTES = (Public, ); }; };
D268D82C26700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = D268D82A26700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.m */; };
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 */; };
D2DEDCB923C6400600C44CC4 /* UnitInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2DEDCB823C6400600C44CC4 /* UnitInterval.swift */; };
D2DEDCBB23C65BC300C44CC4 /* Percent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2DEDCBA23C65BC300C44CC4 /* Percent.swift */; };
D2E1FAD92260C3E400AEFD8C /* DelegateObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAD82260C3E400AEFD8C /* DelegateObject.swift */; };
EA3B264C25FC0B7600008074 /* ModelHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3B264B25FC0B7600008074 /* ModelHandlerProtocol.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
016CF36825FA6DD400B82A1F /* ClientParameterHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientParameterHandler.swift; sourceTree = "<group>"; };
016FF6ED259A4E6300F5E4AA /* ClientParameterModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientParameterModelProtocol.swift; sourceTree = "<group>"; };
016FF6F1259A4FCC00F5E4AA /* ClientParameterModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientParameterModel.swift; sourceTree = "<group>"; };
016FF6FB259BA27E00F5E4AA /* ClientParameterProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientParameterProtocol.swift; sourceTree = "<group>"; };
01934FE625A4FFC2003DCD67 /* ClientParameterActionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientParameterActionProtocol.swift; sourceTree = "<group>"; };
01C851CE23CF7B260021F976 /* JSONMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONMap.swift; sourceTree = "<group>"; };
01C851D023CF97FE0021F976 /* ActionBackModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBackModel.swift; sourceTree = "<group>"; };
01DB1F2A26444F7F000F1AF4 /* ActionOpenPageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionOpenPageProtocol.swift; sourceTree = "<group>"; };
01DF561321F90ADC00CC099B /* Dictionary+MFConvenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+MFConvenience.swift"; sourceTree = "<group>"; };
01F2A03523A80A7300D954D8 /* ActionModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionModelProtocol.swift; sourceTree = "<group>"; };
01F2A03823A812DD00D954D8 /* ActionOpenUrlModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionOpenUrlModel.swift; sourceTree = "<group>"; };
@ -154,6 +174,8 @@
0A11030B20864F94008ADD90 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
0A11030C20864F9A008ADD90 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Localizable.strings"; sourceTree = "<group>"; };
0A42538E23F3414800554656 /* Codable+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Codable+Helpers.swift"; sourceTree = "<group>"; };
0ACC81A12613C73800A9C886 /* ActionContactModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionContactModel.swift; sourceTree = "<group>"; };
0AEBB84525FA75C000EA80EE /* ActionOpenSMSModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionOpenSMSModel.swift; sourceTree = "<group>"; };
0AFF597923FC6E60005C24E8 /* ActionShareModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionShareModel.swift; sourceTree = "<group>"; };
30349BEF1FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreSessionTimeHandler.h; sourceTree = "<group>"; };
30349BF01FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreSessionTimeHandler.m; sourceTree = "<group>"; };
@ -266,12 +288,18 @@
AFFCFA631FCCC0D600FD0730 /* MVMCoreLoadingOverlayHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreLoadingOverlayHandler.m; sourceTree = "<group>"; };
AFFCFA641FCCC0D600FD0730 /* MVMCoreLoadingViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadingViewControllerProtocol.h; sourceTree = "<group>"; };
BB780ADE250F8C890030BD2F /* ActionNoopModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionNoopModel.swift; sourceTree = "<group>"; };
D268D82926700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewManagerViewControllerProtocolHelper.h; sourceTree = "<group>"; };
D268D82A26700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreViewManagerViewControllerProtocolHelper.m; sourceTree = "<group>"; };
D27073B625BB45C4001C7246 /* ActionActionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionActionsModel.swift; sourceTree = "<group>"; };
D27073CC25BB4CEF001C7246 /* MVMCoreActionHandler+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreActionHandler+Extension.swift"; sourceTree = "<group>"; };
D27073D025BB844B001C7246 /* MVMCoreActionDelegateProtocol+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreActionDelegateProtocol+Extension.swift"; sourceTree = "<group>"; };
D282AAB52240085300C46919 /* MVMCoreGetterUtility+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreGetterUtility+Extension.swift"; sourceTree = "<group>"; };
D282AAB72240342D00C46919 /* NSNumber+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNumber+Extension.swift"; sourceTree = "<group>"; };
D2DEDCB623C63F3B00C44CC4 /* Clamping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clamping.swift; sourceTree = "<group>"; };
D2DEDCB823C6400600C44CC4 /* UnitInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitInterval.swift; sourceTree = "<group>"; };
D2DEDCBA23C65BC300C44CC4 /* Percent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Percent.swift; sourceTree = "<group>"; };
D2E1FAD82260C3E400AEFD8C /* DelegateObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegateObject.swift; sourceTree = "<group>"; };
EA3B264B25FC0B7600008074 /* ModelHandlerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelHandlerProtocol.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -286,6 +314,18 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
016FF6EC259A4E3E00F5E4AA /* Client Parameters */ = {
isa = PBXGroup;
children = (
016FF6FB259BA27E00F5E4AA /* ClientParameterProtocol.swift */,
016FF6ED259A4E6300F5E4AA /* ClientParameterModelProtocol.swift */,
016FF6F1259A4FCC00F5E4AA /* ClientParameterModel.swift */,
016CF36825FA6DD400B82A1F /* ClientParameterHandler.swift */,
01934FE625A4FFC2003DCD67 /* ClientParameterActionProtocol.swift */,
);
path = "Client Parameters";
sourceTree = "<group>";
};
8876D5BF1FB50A9E00EB2E3D = {
isa = PBXGroup;
children = (
@ -376,6 +416,7 @@
946EE1A8237B5C650036751F /* Model */ = {
isa = PBXGroup;
children = (
EA3B264B25FC0B7600008074 /* ModelHandlerProtocol.swift */,
946EE1A2237B59C30036751F /* ModelProtocol.swift */,
946EE1A6237B5B1C0036751F /* ModelRegistry.swift */,
);
@ -405,8 +446,10 @@
946EE1B6237B66630036751F /* ActionType */ = {
isa = PBXGroup;
children = (
016FF6EC259A4E3E00F5E4AA /* Client Parameters */,
01F2A03523A80A7300D954D8 /* ActionModelProtocol.swift */,
946EE1BB237B691A0036751F /* ActionOpenPageModel.swift */,
01DB1F2A26444F7F000F1AF4 /* ActionOpenPageProtocol.swift */,
01F2A03823A812DD00D954D8 /* ActionOpenUrlModel.swift */,
01F2A04B23A82B1B00D954D8 /* ActionCallModel.swift */,
01C851D023CF97FE0021F976 /* ActionBackModel.swift */,
@ -416,6 +459,9 @@
94C014D424211AF0005811A9 /* ActionCancelModel.swift */,
94C014D824212360005811A9 /* ActionSettingModel.swift */,
BB780ADE250F8C890030BD2F /* ActionNoopModel.swift */,
D27073B625BB45C4001C7246 /* ActionActionsModel.swift */,
0AEBB84525FA75C000EA80EE /* ActionOpenSMSModel.swift */,
0ACC81A12613C73800A9C886 /* ActionContactModel.swift */,
);
path = ActionType;
sourceTree = "<group>";
@ -456,6 +502,8 @@
AF43A71A1FC5BEBB008E9347 /* MVMCoreViewControllerProtocol.h */,
AF43A71F1FC5D2BA008E9347 /* MVMCoreViewManagerProtocol.h */,
AF1201812108C9B400E2F592 /* MVMCoreViewManagerViewControllerProtocol.h */,
D268D82926700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.h */,
D268D82A26700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.m */,
AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */,
AFEEE8181FCDDE6B00B5EDD0 /* MVMCoreLoggingDelegateProtocol.h */,
AF43A7001FC4B227008E9347 /* MVMCoreGlobalLoadProtocol.h */,
@ -558,8 +606,10 @@
isa = PBXGroup;
children = (
AFBB96B51FBA3CEC0008D868 /* MVMCoreActionDelegateProtocol.h */,
D27073D025BB844B001C7246 /* MVMCoreActionDelegateProtocol+Extension.swift */,
AFBB96B61FBA3CEC0008D868 /* MVMCoreActionHandler.h */,
AFBB96B71FBA3CEC0008D868 /* MVMCoreActionHandler.m */,
D27073CC25BB4CEF001C7246 /* MVMCoreActionHandler+Extension.swift */,
);
path = ActionHandling;
sourceTree = "<group>";
@ -671,6 +721,7 @@
AF43A7011FC4B227008E9347 /* MVMCoreGlobalLoadProtocol.h in Headers */,
AF43A5771FBA5B7C008E9347 /* MVMCoreJSONConstants.h in Headers */,
AFBB96631FBA3A570008D868 /* MVMCoreLoadRequestOperation.h in Headers */,
D268D82B26700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.h in Headers */,
8876D5CE1FB50A9E00EB2E3D /* MVMCore.h in Headers */,
AF43A57B1FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.h in Headers */,
AFBB96B81FBA3CEC0008D868 /* MVMCoreActionDelegateProtocol.h in Headers */,
@ -774,7 +825,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "unset TOOLCHAINS #Xcode 7.3 BUG FIX http://stackoverflow.com/questions/36184930/xcodebuild-7-3-cant-enable-bitcode\n\n# define output folder environment variable\nC_PROJECT_NAME=\"MVMCore\"\n\nUNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal\n\n# Step 1. Build Device and Simulator versions\nxcodebuild -target \"${C_PROJECT_NAME}\" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\"\n\nxcodebuild -target \"${C_PROJECT_NAME}\" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 -arch x86_64 BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\"\n\nmkdir -p \"${UNIVERSAL_OUTPUTFOLDER}\"\n\nrm -rf ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.framework\ncp -R ${BUILD_DIR}/${CONFIGURATION}-iphoneos/${C_PROJECT_NAME}.framework ${UNIVERSAL_OUTPUTFOLDER}\n\n# Step 2. Create universal binary file using lipo\n\nlipo -create -output \"${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}\" \"${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.framework/${C_PROJECT_NAME}\" \"${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${C_PROJECT_NAME}.framework/${C_PROJECT_NAME}\"\n\nmv ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME} ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.framework/${C_PROJECT_NAME}";
shellScript = "unset TOOLCHAINS #Xcode 7.3 BUG FIX http://stackoverflow.com/questions/36184930/xcodebuild-7-3-cant-enable-bitcode\n\n# define output folder environment variable\nC_PROJECT_NAME=\"MVMCore\"\nPHONE_CONFIGURATION=\"Release\"\nSIMULATOR_CONFIGURATION=\"Debug\"\nSIMULATOR_LIBRARY_PATH=\"${BUILD_DIR}/${SIMULATOR_CONFIGURATION}-iphonesimulator/${C_PROJECT_NAME}.framework\"\nUNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/universal\n\n# Step 1. Build Device and Simulator versions\nxcodebuild -scheme \"${C_PROJECT_NAME}\" ONLY_ACTIVE_ARCH=NO -configuration ${PHONE_CONFIGURATION} -sdk iphoneos -archivePath \"${BUILD_DIR}/${PHONE_CONFIGURATION}-iphoneos/${C_PROJECT_NAME}\" archive SKIP_INSTALL=false\n\nxcodebuild -target \"${C_PROJECT_NAME}\" ONLY_ACTIVE_ARCH=NO -configuration ${SIMULATOR_CONFIGURATION} -sdk iphonesimulator -arch x86_64 BUILD_DIR=\"${BUILD_DIR}\" ALWAYS_SEARCH_USER_PATHS=true\n\nmkdir -p \"${UNIVERSAL_OUTPUTFOLDER}\"\n\nrm -rf ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.framework\ncp -R ${BUILD_DIR}/${PHONE_CONFIGURATION}-iphoneos/\"${C_PROJECT_NAME}\".xcarchive/Products/Library/Frameworks/${C_PROJECT_NAME}.framework ${UNIVERSAL_OUTPUTFOLDER}\n\n# Step 2. Create universal binary file using lipo\nlipo -create -output \"${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}\" \"${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.framework/${C_PROJECT_NAME}\" \"${SIMULATOR_LIBRARY_PATH}/${C_PROJECT_NAME}\"\n\nmv ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME} ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.framework/${C_PROJECT_NAME}\n\n# For Swift framework, Swiftmodule needs to be copied in the universal framework\nif [ -d \"${SIMULATOR_LIBRARY_PATH}/Modules/${C_PROJECT_NAME}.swiftmodule/\" ]; then\ncp -a \"${SIMULATOR_LIBRARY_PATH}/Modules/${C_PROJECT_NAME}.swiftmodule/\" \"${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.framework/Modules/${C_PROJECT_NAME}.swiftmodule/\"\nfi\n";
};
/* End PBXShellScriptBuildPhase section */
@ -813,19 +864,24 @@
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 */,
0ACC81A22613C73800A9C886 /* ActionContactModel.swift in Sources */,
AFBB96971FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.m in Sources */,
AF43A5781FBA5B7C008E9347 /* MVMCoreJSONConstants.m in Sources */,
AFBB96691FBA3A570008D868 /* MVMCoreRequestParameters.m in Sources */,
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 */,
016CF36925FA6DD400B82A1F /* ClientParameterHandler.swift 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 */,
@ -833,22 +889,30 @@
94C014D924212360005811A9 /* ActionSettingModel.swift in Sources */,
D2DEDCB723C63F3B00C44CC4 /* Clamping.swift in Sources */,
01DF561421F90ADC00CC099B /* Dictionary+MFConvenience.swift in Sources */,
EA3B264C25FC0B7600008074 /* ModelHandlerProtocol.swift in Sources */,
AFBB96B11FBA3B590008D868 /* MVMCoreDispatchUtility.m in Sources */,
946EE1A3237B59C30036751F /* ModelProtocol.swift in Sources */,
AFEA17A9209B6A1C00BC6740 /* MVMCoreBlockOperation.m in Sources */,
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 */,
D268D82C26700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.m in Sources */,
0AEBB84625FA75C000EA80EE /* ActionOpenSMSModel.swift in Sources */,
016FF6FC259BA27E00F5E4AA /* ClientParameterProtocol.swift in Sources */,
01DB1F2B26444F7F000F1AF4 /* ActionOpenPageProtocol.swift in Sources */,
8876D5F31FB50AB000EB2E3D /* UIFont+MFSpacing.m in Sources */,
D282AAB82240342D00C46919 /* NSNumber+Extension.swift in Sources */,
016FF6EE259A4E6300F5E4AA /* ClientParameterModelProtocol.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -906,6 +970,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@ -967,6 +1032,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -19,143 +19,98 @@ 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)getClientParameter:(nullable NSDictionary *)clientParametersMap requestParameters:(nullable NSDictionary *)requestParameters showLoadingOverlay:(BOOL)showLoadingOverlay 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
/// Opens Text Message
- (void)smsAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// 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.
+ (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// Sends the request to the load handler.
+ (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters actionInformation:(nullable NSDictionary *)actionInformation 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 <MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Tries to open a page
- (void)openPageAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// restarts the app
- (void)restartAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Goes back
- (void)backAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Makes a phone call
- (void)callAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Makes the previous request, needs the delegate for this
- (void)previousSubmitAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Redirects to another experience
- (void)redirectAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Cancels (like in a popup)
- (void)cancelAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Goes to settings app
- (void)settingsAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Handles action errors.
- (void)handleActionError:(nullable MVMCoreErrorObject *)error actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Links away to app or browser
- (void)linkAwayAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// opens the url in a webview.
- (void)openURLInWebView:(nullable NSURL *)url actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
// Sends the request to the load handler.
+ (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject<MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreActionDelegateProtocol>*)delegate __deprecated;
@end

View File

@ -26,13 +26,17 @@
#import "MVMCorePresentationDelegateProtocol.h"
#import <SafariServices/SafariServices.h>
#import <MVMCore/MVMCore-Swift.h>
#import "MVMCoreLoadingOverlayHandler.h"
#import <ContactsUI/ContactsUI.h>
NSString * const KeyActionType = @"actionType";
NSString * const KeyActionTypeLinkAway = @"openURL";
NSString * const KeyActionTypeOpen = @"openPage";
@implementation MVMCoreActionHandler
@interface MVMCoreActionHandler() <CNContactViewControllerDelegate, CNContactPickerDelegate>
@end
@implementation MVMCoreActionHandler
+ (nullable instancetype)sharedActionHandler {
return [MVMCoreActionUtility initializerClassCheck:[MVMCoreObject sharedInstance].actionHandler classToVerify:self];
}
@ -44,50 +48,103 @@ 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:KeyActionTypeSMS]) {
[self smsAction:actionInformation additionalData:additionalData delegateObject:delegateObject];
} else if ([actionType isEqualToString:KeyActionTypeContact]) {
[self contactAction: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)getClientParameter:(nullable NSDictionary *)clientParametersMap requestParameters:(nullable NSDictionary *)requestParameters showLoadingOverlay:(BOOL)showLoadingOverlay completionHandler:(nonnull void (^)(NSDictionary * _Nullable parameters))completionHandler {
if (!clientParametersMap) {
completionHandler(nil);
return;
}
if (showLoadingOverlay) {
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] startLoading];
}
void (^stopLoadingOverlay)(void) = ^(void) {
if (showLoadingOverlay) {
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:true];
}
};
NSError *error = nil;
[MVMCoreLoggingHandler logDebugMessageWithDelegate:@"Fetching client parameters"];
ClientParameterHandler *clientParameterHandler = [[ClientParameterHandler alloc] init];
[clientParameterHandler getParametersWith:clientParametersMap
requestParameters:requestParameters
error:&error
completionHandler:^(NSDictionary<NSString *,id> * _Nullable clientParameters) {
[MVMCoreLoggingHandler logDebugMessageWithDelegate:@"Finshed fetching client parameters"];
if (clientParameters) {
stopLoadingOverlay();
completionHandler(clientParameters);
} else {
[MVMCoreLoggingHandler logDebugMessageWithDelegate:@"No client parameters"];
stopLoadingOverlay();
completionHandler(nil);
}
}];
if (error) {
stopLoadingOverlay();
completionHandler(nil);
[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 +164,25 @@ 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 ([delegateObject.actionDelegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) {
[delegateObject.actionDelegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:additionalData];
} else {
[MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters additionalData:additionalData delegateObject:delegateObject];
}
}];
} else {
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;
}
MVMCoreRequestParameters *requestParameters = [[MVMCoreRequestParameters alloc] initWithActionMap:actionInformation];
[self 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
actionInformation:actionInformation
additionalData:additionalData
delegateObject:delegateObject];
}
}];
}
- (void)shareAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
@ -202,6 +262,75 @@ NSString * const KeyActionTypeOpen = @"openPage";
}
}
- (void)smsAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
NSString *phoneNumber = [actionInformation stringForKey:@"phoneNumber"];
NSString *message = [actionInformation stringForKey:KeyMessage];
NSString *smsQuery = [NSString stringWithFormat:@"sms:%@&body=%@", phoneNumber, message];
[MVMCoreActionUtility linkAway:[smsQuery stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] appURLString:nil];
}
- (void)contactAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
__weak typeof(self) weakSelf = self;
NSString *phoneNumber = [actionInformation string:@"phoneNumber"];
if (!phoneNumber) { return; }
CNMutableContact *contact = [[CNMutableContact alloc] init];
NSString *approach = [actionInformation stringForKey:@"approach"];
CNLabeledValue *phone = [[CNLabeledValue alloc] initWithLabel:CNLabelOther value:[[CNPhoneNumber alloc] initWithStringValue:phoneNumber]];
contact.phoneNumbers = @[phone];
if ([approach isEqualToString:KeyAdd]) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
CNContactPickerViewController *controller = [[CNContactPickerViewController alloc] init];
// Setting to accessibilityValue as a workaround to pass data via the delegate function.
[controller.view setAccessibilityIdentifier:phoneNumber];
controller.delegate = weakSelf;
[[MVMCoreNavigationHandler sharedNavigationHandler] presentViewController:controller animated:YES];
}];
} else if ([approach isEqualToString:KeyCreate]) {
contact.givenName = [actionInformation string:@"firstName"];
contact.familyName = [actionInformation string:@"lastName"];
[MVMCoreDispatchUtility performBlockOnMainThread:^{
CNContactStore *store = [[CNContactStore alloc] init];
CNContactViewController *controller = [CNContactViewController viewControllerForNewContact:contact];
controller.contactStore = store;
controller.delegate = weakSelf;
[[MVMCoreNavigationHandler sharedNavigationHandler] pushViewController:controller animated:YES];
}];
} else if ([approach isEqualToString:KeyView]) {
NSCharacterSet *symbols = [NSCharacterSet characterSetWithCharactersInString:@".+()-  "];
NSString *contactPhoneNumber = [actionInformation string:@"phoneNumber"];
contactPhoneNumber = [[contactPhoneNumber componentsSeparatedByCharactersInSet:symbols] componentsJoinedByString:@""];
CNPhoneNumber *number = [[CNPhoneNumber alloc] initWithStringValue:contactPhoneNumber];
NSPredicate *contactPredicate = [CNContact predicateForContactsMatchingPhoneNumber:number];
NSArray *displayedKeys = @[[CNContactViewController descriptorForRequiredKeys]];
CNContactStore *store = [[CNContactStore alloc] init];
NSError *error;
CNContact *viewContact = [[store unifiedContactsMatchingPredicate:contactPredicate keysToFetch:displayedKeys error:&error] firstObject];
if (viewContact) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
CNContactViewController *controller = [CNContactViewController viewControllerForNewContact:viewContact];
controller.contactStore = store;
controller.delegate = weakSelf;
[[MVMCoreNavigationHandler sharedNavigationHandler] pushViewController:controller animated:YES];
}];
} else {
// No contacts found, show an alert
MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorContactUnAvailable] code:ErrorCodeDefault domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@_%@",NSStringFromClass([delegateObject.actionDelegate class]),KeyActionTypeContact]];
error.silentError = NO;
[self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject];
}
}
}
- (void)callAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
// Call
NSString *callNumber = [actionInformation stringForKey:KeyCallNumber];
@ -219,7 +348,10 @@ NSString * const KeyActionTypeOpen = @"openPage";
if ([delegateObject.actionDelegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) {
[delegateObject.actionDelegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:dataForPage];
} else {
[MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters additionalData:additionalData delegateObject:delegateObject];
[MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters
actionInformation:actionInformation
additionalData:additionalData
delegateObject:delegateObject];
}
}];
}];
@ -241,6 +373,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;
}
@ -263,26 +408,82 @@ NSString * const KeyActionTypeOpen = @"openPage";
}
}
#pragma mark - open url functions
#pragma mark - CNContactViewControllerDelegate
- (void)contactViewController:(CNContactViewController *)viewController didCompleteWithContact:(CNContact *)contact {
[[MVMCoreNavigationHandler sharedNavigationHandler] removeCurrentViewController];
}
- (BOOL)contactViewController:(CNContactViewController *)viewController shouldPerformDefaultActionForContactProperty:(CNContactProperty *)property {
return YES;
}
#pragma mark - CNContactPickerDelegate
- (void)contactPickerDidCancel:(CNContactPickerViewController *)picker {
[[MVMCoreNavigationHandler sharedNavigationHandler] removeCurrentViewController];
}
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact {
// This is a means to pass the data to this delegate function.
NSString *phoneNumber = picker.view.accessibilityIdentifier;
if (!phoneNumber) { return; }
CNContactStore *store = [[CNContactStore alloc] init];
CNMutableContact *existingContact = [(CNMutableContact *)contact mutableCopy];
CNPhoneNumber *number = [[CNPhoneNumber alloc] initWithStringValue:phoneNumber];
CNLabeledValue *labelValue = [[CNLabeledValue alloc] initWithLabel:CNLabelOther value:number];
NSMutableArray<CNLabeledValue *> *phoneNumbers = [NSMutableArray new];
[phoneNumbers addObject:labelValue];
[phoneNumbers addObjectsFromArray:existingContact.phoneNumbers];
existingContact.phoneNumbers = phoneNumbers;
__weak typeof(self) weakSelf = self;
[MVMCoreDispatchUtility performBlockOnMainThread:^{
CNContactViewController *controller = [CNContactViewController viewControllerForNewContact:existingContact];
controller.contactStore = store;
controller.delegate = weakSelf;
[[MVMCoreNavigationHandler sharedNavigationHandler] pushViewController:controller animated:YES];
}];
}
#pragma mark - Open URL
- (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];
}
__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];
};
// 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];
[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 {
@ -318,7 +519,7 @@ NSString * const KeyActionTypeOpen = @"openPage";
} 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([delegateObject.actionDelegate class]),KeyActionTypeLinkAway]];
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];
}];
}
@ -335,14 +536,26 @@ NSString * const KeyActionTypeOpen = @"openPage";
[[MVMCoreNavigationHandler sharedNavigationHandler] presentViewController:safariViewController animated:YES];
}
#pragma mark - Default Action Protocol Functions
#pragma mark - Default Action Protocol
+ (void)defaultLogAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject{
// Currently no default log action but this will eventually be server driven.
}
+ (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
[[MVMCoreLoadHandler sharedGlobal] loadRequest:requestParameters dataForPage:additionalData delegateObject:delegateObject];
+ (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
NSDictionary *clientParamters = [actionInformation dict:KeyClientParameters];
if (clientParamters) {
[[MVMCoreActionHandler sharedActionHandler] getClientParameter:clientParamters
requestParameters: requestParameters.parameters
showLoadingOverlay: !requestParameters.backgroundRequest
completionHandler: ^(NSDictionary * _Nullable jsonDictionary) {
[requestParameters addRequestParameters:jsonDictionary];
[[MVMCoreLoadHandler sharedGlobal] loadRequest:requestParameters dataForPage:additionalData delegateObject:delegateObject];
}];
} else {
[[MVMCoreLoadHandler sharedGlobal] loadRequest:requestParameters dataForPage:additionalData delegateObject:delegateObject];
}
}
+ (void)defaultHandleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
@ -363,248 +576,11 @@ NSString * const KeyActionTypeOpen = @"openPage";
- (void)handleActionWithDictionary:(nullable NSDictionary *)dictionary additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate {
// Have delegate redirect.
[[MVMCoreSessionObject sharedGlobal] redirectWithInfo:actionInformation];
}
- (void)cancelAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate {
if ([delegate respondsToSelector:@selector(handleCancel:additionalData:)]) {
[delegate handleCancel:actionInformation additionalData:additionalData];
}
}
- (void)settingsAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate {
return NO;
}
- (void)unknownAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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<MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)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 <MVMCoreActionDelegateProtocol>*)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

View File

@ -15,3 +15,4 @@ extern NSString * const HardcodedOK;
extern NSString * const HardcodedErrorCritical;
extern NSString * const HardcodedErrorUnableToProcess;
extern NSString * const HardcodedErrorContactUnAvailable;

View File

@ -15,3 +15,4 @@ NSString * const HardcodedOK = @"okCaps";
NSString * const HardcodedErrorCritical = @"Error Message Critical Key";
NSString * const HardcodedErrorUnableToProcess = @"Error Message Unable To Process Request Key";
NSString * const HardcodedErrorContactUnAvailable = @"Contact not available";

View File

@ -24,6 +24,11 @@ extern NSString * const KeyPageMap;
extern NSString * const KeySystemParameters;
extern NSString * const KeyButtonMap;
extern NSString * const KeyOpenSupport;
extern NSString * const KeyPostAction;
extern NSString * const KeyAdd;
extern NSString * const KeyCreate;
extern NSString * const KeyView;
extern NSString * const KeyLinks;
extern NSString * const KeyTitle;
@ -33,16 +38,20 @@ extern NSString * const KeyActionTypeBack;
extern NSString * const KeyActionTypeShare;
extern NSString * const KeyShareType;
extern NSString * const KeyShareText;
extern NSString * const KeyActionTypeSMS;
extern NSString * const KeyActionTypeContact;
extern NSString * const KeyActionTypeCall;
extern NSString * const KeyActionTypePreviousSubmit;
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;
extern NSString * const KeyCallNumber;
extern NSString * const KeyPhoneNumber;
extern NSString * const KeyPresentationStyle;
extern NSString * const KeyExtraParameters;
extern NSString * const KeyContextRoot;
@ -50,6 +59,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

View File

@ -26,6 +26,11 @@ NSString * const KeyPageMap = @"PageMap";
NSString * const KeySystemParameters = @"SystemParams";
NSString * const KeyButtonMap = @"ButtonMap";
NSString * const KeyOpenSupport = @"openSupport";
NSString * const KeyPostAction = @"PostAction";
NSString * const KeyAdd = @"add";
NSString * const KeyCreate = @"create";
NSString * const KeyView = @"view";
NSString * const KeyLinks = @"Links";
NSString * const KeyTitle = @"title";
@ -34,15 +39,19 @@ NSString * const KeyActionTypeRestart = @"restart";
NSString * const KeyActionTypeBack = @"back";
NSString * const KeyActionTypeShare = @"share";
NSString * const KeyActionTypeCall = @"call";
NSString * const KeyActionTypeSMS = @"sms";
NSString * const KeyActionTypeContact = @"contact";
NSString * const KeyActionTypePreviousSubmit = @"previousSubmit";
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";
NSString * const KeyCallNumber = @"callNumber";
NSString * const KeyPhoneNumber = @"phoneNumber";
NSString * const KeyPresentationStyle = @"presentationStyle";
NSString * const KeyExtraParameters = @"extraParameters";
NSString * const KeyContextRoot = @"appContext";
@ -50,6 +59,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

View File

@ -110,34 +110,32 @@
- (nullable NSURLRequest *)requestWithParameters:(nonnull MVMCoreRequestParameters *)requestParameters error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error {
NSURL *url = nil;
if (requestParameters.alternateBaseURL) {
url = requestParameters.alternateBaseURL;
} else {
url = [MVMCoreSessionObject sharedGlobal].baseURL;
}
NSURL *url = requestParameters.URL;
if (!url) {
url = [NSURL URLWithString:URLProdPostpayBase];
if (requestParameters.alternateBaseURL) {
url = requestParameters.alternateBaseURL;
} else {
url = [MVMCoreSessionObject sharedGlobal].baseURL ?: [NSURL URLWithString:URLProdPostpayBase];
}
// Appends the context root.
if (requestParameters.contextRoot) {
url = [url URLByAppendingPathComponent:requestParameters.contextRoot];
} else if ([MVMCoreSessionObject sharedGlobal].contextRoot) {
url = [url URLByAppendingPathComponent:[MVMCoreSessionObject sharedGlobal].contextRoot];
}
// Appends the page type
if (requestParameters.pageType) {
url = [url URLByAppendingPathComponent:requestParameters.pageType];
}
// This has changed since the initial agreement. Seems server always needs page type now.
/* else if (requestParameters.modules) {
url = [url URLByAppendingPathComponent:KeyModuleMap];
}*/
}
// Appends the context root.
if (requestParameters.contextRoot) {
url = [url URLByAppendingPathComponent:requestParameters.contextRoot];
} else if ([MVMCoreSessionObject sharedGlobal].contextRoot) {
url = [url URLByAppendingPathComponent:[MVMCoreSessionObject sharedGlobal].contextRoot];
}
// Appends the page type
if (requestParameters.pageType) {
url = [url URLByAppendingPathComponent:requestParameters.pageType];
}
// This has changed since the initial agreement. Seems server always needs page type now.
/* else if (requestParameters.modules) {
url = [url URLByAppendingPathComponent:KeyModuleMap];
}*/
// Adds modules needed to the request parameters.
if (requestParameters.modules.count > 0) {
[requestParameters addRequestParameters:@{KeyModuleList:requestParameters.modules}];

View File

@ -26,6 +26,9 @@
@property (nonatomic) BOOL backgroundLoad;
@property (nonatomic, getter=areDependenciesAdded) BOOL dependenciesAdded;
/// Legacy flag for if this operation will have an alert to show when finished.
@property (nonatomic, readonly) BOOL alertToShow;
// Initializes the operation with the request parameters object, data for page, and mvm view controller to handle the loading with.
- (nullable instancetype)initWithRequestParameters:(nullable MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegateObject:(nullable DelegateObject *)delegateObject backgroundLoad:(BOOL)backgroundLoad;

View File

@ -34,7 +34,7 @@
@property (weak, nonatomic) NSURLSessionTask *sessionTask;
// For temporarily storing any alert to show until we determine it's delegate.
@property (nonatomic) BOOL alertToShow;
@property (nonatomic, readwrite) BOOL alertToShow;
@property (strong, nonatomic, nullable) MVMCoreErrorObject *errorForAlertToShow;
@end
@ -149,7 +149,6 @@
if (!requestForMissingData) {
// We have all the needed data, continue with the load.
[[MVMCoreSessionTimeHandler sharedSessionHandler] sendKeepAliveToServer:NO];
[MVMCoreLoadRequestOperation handleLoadObject:loadObject error:nil];
} else {
@ -167,8 +166,15 @@
// Can continue loading with the page.
[MVMCoreLoadRequestOperation handleLoadObject:loadObject error:error];
} else {
} else if (loadObject.operation.backgroundLoad || loadObject.requestParameters.noViewControllerToLoad || ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(hasContentToShow:error:)] && [[MVMCoreObject sharedInstance].globalLoadDelegate hasContentToShow:loadObject error:error])) {
// Something to show, or nothing was expected to show, can finish.
[MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:nil errorObject:error];
} else {
// Error, foreground request with no page, alert, action, or anything else to show. Abort with error.
MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeNoPageType domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
[MVMCoreLoadRequestOperation loadAbortedWithError:error loadObject:loadObject];
}
}];
}];
@ -185,29 +191,32 @@
// Never use the cache.
completionHandler(nil,nil);
} else if (requestParameters.pageType.length != 0 && requestParameters.modules.count > 0) {
} else {
NSArray *modules = [requestParameters allModules];
if (requestParameters.pageType.length != 0 && modules.count > 0) {
// Check cache function for page json is already in the cache.
[[MVMCoreCache sharedCache] fetchJSONForPageType:requestParameters.pageType completionHandler:^(NSDictionary * _Nullable jsonDictionary) {
// Check cache function for page json is already in the cache.
[[MVMCoreCache sharedCache] fetchJSONForPageType:requestParameters.pageType completionHandler:^(NSDictionary * _Nullable jsonDictionary) {
// Check cache function for modules already in the cache.
NSDictionary *pageDictionary = jsonDictionary;
[[MVMCoreCache sharedCache] fetchJSONForModules:requestParameters.modules completionHandler:^(NSDictionary * _Nullable jsonDictionary) {
completionHandler(pageDictionary,jsonDictionary);
// Check cache function for modules already in the cache.
NSDictionary *pageDictionary = jsonDictionary;
[[MVMCoreCache sharedCache] fetchJSONForModules:modules completionHandler:^(NSDictionary * _Nullable jsonDictionary) {
completionHandler(pageDictionary,jsonDictionary);
}];
}];
}];
} else if (requestParameters.pageType.length != 0) {
} else if (requestParameters.pageType.length != 0) {
// Check cache function if page json is already in the cache.
[[MVMCoreCache sharedCache] fetchJSONForPageType:requestParameters.pageType completionHandler:^(NSDictionary * _Nullable jsonDictionary) {
completionHandler(jsonDictionary,nil);
}];
} else if (requestParameters.modules.count > 0) {
// Check cache function if modules already in the cache.
[[MVMCoreCache sharedCache] fetchJSONForModules:requestParameters.modules completionHandler:^(NSDictionary * _Nullable jsonDictionary) {
completionHandler(nil,jsonDictionary);
}];
// Check cache function if page json is already in the cache.
[[MVMCoreCache sharedCache] fetchJSONForPageType:requestParameters.pageType completionHandler:^(NSDictionary * _Nullable jsonDictionary) {
completionHandler(jsonDictionary,nil);
}];
} else if (modules > 0) {
// Check cache function if modules already in the cache.
[[MVMCoreCache sharedCache] fetchJSONForModules:modules completionHandler:^(NSDictionary * _Nullable jsonDictionary) {
completionHandler(nil,jsonDictionary);
}];
}
}
}
@ -233,16 +242,21 @@
// Check if the page was found in the cache.
BOOL pageNotInCache = loadObject.operation.requestParameters.pageType && !loadObject.pageDataFromCache;
// Check all modules were found in the cache
// Check all required modules were found in the cache
NSMutableArray *modulesNotInCache = [NSMutableArray array];
for (NSString *module in loadObject.operation.requestParameters.modules) {
NSMutableArray *requiredModules = [NSMutableArray arrayWithArray:loadObject.operation.requestParameters.modules];
for (NSString *module in [loadObject.operation.requestParameters allModules]) {
if (![loadObject.modulesJSON objectForKey:module]) {
// Missing a module.
[modulesNotInCache addObject:module];
} else {
// Received a required module.
[requiredModules removeObject:module];
}
}
MVMCoreRequestParameters *requestParametersForServer = nil;
if (pageNotInCache || modulesNotInCache.count > 0) {
if (pageNotInCache || requiredModules.count > 0) {
// We are missing data, will need to go to server.
requestParametersForServer = [loadObject.operation.requestParameters copy];

View File

@ -29,16 +29,23 @@ typedef NS_ENUM(NSInteger, MFLoadStyle) {
// request parameters
@property (nullable, strong, nonatomic) NSString *pageType;
@property (nullable, strong, nonatomic) NSArray *modules;
@property (nullable, strong, nonatomic) NSArray<NSString *> *optionalModules;
@property (nullable, strong, nonatomic) NSArray<NSString *> *modules;
@property (nullable, strong, nonatomic) NSDictionary *parameters;
@property (nullable, strong, nonatomic) NSData *imageData;
// adding parent pageType for freebee
@property (nullable, strong, nonatomic) NSString *parentPageType;
// Context root for server
/// App context to use when building the url. For ex: baseURL/appContext/pageType
@property (nullable, strong, nonatomic) NSString *contextRoot;
/// Base URL to use when building the url. For ex: baseURL/appContext/pageType
@property (nullable, strong, nonatomic) NSURL *alternateBaseURL;
/// Entire URL to use instead of building the url
@property (nullable, strong, nonatomic) NSURL *URL;
// A flag for if you do not want to try loading any actual view controller. (Unless there is an error screen)
@property (assign, nonatomic) BOOL noViewControllerToLoad;
@ -82,9 +89,6 @@ typedef NS_ENUM(NSInteger, MFLoadStyle) {
// If the request was created with an action map.
@property (nullable, strong, nonatomic) NSDictionary *actionMap;
// only used when the base url is not the same as mf
@property (nullable, strong, nonatomic) NSURL *alternateBaseURL;
@property (nullable, strong, nonatomic) NSNumber *customTimeoutTime;
// Will open support panel at the end of the load.
@ -117,4 +121,7 @@ typedef NS_ENUM(NSInteger, MFLoadStyle) {
// Sets the load style based on the server sent presentationStyle
- (void)setMFLoadStyleBasedOnPresentationStyle:(nonnull NSString *)presentationStyle;
/// Returns optional and required modules
- (nullable NSArray<NSString *> *)allModules;
@end

View File

@ -33,7 +33,8 @@
- (nullable instancetype)initWithPageType:(nonnull NSString *)pageType extraParameters:(nullable NSDictionary *)extraParameters {
if (self = [self initWithExtraParameters:extraParameters]) {
self.pageType = pageType;
self.modules = [[MVMCoreViewControllerMappingObject sharedViewControllerMappingObject] allModulesForPageType:pageType];
self.optionalModules = [[MVMCoreViewControllerMappingObject sharedViewControllerMappingObject] modulesOptionalForPageType:pageType];
self.modules = [[MVMCoreViewControllerMappingObject sharedViewControllerMappingObject] modulesRequiredForPageType:pageType];
}
return self;
}
@ -66,6 +67,14 @@
if (self = [self initWithPageType:[actionMap stringForKey:KeyPageType] additionalModules:[actionMap array:KeyModuleList] extraParameters:[actionMap dict:KeyExtraParameters]]) {
self.contextRoot = [actionMap string:KeyContextRoot];
NSString *alternateBase = [actionMap string:@"baseURL"];
if (alternateBase) {
self.alternateBaseURL = [NSURL URLWithString:alternateBase];
}
NSString *url = [actionMap string:@"requestURL"];
if (url) {
self.URL = [NSURL URLWithString:url];
}
self.actionMap = actionMap;
self.customTimeoutTime = [actionMap optionalNumberForKey:@"customTimeoutTime"];
self.openSupportPanel = [actionMap boolForKey:KeyOpenSupport];
@ -108,12 +117,18 @@
}
}
- (nullable NSArray *)allModules {
NSMutableSet *set = [NSMutableSet setWithArray:self.optionalModules];
[set addObjectsFromArray:self.modules];
return [set allObjects];
}
- (id)copyWithZone:(nullable NSZone *)zone {
MVMCoreRequestParameters *copyObject = [[MVMCoreRequestParameters alloc] init];
copyObject.pageType = [self.pageType copy];
copyObject.parentPageType = self.parentPageType;
copyObject.optionalModules = [self.optionalModules copy];
copyObject.modules = [self.modules copy];
copyObject.parameters = [self.parameters copy];
copyObject.contextRoot = [self.contextRoot copy];
@ -136,6 +151,7 @@
copyObject.imageData = self.imageData;
copyObject.customTimeoutTime = self.customTimeoutTime;
copyObject.backgroundRequest = self.backgroundRequest;
copyObject.URL = self.URL;
return copyObject;
}

View File

@ -36,6 +36,7 @@ FOUNDATION_EXPORT const unsigned char MVMCoreVersionString[];
#import <MVMCore/MVMCoreOperation.h>
#import <MVMCore/MVMCoreObject.h>
#import <MVMCore/MVMCoreBlockOperation.h>
#import <MVMCore/Reachability.h>
// Mapping
#import <MVMCore/MVMCoreViewControllerMappingObject.h>
@ -73,6 +74,7 @@ FOUNDATION_EXPORT const unsigned char MVMCoreVersionString[];
#import <MVMCore/MVMCoreViewControllerProtocol.h>
#import <MVMCore/MVMCoreViewManagerProtocol.h>
#import <MVMCore/MVMCoreViewManagerViewControllerProtocol.h>
#import <MVMCore/MVMCoreViewManagerViewControllerProtocolHelper.h>
#import <MVMCore/MVMCoreViewProtocol.h>
#import <MVMCore/MVMCoreLoggingDelegateProtocol.h>

View File

@ -38,4 +38,7 @@
// Shows the appropriate alert style for the given response info and/or error.
- (void)createAndShowAlertForLoadObject:(nullable MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error delegateObject:(nullable DelegateObject *)delegateObject;
/// Checks to see if the operation has content to show.
- (BOOL)hasContentToShow:(nonnull MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error;
@end

View File

@ -16,7 +16,7 @@
@property (nullable, strong, nonatomic) NSString *pageType;
// This view controller should subclass this function and check the load to make sure it has all the needed data. Fills the error object if there are any errors. Returns if we should finish the load or not. Ideally error should use code ErrorCodeViewControllerProcessingJSON.
- (BOOL)shouldFinishProcessingLoad:(nonnull MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error;
- (BOOL)shouldFinishProcessingLoad:(nonnull MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nullable *_Nonnull)error;
@optional

View File

@ -12,9 +12,20 @@
@protocol MVMCoreViewManagerProtocol
// Return the current view controller showing
- (nonnull UIViewController *)getCurrentViewController;
- (nullable UIViewController *)getCurrentViewController;
// Returns if the manage currently contains the page with the page type.
- (BOOL)containsPageWithPageType:(nullable NSString *)pageType;
@optional
/// Notifies the manager that the controller received new data.
- (void)newDataReceivedInViewController:(nonnull UIViewController *)viewController;
/// Call on a manager when a view controller will be displayed. (Mostly called by other managers)
- (void)willDisplayViewController:(nonnull UIViewController *)viewController;
/// Call on a manager when a controller is displayed. (Mostly called by other managers)
- (void)displayedViewController:(nonnull UIViewController *)viewController;
@end

View File

@ -0,0 +1,22 @@
//
// MVMCoreViewManagerViewControllerProtocolHelper.h
// MVMCore
//
// Created by Scott Pfeil on 6/8/21.
// Copyright © 2021 myverizon. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <MVMCore/MVMCoreViewManagerProtocol.h>
#import <MVMCore/MVMCoreViewManagerViewControllerProtocol.h>
NS_ASSUME_NONNULL_BEGIN
@interface MVMCoreViewManagerViewControllerProtocolHelper : NSObject
/// Set the manager on the view controller. This function is a helper for swift because of the objc protocol
+ (void)helpSetManager:(nonnull UIViewController <MVMCoreViewManagerProtocol>*)manager viewController:(nullable UIViewController <MVMCoreViewManagerViewControllerProtocol>*)viewController;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,17 @@
//
// MVMCoreViewManagerViewControllerProtocolHelper.m
// MVMCore
//
// Created by Scott Pfeil on 6/8/21.
// Copyright © 2021 myverizon. All rights reserved.
//
#import "MVMCoreViewManagerViewControllerProtocolHelper.h"
@implementation MVMCoreViewManagerViewControllerProtocolHelper
+ (void)helpSetManager:(nonnull UIViewController <MVMCoreViewManagerProtocol>*)manager viewController:(nullable UIViewController <MVMCoreViewManagerViewControllerProtocol>*)viewController {
viewController.manager = manager;
}
@end

View File

@ -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)
}
}

View File

@ -0,0 +1,39 @@
//
// ActionContactModel.swift
// MVMCore
//
// Created by Kevin Christiano on 3/30/21.
// Copyright © 2021 myverizon. All rights reserved.
//
import ContactsUI
@objcMembers public class ActionContactModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "contact"
public var actionType: String = ActionContactModel.identifier
public var phoneNumber: String
public var firstName: String?
public var lastName: String?
public var approach: String = KeyCreate
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(phoneNumber: String, firstName: String? = nil, lastName: String? = nil, approach: String = KeyCreate, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.phoneNumber = phoneNumber
self.firstName = firstName
self.lastName = lastName
self.approach = approach
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
}

View File

@ -12,12 +12,18 @@ public protocol ActionModelProtocol: ModelProtocol {
var actionType: String { get }
var extraParameters: JSONValueDictionary? { get set }
var analyticsData: JSONValueDictionary? { get set }
var accessibilityIdentifier: String? { get set }
}
public extension ActionModelProtocol {
var actionType: String {
get { return Self.identifier }
get { Self.identifier }
}
var accessibilityIdentifier: String? {
get { nil }
set { }
}
static var categoryCodingKey: String {

View File

@ -7,7 +7,7 @@
//
@objcMembers open class ActionOpenPageModel: ActionModelProtocol {
@objcMembers open class ActionOpenPageModel: ActionModelProtocol, ActionOpenPageProtocol, ClientParameterActionProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -15,12 +15,16 @@
public static var identifier: String = "openPage"
public var actionType: String = ActionOpenPageModel.identifier
public var pageType: String
public var baseURL: String?
public var appContext: String?
public var requestURL: String?
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
public var presentationStyle: String?
public var tabBarIndex: Int?
public var background: Bool?
public var clientParameters: ClientParameterModel?
//--------------------------------------------------
// MARK: - Initialzier
//--------------------------------------------------

View File

@ -0,0 +1,14 @@
//
// ActionOpenPageProtocol.swift
// MVMCore
//
// Created by Suresh, Kamlesh on 5/6/21.
// Copyright © 2021 myverizon. All rights reserved.
//
import Foundation
public protocol ActionOpenPageProtocol: ModelProtocol {
var pageType: String { get set }
}

View File

@ -0,0 +1,33 @@
//
// ActionOpenSMSModel.swift
// MVMCore
//
// Created by Kevin Christiano on 3/11/21.
// Copyright © 2021 myverizon. All rights reserved.
//
@objcMembers public class ActionOpenSMSModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "sms"
public var actionType: String = ActionOpenSMSModel.identifier
public var phoneNumber: String
public var message: String?
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(phoneNumber: String, message: String?, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.phoneNumber = phoneNumber
self.message = message
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
}

View File

@ -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

View File

@ -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 }
}

View File

@ -0,0 +1,112 @@
//
// ClientParameterHandler.swift
// MVMCore
//
// Created by Suresh, Kamlesh on 3/11/21.
// Copyright © 2021 myverizon. All rights reserved.
//
@objcMembers open class ClientParameterHandler: NSObject {
var parameterHandlerList: [ClientParameterProtocol] = []
let parametersWorkQueue = DispatchQueue(label: "com.mva.clientparameter")
let group = DispatchGroup()
open func createParametersHandler(_ clientParameterModel: ClientParameterModelProtocol) -> ClientParameterProtocol? {
do {
let parameterType = try ModelRegistry.getHandler(clientParameterModel) as! ClientParameterProtocol.Type
return parameterType.init(clientParameterModel)
} catch {
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: #function) {
MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
}
return nil
}
}
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], requestParameters: [String: Any], completionHandler:@escaping ([String: Any]?) -> ()) throws {
guard let clientParameterModel = try getClientParameterModel(clientParameters) else {
completionHandler(nil)
return
}
let timeout = clientParameterModel.timeout ?? 30.0
// Dispatch setup on queue to ensure setup is complete before completion callbacks.
// Don't use [weak self]. Object is deallocated in the dispatch queue.
parametersWorkQueue.async(group: group, qos: .userInitiated) {
// Create the handler list so that same object can be used when merging. Merging needs default value in case of timeout
for parameterModel in clientParameterModel.list {
if let parameterHandler = self.createParametersHandler(parameterModel) {
self.parameterHandlerList.append(parameterHandler)
}
}
var returnedList = [[String: AnyHashable]?](repeating: nil, count: self.parameterHandlerList.count)
var mergedParametersList: [String: AnyHashable] {
var parametersList: [String: AnyHashable] = [:]
for (index, item) in returnedList.enumerated() {
let parameter = item ?? self.parameterHandlerList[index].valueOnTimeout()
parametersList = parametersList.merging(parameter) { (_, new) in new }
}
return parametersList
}
// Setup completion handlers. Barriered to ensure one happens after the other.
var complete = false
let timeoutWorkItem = DispatchWorkItem(qos: .userInitiated) {
completionHandler(mergedParametersList);
complete = true
}
let completionWorkItem = DispatchWorkItem(qos: .userInitiated) {
timeoutWorkItem.cancel()
if !complete { // In the case of firing after timeout.
completionHandler(mergedParametersList);
complete = true
}
}
// Setup timeout.
self.parametersWorkQueue.asyncAfter(deadline: .now() + .seconds(Int(timeout)), execute: timeoutWorkItem)
// Setup the parameter execution.
for (index, parameterHandler) in self.parameterHandlerList.enumerated() {
self.group.enter()
parameterHandler.fetchClientParameters(requestParameters: requestParameters,
timingOutIn: timeout) { (receivedParameter) in
// Queue the results for merge.
self.parametersWorkQueue.async {
returnedList[index] = receivedParameter
self.group.leave() // Leaving is only done after setup (barriered).
}
}
}
// Callback when all parameters have been merged.
self.group.notify(queue: self.parametersWorkQueue, work: completionWorkItem);
}
}
}

View File

@ -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)
}
}

View File

@ -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)"
}
}

View File

@ -0,0 +1,41 @@
//
// ClientParameterProtocol.swift
// MVMCore
//
// Created by Suresh, Kamlesh on 12/29/20.
// Copyright © 2020 myverizon. All rights reserved.
//
import Foundation
public protocol ClientParameterProtocol: ModelHandlerProtocol {
static var name: String { get }
init(_ clientParameterModel: ClientParameterModelProtocol)
var clientParameterModel: ClientParameterModelProtocol { get set }
func fetchClientParameters(requestParameters: [String: Any], timingOutIn timeout: Double, completionHandler:@escaping ([String: AnyHashable]?) -> ())
/// Default parameter for timeout scenarios. It will use the protocol extension method bydefault. Can override to send custom values.
func valueOnTimeout() -> [String: AnyHashable]
}
public extension ClientParameterProtocol {
func valueOnTimeout() -> [String: AnyHashable] {
return [Self.name: "failed_to_collect"]
}
/// The handler should call this method to pass the parameter back to the caller.
func returnParameters(_ isFlatMap: Bool, _ parameter: [String: AnyHashable]?, completionHandler: @escaping ([String: AnyHashable]?) -> ()) {
guard let parameter = parameter else {
return completionHandler(nil)
}
if isFlatMap {
completionHandler(parameter)
} else {
completionHandler([Self.name: parameter])
}
}
}

View File

@ -0,0 +1,11 @@
//
// ModelViewProtocol.swift
// MVMCore
//
// Created by Matt Bruce on 3/12/21.
// Copyright © 2021 myverizon. All rights reserved.
//
import Foundation
public protocol ModelHandlerProtocol {}

View File

@ -35,11 +35,11 @@ public protocol ModelProtocol: Codable {
extension ModelProtocol {
static public func decode<K: CodingKey>(keyedContainer: KeyedDecodingContainer<K>, codingKey: K) throws -> Self? {
return try keyedContainer.decodeIfPresent(self, forKey: codingKey)
try keyedContainer.decodeIfPresent(self, forKey: codingKey)
}
static public func decode(unkeyedContainer: inout UnkeyedDecodingContainer) throws -> Self? {
return try unkeyedContainer.decodeIfPresent(self)
try unkeyedContainer.decodeIfPresent(self)
}
public func encode<K: CodingKey>(keyedContainer: inout KeyedEncodingContainer<K>, codingKey: K) throws {
@ -50,4 +50,3 @@ extension ModelProtocol {
try unkeyedContainer.encode(self)
}
}

View File

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

View File

@ -6,20 +6,23 @@
// Copyright © 2019 myverizon. All rights reserved.
//
import Foundation
@objcMembers public class ModelMapping: NSObject {
@objcMembers open class ModelMapping: NSObject {
public static func registerObjects() {
try? ModelRegistry.register(ActionOpenPageModel.self)
try? ModelRegistry.register(ActionOpenUrlModel.self)
try? ModelRegistry.register(ActionCallModel.self)
try? ModelRegistry.register(ActionBackModel.self)
try? ModelRegistry.register(ActionShareModel.self)
try? ModelRegistry.register(ActionRestartModel.self)
try? ModelRegistry.register(ActionPreviousSubmitModel.self)
try? ModelRegistry.register(ActionCancelModel.self)
try? ModelRegistry.register(ActionSettingModel.self)
try? ModelRegistry.register(ActionNoopModel.self)
open class func registerObjects() { }
open class func registerActions() {
ModelRegistry.register(ActionOpenPageModel.self)
ModelRegistry.register(ActionOpenUrlModel.self)
ModelRegistry.register(ActionCallModel.self)
ModelRegistry.register(ActionBackModel.self)
ModelRegistry.register(ActionShareModel.self)
ModelRegistry.register(ActionRestartModel.self)
ModelRegistry.register(ActionPreviousSubmitModel.self)
ModelRegistry.register(ActionCancelModel.self)
ModelRegistry.register(ActionSettingModel.self)
ModelRegistry.register(ActionNoopModel.self)
ModelRegistry.register(ActionActionsModel.self)
ModelRegistry.register(ActionOpenSMSModel.self)
ModelRegistry.register(ActionContactModel.self)
}
}

View File

@ -9,6 +9,8 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AVKit/AVKit.h>
@class MVMCoreErrorObject;
//block returned when getting image
//parameters are UIImage object for the image, NSData for gif images, UIImage object for the image, A BOOL to indicate if it is a fall back image.
@ -152,4 +154,13 @@ typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOO
// clears the image cache
- (void)clearImageCache;
#pragma mark - Video Functions
/// Loads an AVPlayerAsset from the shared asset cache. Uses default track keys of "duration" and "playable".
- (void)playerAssetFromFileName:(nonnull NSString *)filename onComplete:(void(^_Nonnull)(AVAsset * _Nullable, NSString * _Nonnull, MVMCoreErrorObject * _Nullable))completionHandler;
/// Loads an AVPlayerAsset from the shared asset cache.
- (void)playerAssetFromFileName:(nonnull NSString *)filename trackKeys:(nonnull NSArray *)trackKeys onComplete:(void(^_Nonnull)(AVAsset * _Nullable, NSString * _Nonnull, MVMCoreErrorObject * _Nullable))completionHandler;
@end

View File

@ -15,6 +15,8 @@
#import "MVMCoreConstants.h"
#import "MVMCoreActionUtility.h"
#import "MVMCoreLoggingHandler.h"
#import "MVMCoreDispatchUtility.h"
#import "MVMCoreErrorConstants.h"
@interface MVMCoreCache ()
@ -34,6 +36,9 @@
// For thread safety
@property (strong, nonatomic) dispatch_queue_t imageCacheQueue;
@property (nonnull, strong, nonatomic) dispatch_queue_t videoQueue;
@property (nullable, strong, nonatomic) NSCache *playerItemCache;
@end
@ -58,6 +63,9 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
self.imageCacheQueue = dispatch_queue_create("imgCache", DISPATCH_QUEUE_CONCURRENT);
self.imageBundles = [NSMutableArray array];
self.videoQueue = dispatch_queue_create("video_queue", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, 0));
self.playerItemCache = [[NSCache alloc] init];
}
return self;
}
@ -664,6 +672,7 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
dispatch_barrier_async(self.imageCacheQueue, ^{
[self.imgCache removeAllObjects];
});
[self.playerItemCache removeAllObjects];
}
- (void)removeImageFromCache:(nonnull NSString *)imageName {
@ -691,4 +700,50 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
}
}
#pragma mark - Video Functions
- (void)playerAssetFromFileName:(NSString *)filename onComplete:(void(^)(AVAsset * _Nullable, NSString * _Nonnull, MVMCoreErrorObject * _Nullable))completionHandler {
[self playerAssetFromFileName:filename trackKeys:@[@"playable",@"duration"] onComplete:completionHandler];
}
- (void)playerAssetFromFileName:(NSString *)filename trackKeys:(NSArray *)trackKeys onComplete:(void(^)(AVAsset * _Nullable, NSString * _Nonnull, MVMCoreErrorObject * _Nullable))completionHandler {
[MVMCoreDispatchUtility performBlock:^{
//get the video url, can be downloaded or assets
NSURL *url;
MVMCoreErrorObject *error = nil;
__block AVAsset *item = [self.playerItemCache objectForKey:filename];
if (!item) {
if ([filename hasPrefix:@"http"]) {
url = [NSURL URLWithString:filename];
} else {
url = [[MVMCoreGetterUtility bundleForMVMCore] URLForResource:filename withExtension:@"mp4"];
}
if (url) {
item = [AVURLAsset URLAssetWithURL:url options:@{AVURLAssetPreferPreciseDurationAndTimingKey:@YES}];
[self.playerItemCache setObject:item forKey:filename]; // Set initial vanilla AVURLAsset.
} else {
error = [[MVMCoreErrorObject alloc] initWithTitle:nil messageToLog:[NSString stringWithFormat:@"Invalid URL: %@", filename] code:0 domain:ErrorDomainNative location:nil];
}
} else {
MVMCoreLog(@"Loading video from cache: %@", filename);
}
if (!item) {
if (!error) {
error = [[MVMCoreErrorObject alloc] initWithTitle:nil messageToLog:[NSString stringWithFormat:@"Failed to initialize asset for URL: %@", filename] code:0 domain:ErrorDomainNative location:nil];
}
[MVMCoreDispatchUtility performBlockOnMainThread:^{
completionHandler(nil, filename, error);
}];
} else {
// Load the duration key up front off of the main thread. Later duration will be used for seek and loop calculations.
[item loadValuesAsynchronouslyForKeys:trackKeys completionHandler:^{
[MVMCoreDispatchUtility performBlockOnMainThread:^{
completionHandler(item, filename, error);
}];
}];
}
} onQueue:self.videoQueue];
}
@end

View File

@ -16,7 +16,6 @@
- (instancetype)init {
if (self = [super init]) {
self.session = [self createNSURLSession];
[ModelMapping registerObjects];
}
return self;
}

View File

@ -16,6 +16,12 @@
// The time that we started the last session timer.
@property (assign, nonatomic, readonly) NSTimeInterval timeTimerStarted;
// Keeps track of if the session is currently being timed. Used for entering from the background.
@property (assign, nonatomic, readonly) BOOL sessionBeingTimed;
// Keeps track of if the session has already timed out.
@property (assign, nonatomic, readonly) BOOL sessionTimedOut;
#pragma mark - functions to override
// Can override to provide a time until the warning shows in seconds. Set to 0 if there should be no warning. Default is 0

View File

@ -21,16 +21,17 @@
@interface MVMCoreSessionTimeHandler ()
@property (strong, nonatomic) NSTimer *sessionWarningTimer;
@property (strong, nonatomic) NSTimer *sessionTimer;
// The time that we started the last session timer.
@property (assign, nonatomic, readwrite) NSTimeInterval timeTimerStarted;
// Keeps track of if the session is currently being timed. Used for entering from the background.
@property (assign, nonatomic) BOOL sessionBeingTimed;
@property (assign, nonatomic, readwrite) BOOL sessionBeingTimed;
// Keeps track of if the session has already timed out.
@property (assign, nonatomic) BOOL sessionTimedOut;
@property (assign, nonatomic, readwrite) BOOL sessionTimedOut;
@property (assign, nonatomic) NSTimeInterval secondsUntilWarning;
@property (assign, nonatomic) NSTimeInterval secondsUntilTimeout;
@ -74,13 +75,6 @@
}
- (void)sessionTimeoutWarning {
// Starts the timeout timer
if (!fequal(0, self.secondsUntilTimeout)) {
self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:self.secondsUntilTimeout target:self selector:@selector(sessionTimeout:) userInfo:nil repeats:NO];
} else {
[self stopSessionTimer];
}
}
- (void)sessionTimeout:(BOOL)whileInBackground {
@ -98,22 +92,22 @@
if (!self.sessionTimedOut) {
[self.sessionWarningTimer invalidate];
[self.sessionTimer invalidate];
self.secondsUntilWarning = [self timeUntilWarning];
self.secondsUntilTimeout = [self timeUntilTimeout];
if (!fequal(0, self.secondsUntilWarning) || !fequal(0, self.secondsUntilTimeout)) {
if (!fequal(0, self.secondsUntilTimeout)) {
self.sessionBeingTimed = YES;
self.timeTimerStarted = [NSDate timeIntervalSinceReferenceDate];
}
// Only start physical timer if active, otherwise will begin once entering foreground.
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive && !fequal(0, self.secondsUntilTimeout)) {
if (!fequal(0, self.secondsUntilWarning)) {
self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:self.secondsUntilWarning target:self selector:@selector(sessionTimeoutWarning) userInfo:nil repeats:NO];
} else if (!fequal(0, self.secondsUntilTimeout)) {
self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:self.secondsUntilTimeout target:self selector:@selector(sessionTimeout:) userInfo:nil repeats:NO];
self.sessionWarningTimer = [NSTimer scheduledTimerWithTimeInterval:self.secondsUntilWarning target:self selector:@selector(sessionTimeoutWarning) userInfo:nil repeats:NO];
}
self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:self.secondsUntilTimeout target:self selector:@selector(sessionTimeout:) userInfo:nil repeats:NO];
}
}
});
@ -124,6 +118,8 @@
// nil timer, session no longer timed.
dispatch_async(dispatch_get_main_queue(), ^(void) {
self.sessionBeingTimed = NO;
[self.sessionWarningTimer invalidate];
self.sessionWarningTimer = nil;
[self.sessionTimer invalidate];
self.sessionTimer = nil;
});
@ -133,6 +129,8 @@
// Session is still being timed. Invalidates here, will start up again on enter foreground if need be.
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self.sessionWarningTimer invalidate];
self.sessionWarningTimer = nil;
[self.sessionTimer invalidate];
self.sessionTimer = nil;
});
@ -143,22 +141,25 @@
if (self.sessionBeingTimed || self.sessionTimedOut) {
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
if ((!fequal(0, self.secondsUntilWarning) && now > self.timeTimerStarted + self.secondsUntilWarning) || (fequal(0, self.secondsUntilWarning) && !fequal(0, self.secondsUntilTimeout) && now > self.timeTimerStarted + self.secondsUntilTimeout)) {
if ((!fequal(0, self.secondsUntilWarning) && now > self.timeTimerStarted + self.secondsUntilWarning) || (!fequal(0, self.secondsUntilTimeout) && now > self.timeTimerStarted + self.secondsUntilTimeout)) {
// Timeout if we are passed the warning.
[self sessionTimeout:YES];
} else if (!fequal(0, self.secondsUntilWarning)) {
} else {
if (!fequal(0, self.secondsUntilWarning)) {
// Restart the warning timer!
NSTimeInterval timeLeftTillWarning = self.timeTimerStarted + self.secondsUntilWarning - now;
[self.sessionTimer invalidate];
self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:timeLeftTillWarning target:self selector:@selector(sessionTimeoutWarning) userInfo:nil repeats:NO];
} else if (!fequal(0, self.secondsUntilTimeout)) {
// Restart the warning timer!
NSTimeInterval timeLeftTillWarning = self.timeTimerStarted + self.secondsUntilWarning - now;
[self.sessionWarningTimer invalidate];
self.sessionWarningTimer = [NSTimer scheduledTimerWithTimeInterval:timeLeftTillWarning target:self selector:@selector(sessionTimeoutWarning) userInfo:nil repeats:NO];
}
if (!fequal(0, self.secondsUntilTimeout)) {
// Restart the timeout timer!
NSTimeInterval timeLeftTillTimeout = self.timeTimerStarted + self.secondsUntilTimeout - now;
[self.sessionTimer invalidate];
self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:timeLeftTillTimeout target:self selector:@selector(sessionTimeout:) userInfo:nil repeats:NO];
// Restart the timeout timer!
NSTimeInterval timeLeftTillTimeout = self.timeTimerStarted + self.secondsUntilTimeout - now;
[self.sessionTimer invalidate];
self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:timeLeftTillTimeout target:self selector:@selector(sessionTimeout:) userInfo:nil repeats:NO];
}
}
}
}
@ -174,6 +175,8 @@
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
if ((!fequal(0, self.secondsUntilWarning) && now > self.timeTimerStarted + self.secondsUntilWarning) || (!fequal(0, self.secondsUntilTimeout) && now > self.timeTimerStarted + self.secondsUntilTimeout)) {
self.sessionTimedOut = YES;
[self.sessionWarningTimer invalidate];
self.sessionWarningTimer = nil;
[self.sessionTimer invalidate];
self.sessionTimer = nil;
} else if (![self.sessionTimer isValid]) {

View File

@ -7,6 +7,7 @@
//
#import "MVMCoreObject.h"
#import <MVMCore/MVMCore-Swift.h>
@implementation MVMCoreObject
@ -20,6 +21,7 @@
}
- (void)defaultInitialSetup {
[ModelMapping registerObjects];
self.session = [[MVMCoreSessionObject alloc] init];
self.cache = [[MVMCoreCache alloc] init];
self.viewControllerMapping = [[MVMCoreViewControllerMappingObject alloc] init];

View File

@ -13,3 +13,5 @@
"Error Message Critical Key" = "Unable to process your request at this time. Please contact Customer Service by dialing *611. Thank you.";
"Error Message Unable To Process Request Key" = "Unable to process your request. Please try again later.";
//Fios
"Contact not available" = "Contact not available";

View File

@ -12,3 +12,6 @@
"Error Message Critical Key" = "En este momento no podemos procesar tu solicitud. Comunícate con Servicio al Cliente marcando *611. Muchas gracias.";
"Error Message Unable To Process Request Key" = "No podemos procesar tu solicitud. Vuelve a intentarlo más tarde.";
//Fios
"Contact not available" = "Contacto no disponible";

View File

@ -12,3 +12,6 @@
"Error Message Critical Key" = "En este momento no podemos procesar tu solicitud. Comunícate con Servicio al Cliente marcando *611. Muchas gracias.";
"Error Message Unable To Process Request Key" = "No podemos procesar tu solicitud. Vuelve a intentarlo más tarde.";
//Fios
"Contact not available" = "Contacto no disponible";

View File

@ -69,16 +69,16 @@
- (NSDictionary *) getHardCodedResponseForRequest:(MVMCoreRequestParameters *)request {
NSDictionary *cannedResponse = nil;
if(_jsonDictionary) {
if (_jsonDictionary) {
BOOL matchesQuery = NO;
for(NSString *urlKey in _sortedKeys) {
for (NSString *urlKey in _sortedKeys) {
NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:urlKey];
if([urlComponents.path isEqualToString:request.pageType]) {
if ([urlComponents.path isEqualToString:request.pageType]) {
matchesQuery = YES;
if(urlComponents.query) {
for(NSURLQueryItem *item in urlComponents.queryItems) {
if (urlComponents.query) {
for (NSURLQueryItem *item in urlComponents.queryItems) {
NSString *requestVal = [request.parameters stringForKey:item.name];
if(!requestVal || ![requestVal isEqualToString:item.value]) {
if (!requestVal || ![requestVal isEqualToString:item.value]) {
matchesQuery = NO;
break;
}
@ -88,7 +88,7 @@
cannedResponse = _jsonDictionary[urlKey];
// keep searching to see if there is a more exact match on parameters
}
if(matchesQuery) {
if (matchesQuery) {
cannedResponse = _jsonDictionary[urlKey];
// found a request parameters match, accept as the best result and break out
break;
@ -101,7 +101,6 @@
cannedResponse = [self getHardCodedResponseForPageType:request.pageType];
}
return cannedResponse;
}

View File

@ -19,6 +19,15 @@
// Returns the hardcoded string from the string file.
+ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key;
// Returns the hardcoded string from the string file based on module.
+ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key bundle:(nullable NSBundle *)bundle;
// Return current system language
+ (nullable NSString *)getSystemLanguage;
// Return current preferred system language
+ (nullable NSString *)getPreferredAvailableLanguage;
// Returns true if the user's language is Spanish
+ (BOOL)userPrefersSpanish;

View File

@ -18,20 +18,34 @@
return [NSBundle bundleWithIdentifier:@"com.vzw.MVMCore"];
}
// Confirms that the preferred user language is for spanish users.
+ (BOOL)userPrefersSpanish {
// This should be enough for us to look at what the user prefers.
return [[[[[NSLocale preferredLanguages] objectAtIndex:0] substringToIndex:2] lowercaseString] isEqualToString:@"es"];
return [[[MVMCoreGetterUtility getPreferredAvailableLanguage] lowercaseString] hasPrefix:@"es"];
}
// Gets language code based on what the user prefers and the app provides.
+ (NSString *)getPreferredAvailableLanguage {
return [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0];
}
// Returns the preferred language set system wide.
+ (NSString *)getSystemLanguage {
return [[NSLocale preferredLanguages] objectAtIndex:0];
}
+ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key {
// Redirect key with relevant module.
return [MVMCoreGetterUtility hardcodedStringWithKey:key bundle:[MVMCoreGetterUtility bundleForMVMCore]];
}
+ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key bundle:(nullable NSBundle *)bundle {
// If the app language is not english... force load from the english file anyway.
if ([MVMCoreGetterUtility userPrefersSpanish]) {
return [[NSBundle bundleWithPath:[[MVMCoreGetterUtility bundleForMVMCore] pathForResource:@"es" ofType:@"lproj"]] localizedStringForKey:key value:@"" table:nil];
} else {
return [[NSBundle bundleWithPath:[[MVMCoreGetterUtility bundleForMVMCore] pathForResource:@"en" ofType:@"lproj"]] localizedStringForKey:key value:@"" table:nil];
}
NSString *languageCode = [MVMCoreGetterUtility userPrefersSpanish] ? @"es" : @"en";
return [[NSBundle bundleWithPath:[bundle pathForResource:languageCode ofType:@"lproj"]] localizedStringForKey:key value:@"" table:nil];
}
+ (nonnull UIColor *)getColorForHexString:(nonnull NSString *)hexString {

View File

@ -10,10 +10,16 @@
@interface MVMCoreViewControllerProgrammaticMappingObject : NSObject <MVMCoreViewControllerMappingProtocol>
// View Controller Class, for loading by class.
/// View Controller Class, for loading by class.
@property (nonnull, strong, nonatomic) Class viewControllerClass;
// Initializes with the given class.
/// Configuration block executed on creation of the controller before loading.
@property (nullable, copy, nonatomic) void (^configureHandler)(UIViewController * _Nonnull);
/// Initializes with the given class.
- (nullable id)initWithClass:(nonnull Class)viewControllerClass;
/// Initializes with the given class. Assigns a configuration block triggered on intiailization which allows additional configuration of the view controller before its loaded, i.e. injecting top level dependencies.
- (nullable id)initWithClass:(nonnull Class)viewControllerClass configureHandler:(void(^_Nonnull)(UIViewController * _Nonnull))configureBlock;
@end

View File

@ -19,8 +19,20 @@
return self;
}
- (nullable id)initWithClass:(nonnull Class)viewControllerClass configureHandler:(void(^)(UIViewController * _Nonnull))configureBlock {
self = [self initWithClass:viewControllerClass];
if (self) {
self.configureHandler = configureBlock;
}
return self;
}
- (nullable UIViewController <MVMCoreViewControllerProtocol> *)createViewController {
return [[self.viewControllerClass alloc] init];
UIViewController <MVMCoreViewControllerProtocol> *newController = [[self.viewControllerClass alloc] init];
if (self.configureHandler) {
self.configureHandler(newController);
}
return newController;
}
@end

View File

@ -0,0 +1,24 @@
#!/bin/sh -e
# upload_core_frameworks.sh
#
# Uploads all compiled framework flavors in MVMCore to Artifactory with the SNAPSHOT classifier. This is to avoid accidently clobbering a release build of a particular version. Remove the classifier in Artificatory to make a release.
#
# Copied from Hedden, Kyle Matthew on 3/2/18.
#
FRAMEWORK_VERSION=$(cd ../MVMCore && agvtool vers -terse)
if [ $(git tag --list | grep "v${FRAMEWORK_VERSION}") ]; then
echo This version tag has already been committed! Aborting!
exit 1
fi
# Create new aggregate builds
if [ -z $ARTIFACTORY_URL ]; then
ARTIFACTORY_URL="https://oneartifactoryprod.verizon.com/artifactory"
fi
# Remote View Versions
BUILD_DIR=$(xcodebuild -showBuildSettings -project ../MVMCore/MVMCore.xcodeproj | grep -w -o 'BUILD_DIR = .*' | cut -d\ -f3-)
./upload_framework.sh $ARTIFACTORY_URL "${BUILD_DIR}/universal/MVMCore.framework" BPHV_MobileFirst_IOS/com/vzw/hss/myverizon/MVMCore/[VER]/MVMCore-[VER]-Debug-SNAPSHOT

90
Scripts/upload_framework.sh Executable file
View File

@ -0,0 +1,90 @@
#!/bin/bash -e
# upload_framework.sh
#
# Uploads an iOS framework to Artificatory given the local path as the first argument and the remote project name in Verizon OneArtifactory for the second argument.
#
# An API key from Artifcatory is required in the api_key.private file before uploading.
#
# The script will replace [VER] in the provided remote path with the version found in the framework bundle.
#
# Copied from Hedden, Kyle Matthew on 3/2/18.
# Copyright © 2018 Verizon. All rights reserved.
URL=$1
LOCALPATH=$2
REMOTEPATH=$3
if [ -z $URL ]; then
echo "The artifactory instance url must be specified as the first argument!"
exit 1
fi
echo ">>> UPLOAD START <<<"
echo "Local path: ${LOCALPATH}"
echo "Remote path: ${REMOTEPATH}"
cat "${LOCALPATH}/Info.plist"
LOCALBASE=$(basename "${LOCALPATH}")
LOCALDIR=$(dirname "${LOCALPATH}")
# Grab the framework version from the bundled Info.plist.
FRAMEWORKVER=$(/usr/libexec/plistbuddy -c "Print :CFBundleShortVersionString" "${LOCALPATH}/Info.plist")
echo -e "\nFramework version: \t${FRAMEWORKVER}"
# Replace the [VER] placeholders with the found version.
REMOTEPATH="${REMOTEPATH//\[VER\]/$FRAMEWORKVER}"
echo -e "Resolved path: \t\t${REMOTEPATH}"
if [ -z $ARTIFACTORY_APIKEY ]; then
# Read the API key from a private file.
read -r APIKEY < "api_key.private"
else
APIKEY=$ARTIFACTORY_APIKEY
fi
if [ -z $APIKEY ]; then
read -p "Artifactory API Key:" APIKEY
echo $APIKEY >> api_key.private
fi
echo -e "API Key: \t\t${APIKEY}"
# Zip the framework & DSYM for uploading.
pushd $LOCALDIR
echo -e "---------\nZipping: \t\t${LOCALBASE}.zip"
zip -r -X "${LOCALBASE}.zip" $LOCALBASE
# Generate framework's SHA-1 checksum.
CHECKSUM=$(shasum -a 1 "${LOCALBASE}.zip" | cut -d " " -f 1)
echo -e "SHA-1 Checksum: \t${CHECKSUM}"
if [ -e ${LOCALBASE}.dSYM ]; then
echo -e "---------\nZipping: \t\t${LOCALBASE}.dSYM.zip"
zip -r -X "${LOCALBASE}.dSYM.zip" $LOCALBASE.dSYM
# Generate its SHA-1 checksum for dsym.
DSYM_CHECKSUM=$(shasum -a 1 "${LOCALBASE}.dSYM.zip" | cut -d " " -f 1)
echo -e "SHA-1 Checksum: \t${DSYM_CHECKSUM}"
fi
popd
mv ${LOCALPATH}.zip .
if [ -e ${LOCALPATH}.dSYM.zip ]; then
mv ${LOCALPATH}.dSYM.zip .
fi
# Upload framework to Artifactory.
echo -e "---------\nUploading to: \t\t${URL}/${REMOTEPATH}.zip"
curl --header "X-JFrog-Art-Api: ${APIKEY}" --header "X-Checksum-Sha1: ${CHECKSUM}" -X PUT "${URL}/${REMOTEPATH}.zip" -T "${LOCALBASE}.zip"
# Cleanup.
rm "${LOCALBASE}.zip"
if [ -e ${LOCALBASE}.dSYM.zip ]; then
# Upload dsym Artifactory.
echo -e "---------\nUploading to: \t\t${URL}/${REMOTEPATH}.dSYM.zip"
curl --header "X-JFrog-Art-Api: ${APIKEY}" --header "X-Checksum-Sha1: ${DSYM_CHECKSUM}" -X PUT "${URL}/${REMOTEPATH}.dSYM.zip" -T "${LOCALBASE}.dSYM.zip"
# Cleanup dsym.
rm ${LOCALBASE}.dSYM.zip
fi
echo -e "\n\n<<< UPLOAD COMPLETE >>>\n\n"