Compare commits

..

No commits in common. "develop" and "feature/JSONValue-Update" have entirely different histories.

127 changed files with 3312 additions and 4315 deletions

View File

@ -31,9 +31,9 @@ deploy_snapshot:
- bash_shell
environment:
name: oneartifactory
url: https://oneartifactoryci.verizon.com/artifactory
url: https://oneartifactoryprod.verizon.com/artifactory
variables:
ARTIFACTORY_URL: https://oneartifactoryci.verizon.com/artifactory
ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory
#promote_snapshot:
# stage: go live
@ -49,9 +49,9 @@ deploy_snapshot:
# - bash_shell
# environment:
# name: oneartifactory
# url: https://oneartifactoryci.verizon.com/artifactory
# url: https://oneartifactoryprod.verizon.com/artifactory
# variables:
# ARTIFACTORY_URL: https://oneartifactoryci.verizon.com/artifactory
# ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory
#
#create_version_tag:
# stage: tag

View File

@ -39,14 +39,8 @@
0AEBB84625FA75C000EA80EE /* ActionOpenSMSModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AEBB84525FA75C000EA80EE /* ActionOpenSMSModel.swift */; };
0AFF597A23FC6E60005C24E8 /* ActionShareModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF597923FC6E60005C24E8 /* ActionShareModel.swift */; };
1DAD0FFE26AAB40000216E83 /* ActionRunJavaScriptModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DAD0FFD26AAB3FF00216E83 /* ActionRunJavaScriptModel.swift */; };
2723337B28BD534D004EAEE0 /* MVMCoreEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2723337A28BD534D004EAEE0 /* MVMCoreEvent.swift */; };
2723337D28BD53C2004EAEE0 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2723337C28BD53C2004EAEE0 /* Date+Extension.swift */; };
5846ABF42B44BB9000FA6C76 /* Collection+Safe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5846ABF32B44BB9000FA6C76 /* Collection+Safe.swift */; };
5878F0B22BDAA63E00ADE23D /* ReadableDecodingErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5878F0B12BDAA63E00ADE23D /* ReadableDecodingErrors.swift */; };
6042E8FC2B317B190031644B /* MVMCoreLoggingHandlerHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 6042E8FB2B3094680031644B /* MVMCoreLoggingHandlerHelper.h */; settings = {ATTRIBUTES = (Public, ); }; };
605A9A2A2ABD712F00487E47 /* MVMCoreLoggingHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 605A9A292ABD712F00487E47 /* MVMCoreLoggingHandler.swift */; };
6079EDCE2AD97AA5004B7A85 /* MVMCoreLoggingDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6079EDCD2AD97AA5004B7A85 /* MVMCoreLoggingDelegateProtocol.swift */; };
60CBD0542A02397A00056CB0 /* MVMCoreSessionTimeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60CBD0532A02397A00056CB0 /* MVMCoreSessionTimeHandler.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 */; };
881D26931FCC9D180079C521 /* MVMCoreErrorObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 881D268F1FCC9D180079C521 /* MVMCoreErrorObject.m */; };
881D26941FCC9D180079C521 /* MVMCoreOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 881D26901FCC9D180079C521 /* MVMCoreOperation.m */; };
881D26951FCC9D180079C521 /* MVMCoreErrorObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 881D26911FCC9D180079C521 /* MVMCoreErrorObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -77,7 +71,6 @@
AF1201832108C9B400E2F592 /* MVMCoreViewManagerViewControllerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AF1201812108C9B400E2F592 /* MVMCoreViewManagerViewControllerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF130B8E2788DF6E00C6C03C /* OpenURLOptionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF130B8D2788DF6E00C6C03C /* OpenURLOptionsModel.swift */; };
AF26DDAE1FCE6A37004E8F65 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = AF26DDB01FCE6A37004E8F65 /* Localizable.strings */; };
AF3A3F3F2ACC716A005094B2 /* MVMCoreObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF3A3F3E2ACC716A005094B2 /* MVMCoreObject.swift */; };
AF43A5771FBA5B7C008E9347 /* MVMCoreJSONConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A5751FBA5B7C008E9347 /* MVMCoreJSONConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF43A5781FBA5B7C008E9347 /* MVMCoreJSONConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A5761FBA5B7C008E9347 /* MVMCoreJSONConstants.m */; };
AF43A57B1FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A5791FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -90,6 +83,8 @@
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, ); }; };
AF43A7071FC4D7A2008E9347 /* MVMCoreObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A7051FC4D7A2008E9347 /* MVMCoreObject.m */; };
AF43A70A1FC4F415008E9347 /* MVMCoreCache.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A7081FC4F415008E9347 /* MVMCoreCache.m */; };
AF43A70B1FC4F415008E9347 /* MVMCoreCache.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A7091FC4F415008E9347 /* MVMCoreCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF43A71B1FC5BEBB008E9347 /* MVMCoreViewControllerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A71A1FC5BEBB008E9347 /* MVMCoreViewControllerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -97,27 +92,7 @@
AF43A7411FC5FA6F008E9347 /* MVMCoreViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF43A74C1FC6109F008E9347 /* MVMCoreSessionObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF43A74D1FC6109F008E9347 /* MVMCoreSessionObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */; };
AF4955E22BAB1EB200567276 /* MVMCoreCache+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4955E12BAB1EB200567276 /* MVMCoreCache+Extension.swift */; };
AF60A7F2289212CA00919EEB /* MVMError.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F1289212CA00919EEB /* MVMError.swift */; };
AF60A7F4289212EB00919EEB /* MVMCoreError.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF60A7F3289212EB00919EEB /* MVMCoreError.swift */; };
AF686FDA2A8A876A008F666A /* NavigationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF686FD92A8A876A008F666A /* NavigationOperation.swift */; };
AF686FDE2A8D29CD008F666A /* MVMCoreLoadRequestOperation+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF686FDD2A8D29CD008F666A /* MVMCoreLoadRequestOperation+Extension.swift */; };
AF6870292A9CD0E9008F666A /* MVMCoreLoadHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF6870282A9CD0E9008F666A /* MVMCoreLoadHandler.swift */; };
AF69D4E9286E54D500BC6862 /* ActionCallHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF69D4E8286E54D500BC6862 /* ActionCallHandler.swift */; };
AF69D4EB286E586200BC6862 /* ActionRestartHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF69D4EA286E586200BC6862 /* ActionRestartHandler.swift */; };
AF69D4ED286E5D8C00BC6862 /* ActionCancelHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF69D4EC286E5D8C00BC6862 /* ActionCancelHandler.swift */; };
AF69D4EF286E612800BC6862 /* ActionOpenSMSHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF69D4EE286E612800BC6862 /* ActionOpenSMSHandler.swift */; };
AF69D4F1286E9D8000BC6862 /* ActionNoopHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF69D4F0286E9D8000BC6862 /* ActionNoopHandler.swift */; };
AF69D4F3286E9DCE00BC6862 /* ActionActionsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF69D4F2286E9DCE00BC6862 /* ActionActionsHandler.swift */; };
AF69D4F5286E9F5900BC6862 /* ActionSettingHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF69D4F4286E9F5900BC6862 /* ActionSettingHandler.swift */; };
AF69D4F7286EA0B800BC6862 /* ActionPreviousSubmitHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF69D4F6286EA0B800BC6862 /* ActionPreviousSubmitHandler.swift */; };
AF70699A287DD02400077CF6 /* ActionContactHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF706999287DD02400077CF6 /* ActionContactHandler.swift */; };
AF70699E2880D01400077CF6 /* ActionShareHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF70699D2880D01400077CF6 /* ActionShareHandler.swift */; };
AF7069A02880F0EB00077CF6 /* ActionOpenPageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF70699F2880F0EB00077CF6 /* ActionOpenPageHandler.swift */; };
AF787413286DEF8B00670588 /* ActionBackHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF787412286DEF8B00670588 /* ActionBackHandler.swift */; };
AF8D13392774EA1D008AF4A9 /* ActionOpenUrlHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF8D13382774EA1D008AF4A9 /* ActionOpenUrlHandler.swift */; };
AF925DEE28BD35A2008E8677 /* NavigationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF925DED28BD35A2008E8677 /* NavigationHandler.swift */; };
AFA4931E29E5C988001A9663 /* MVMCoreActionUtility+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA4931D29E5C988001A9663 /* MVMCoreActionUtility+Extension.swift */; };
AFBB96341FBA34310008D868 /* MVMCoreErrorConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96321FBA34310008D868 /* MVMCoreErrorConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB96351FBA34310008D868 /* MVMCoreErrorConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96331FBA34310008D868 /* MVMCoreErrorConstants.m */; };
AFBB96381FBA39E70008D868 /* MVMCoreLoadDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96371FBA39E70008D868 /* MVMCoreLoadDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -129,12 +104,28 @@
AFBB96641FBA3A570008D868 /* MVMCoreLoadHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB964B1FBA3A560008D868 /* MVMCoreLoadHandler.m */; };
AFBB96691FBA3A570008D868 /* MVMCoreRequestParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96511FBA3A560008D868 /* MVMCoreRequestParameters.m */; };
AFBB966A1FBA3A570008D868 /* MVMCoreLoadRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96521FBA3A570008D868 /* MVMCoreLoadRequestOperation.m */; };
AFBB968B1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB966D1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB968C1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB966E1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.m */; };
AFBB968D1FBA3A9A0008D868 /* MVMCoreNavigationHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB966F1FBA3A9A0008D868 /* MVMCoreNavigationHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB968E1FBA3A9A0008D868 /* MVMCoreNavigationHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96701FBA3A9A0008D868 /* MVMCoreNavigationHandler.m */; };
AFBB968F1FBA3A9A0008D868 /* MVMCoreNavigationObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96711FBA3A9A0008D868 /* MVMCoreNavigationObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB96901FBA3A9A0008D868 /* MVMCoreNavigationObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96721FBA3A9A0008D868 /* MVMCoreNavigationObject.m */; };
AFBB96911FBA3A9A0008D868 /* MVMCoreNavigationOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96731FBA3A9A0008D868 /* MVMCoreNavigationOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB96921FBA3A9A0008D868 /* MVMCoreNavigationOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96741FBA3A9A0008D868 /* MVMCoreNavigationOperation.m */; };
AFBB96931FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96751FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB96941FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96761FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.m */; };
AFBB96951FBA3A9A0008D868 /* MVMCorePresentationDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96771FBA3A9A0008D868 /* MVMCorePresentationDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB96961FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96781FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB96971FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96791FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.m */; };
AFBB96981FBA3A9A0008D868 /* MVMCoreAlertController.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB967B1FBA3A9A0008D868 /* MVMCoreAlertController.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB96991FBA3A9A0008D868 /* MVMCoreAlertController.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB967C1FBA3A9A0008D868 /* MVMCoreAlertController.m */; };
AFBB96B01FBA3B590008D868 /* MVMCoreDispatchUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96AC1FBA3B590008D868 /* MVMCoreDispatchUtility.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB96B11FBA3B590008D868 /* MVMCoreDispatchUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96AD1FBA3B590008D868 /* MVMCoreDispatchUtility.m */; };
AFBB96B21FBA3B590008D868 /* MVMCoreGetterUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96AE1FBA3B590008D868 /* MVMCoreGetterUtility.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB96B31FBA3B590008D868 /* MVMCoreGetterUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96AF1FBA3B590008D868 /* MVMCoreGetterUtility.m */; };
AFBB96B81FBA3CEC0008D868 /* MVMCoreActionDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96B51FBA3CEC0008D868 /* MVMCoreActionDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB96B91FBA3CEC0008D868 /* MVMCoreActionHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96B61FBA3CEC0008D868 /* MVMCoreActionHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFBB96BA1FBA3CEC0008D868 /* MVMCoreActionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96B71FBA3CEC0008D868 /* MVMCoreActionHandler.m */; };
AFBB96EC1FBA4A260008D868 /* MFHardCodedServerResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96E91FBA4A260008D868 /* MFHardCodedServerResponse.h */; };
AFBB96ED1FBA4A260008D868 /* MFHardCodedServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96EA1FBA4A260008D868 /* MFHardCodedServerResponse.m */; };
AFEA17A8209B6A1C00BC6740 /* MVMCoreBlockOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = AFEA17A6209B6A1C00BC6740 /* MVMCoreBlockOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -147,6 +138,9 @@
AFED77A61FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AFED779E1FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.m */; };
AFED77A71FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AFED779F1FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.m */; };
AFED77A81FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AFED77A01FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFEEE8191FCDEB8D00B5EDD0 /* MVMCoreLoggingDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFEEE8181FCDDE6B00B5EDD0 /* MVMCoreLoggingDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFEEE81E1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = AFEEE81C1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFEEE81F1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AFEEE81D1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.m */; };
AFFCFA651FCCC0D700FD0730 /* MVMCoreLoadingOverlayDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFFCFA611FCCC0D600FD0730 /* MVMCoreLoadingOverlayDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFFCFA661FCCC0D700FD0730 /* MVMCoreLoadingOverlayHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = AFFCFA621FCCC0D600FD0730 /* MVMCoreLoadingOverlayHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
AFFCFA671FCCC0D700FD0730 /* MVMCoreLoadingOverlayHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AFFCFA631FCCC0D600FD0730 /* MVMCoreLoadingOverlayHandler.m */; };
@ -155,8 +149,8 @@
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.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27073CC25BB4CEF001C7246 /* MVMCoreActionHandler.swift */; };
D27073D125BB844B001C7246 /* ActionDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27073D025BB844B001C7246 /* ActionDelegateProtocol.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 */; };
D288D5F526C6EFE000A5C365 /* MVMCoreLoggingHandler+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D288D5F426C6EFE000A5C365 /* MVMCoreLoggingHandler+Extension.swift */; };
@ -190,16 +184,8 @@
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>"; };
1DAD0FFD26AAB3FF00216E83 /* ActionRunJavaScriptModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionRunJavaScriptModel.swift; sourceTree = "<group>"; };
2723337A28BD534D004EAEE0 /* MVMCoreEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreEvent.swift; sourceTree = "<group>"; };
2723337C28BD53C2004EAEE0 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = "<group>"; };
581FABEE2A71D0E6003A8508 /* mvmcore_dev.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = mvmcore_dev.xcconfig; sourceTree = "<group>"; };
5836B8E22A4338DF002553D9 /* mvmcore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = mvmcore.xcconfig; sourceTree = "<group>"; };
5846ABF32B44BB9000FA6C76 /* Collection+Safe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Collection+Safe.swift"; sourceTree = "<group>"; };
5878F0B12BDAA63E00ADE23D /* ReadableDecodingErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadableDecodingErrors.swift; sourceTree = "<group>"; };
6042E8FB2B3094680031644B /* MVMCoreLoggingHandlerHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoggingHandlerHelper.h; sourceTree = "<group>"; };
605A9A292ABD712F00487E47 /* MVMCoreLoggingHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreLoggingHandler.swift; sourceTree = "<group>"; };
6079EDCD2AD97AA5004B7A85 /* MVMCoreLoggingDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreLoggingDelegateProtocol.swift; sourceTree = "<group>"; };
60CBD0532A02397A00056CB0 /* MVMCoreSessionTimeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreSessionTimeHandler.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>"; };
881D268F1FCC9D180079C521 /* MVMCoreErrorObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreErrorObject.m; sourceTree = "<group>"; };
881D26901FCC9D180079C521 /* MVMCoreOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreOperation.m; sourceTree = "<group>"; };
881D26911FCC9D180079C521 /* MVMCoreErrorObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreErrorObject.h; sourceTree = "<group>"; };
@ -232,7 +218,6 @@
AF1201812108C9B400E2F592 /* MVMCoreViewManagerViewControllerProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewManagerViewControllerProtocol.h; sourceTree = "<group>"; };
AF130B8D2788DF6E00C6C03C /* OpenURLOptionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenURLOptionsModel.swift; sourceTree = "<group>"; };
AF26DDAF1FCE6A37004E8F65 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
AF3A3F3E2ACC716A005094B2 /* MVMCoreObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreObject.swift; sourceTree = "<group>"; };
AF43A5751FBA5B7C008E9347 /* MVMCoreJSONConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreJSONConstants.h; sourceTree = "<group>"; };
AF43A5761FBA5B7C008E9347 /* MVMCoreJSONConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreJSONConstants.m; sourceTree = "<group>"; };
AF43A5791FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreHardcodedStringsConstants.h; sourceTree = "<group>"; };
@ -248,6 +233,8 @@
AF43A6FB1FBE2F2A008E9347 /* Reachability.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = "<group>"; };
AF43A6FE1FBE2FEE008E9347 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
AF43A7001FC4B227008E9347 /* MVMCoreGlobalLoadProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreGlobalLoadProtocol.h; sourceTree = "<group>"; };
AF43A7041FC4D7A2008E9347 /* MVMCoreObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreObject.h; sourceTree = "<group>"; };
AF43A7051FC4D7A2008E9347 /* MVMCoreObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreObject.m; sourceTree = "<group>"; };
AF43A7081FC4F415008E9347 /* MVMCoreCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreCache.m; sourceTree = "<group>"; };
AF43A7091FC4F415008E9347 /* MVMCoreCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreCache.h; sourceTree = "<group>"; };
AF43A71A1FC5BEBB008E9347 /* MVMCoreViewControllerProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewControllerProtocol.h; sourceTree = "<group>"; };
@ -255,27 +242,7 @@
AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewProtocol.h; sourceTree = "<group>"; };
AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreSessionObject.h; sourceTree = "<group>"; };
AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreSessionObject.m; sourceTree = "<group>"; };
AF4955E12BAB1EB200567276 /* MVMCoreCache+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreCache+Extension.swift"; sourceTree = "<group>"; };
AF60A7F1289212CA00919EEB /* MVMError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMError.swift; sourceTree = "<group>"; };
AF60A7F3289212EB00919EEB /* MVMCoreError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreError.swift; sourceTree = "<group>"; };
AF686FD92A8A876A008F666A /* NavigationOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationOperation.swift; sourceTree = "<group>"; };
AF686FDD2A8D29CD008F666A /* MVMCoreLoadRequestOperation+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreLoadRequestOperation+Extension.swift"; sourceTree = "<group>"; };
AF6870282A9CD0E9008F666A /* MVMCoreLoadHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreLoadHandler.swift; sourceTree = "<group>"; };
AF69D4E8286E54D500BC6862 /* ActionCallHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCallHandler.swift; sourceTree = "<group>"; };
AF69D4EA286E586200BC6862 /* ActionRestartHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionRestartHandler.swift; sourceTree = "<group>"; };
AF69D4EC286E5D8C00BC6862 /* ActionCancelHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCancelHandler.swift; sourceTree = "<group>"; };
AF69D4EE286E612800BC6862 /* ActionOpenSMSHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionOpenSMSHandler.swift; sourceTree = "<group>"; };
AF69D4F0286E9D8000BC6862 /* ActionNoopHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionNoopHandler.swift; sourceTree = "<group>"; };
AF69D4F2286E9DCE00BC6862 /* ActionActionsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionActionsHandler.swift; sourceTree = "<group>"; };
AF69D4F4286E9F5900BC6862 /* ActionSettingHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionSettingHandler.swift; sourceTree = "<group>"; };
AF69D4F6286EA0B800BC6862 /* ActionPreviousSubmitHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionPreviousSubmitHandler.swift; sourceTree = "<group>"; };
AF706999287DD02400077CF6 /* ActionContactHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionContactHandler.swift; sourceTree = "<group>"; };
AF70699D2880D01400077CF6 /* ActionShareHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionShareHandler.swift; sourceTree = "<group>"; };
AF70699F2880F0EB00077CF6 /* ActionOpenPageHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionOpenPageHandler.swift; sourceTree = "<group>"; };
AF787412286DEF8B00670588 /* ActionBackHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBackHandler.swift; sourceTree = "<group>"; };
AF8D13382774EA1D008AF4A9 /* ActionOpenUrlHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionOpenUrlHandler.swift; sourceTree = "<group>"; };
AF925DED28BD35A2008E8677 /* NavigationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationHandler.swift; sourceTree = "<group>"; };
AFA4931D29E5C988001A9663 /* MVMCoreActionUtility+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreActionUtility+Extension.swift"; sourceTree = "<group>"; };
AFBB96321FBA34310008D868 /* MVMCoreErrorConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreErrorConstants.h; sourceTree = "<group>"; };
AFBB96331FBA34310008D868 /* MVMCoreErrorConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreErrorConstants.m; sourceTree = "<group>"; };
AFBB96371FBA39E70008D868 /* MVMCoreLoadDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadDelegateProtocol.h; sourceTree = "<group>"; };
@ -287,12 +254,29 @@
AFBB964B1FBA3A560008D868 /* MVMCoreLoadHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreLoadHandler.m; sourceTree = "<group>"; };
AFBB96511FBA3A560008D868 /* MVMCoreRequestParameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreRequestParameters.m; sourceTree = "<group>"; };
AFBB96521FBA3A570008D868 /* MVMCoreLoadRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreLoadRequestOperation.m; sourceTree = "<group>"; };
AFBB966D1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreDismissViewControllerOperation.h; sourceTree = "<group>"; };
AFBB966E1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreDismissViewControllerOperation.m; sourceTree = "<group>"; };
AFBB966F1FBA3A9A0008D868 /* MVMCoreNavigationHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreNavigationHandler.h; sourceTree = "<group>"; };
AFBB96701FBA3A9A0008D868 /* MVMCoreNavigationHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreNavigationHandler.m; sourceTree = "<group>"; };
AFBB96711FBA3A9A0008D868 /* MVMCoreNavigationObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreNavigationObject.h; sourceTree = "<group>"; };
AFBB96721FBA3A9A0008D868 /* MVMCoreNavigationObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreNavigationObject.m; sourceTree = "<group>"; };
AFBB96731FBA3A9A0008D868 /* MVMCoreNavigationOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreNavigationOperation.h; sourceTree = "<group>"; };
AFBB96741FBA3A9A0008D868 /* MVMCoreNavigationOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreNavigationOperation.m; sourceTree = "<group>"; };
AFBB96751FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCorePresentAnimationOperation.h; sourceTree = "<group>"; };
AFBB96761FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCorePresentAnimationOperation.m; sourceTree = "<group>"; };
AFBB96771FBA3A9A0008D868 /* MVMCorePresentationDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCorePresentationDelegateProtocol.h; sourceTree = "<group>"; };
AFBB96781FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCorePresentViewControllerOperation.h; sourceTree = "<group>"; };
AFBB96791FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCorePresentViewControllerOperation.m; sourceTree = "<group>"; };
AFBB967B1FBA3A9A0008D868 /* MVMCoreAlertController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreAlertController.h; sourceTree = "<group>"; };
AFBB967C1FBA3A9A0008D868 /* MVMCoreAlertController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreAlertController.m; sourceTree = "<group>"; };
AFBB96AC1FBA3B590008D868 /* MVMCoreDispatchUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreDispatchUtility.h; sourceTree = "<group>"; };
AFBB96AD1FBA3B590008D868 /* MVMCoreDispatchUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreDispatchUtility.m; sourceTree = "<group>"; };
AFBB96AE1FBA3B590008D868 /* MVMCoreGetterUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreGetterUtility.h; sourceTree = "<group>"; };
AFBB96AF1FBA3B590008D868 /* MVMCoreGetterUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreGetterUtility.m; sourceTree = "<group>"; };
AFBB96B51FBA3CEC0008D868 /* MVMCoreActionDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreActionDelegateProtocol.h; sourceTree = "<group>"; };
AFBB96B61FBA3CEC0008D868 /* MVMCoreActionHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreActionHandler.h; sourceTree = "<group>"; };
AFBB96B71FBA3CEC0008D868 /* MVMCoreActionHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreActionHandler.m; sourceTree = "<group>"; };
AFBB96D21FBA44420008D868 /* VZWAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VZWAuthentication.framework; path = ../../SharedFrameworks/VZWAuthentication.framework; sourceTree = "<group>"; };
AFBB96E91FBA4A260008D868 /* MFHardCodedServerResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFHardCodedServerResponse.h; sourceTree = "<group>"; };
AFBB96EA1FBA4A260008D868 /* MFHardCodedServerResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFHardCodedServerResponse.m; sourceTree = "<group>"; };
AFEA17A6209B6A1C00BC6740 /* MVMCoreBlockOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreBlockOperation.h; sourceTree = "<group>"; };
@ -305,6 +289,9 @@
AFED779E1FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreViewControllerStoryBoardMappingObject.m; sourceTree = "<group>"; };
AFED779F1FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreViewControllerProgrammaticMappingObject.m; sourceTree = "<group>"; };
AFED77A01FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewControllerStoryBoardMappingObject.h; sourceTree = "<group>"; };
AFEEE8181FCDDE6B00B5EDD0 /* MVMCoreLoggingDelegateProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoggingDelegateProtocol.h; sourceTree = "<group>"; };
AFEEE81C1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoggingHandler.h; sourceTree = "<group>"; };
AFEEE81D1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreLoggingHandler.m; sourceTree = "<group>"; };
AFFCFA611FCCC0D600FD0730 /* MVMCoreLoadingOverlayDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadingOverlayDelegateProtocol.h; sourceTree = "<group>"; };
AFFCFA621FCCC0D600FD0730 /* MVMCoreLoadingOverlayHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadingOverlayHandler.h; sourceTree = "<group>"; };
AFFCFA631FCCC0D600FD0730 /* MVMCoreLoadingOverlayHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreLoadingOverlayHandler.m; sourceTree = "<group>"; };
@ -313,8 +300,8 @@
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.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreActionHandler.swift; sourceTree = "<group>"; };
D27073D025BB844B001C7246 /* ActionDelegateProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionDelegateProtocol.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>"; };
D288D5F426C6EFE000A5C365 /* MVMCoreLoggingHandler+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MVMCoreLoggingHandler+Extension.swift"; sourceTree = "<group>"; };
@ -376,6 +363,7 @@
8876D5D41FB50AAB00EB2E3D /* Utility */,
AF43A7191FC5BE9E008E9347 /* MainProtocols */,
AFBB96B41FBA3CEC0008D868 /* ActionHandling */,
AFBB967A1FBA3A9A0008D868 /* AlertHandling */,
AFBB966B1FBA3A9A0008D868 /* PresentationHandling */,
AFBB96361FBA39E70008D868 /* LoadHandling */,
AFBB96131FBA26650008D868 /* ViewControllerMapping */,
@ -386,8 +374,6 @@
AF26DDAB1FCE5CF2004E8F65 /* Strings */,
8876D5CC1FB50A9E00EB2E3D /* MVMCore.h */,
8876D5CD1FB50A9E00EB2E3D /* Info.plist */,
5836B8E22A4338DF002553D9 /* mvmcore.xcconfig */,
581FABEE2A71D0E6003A8508 /* mvmcore_dev.xcconfig */,
);
path = MVMCore;
sourceTree = "<group>";
@ -395,9 +381,6 @@
8876D5D41FB50AAB00EB2E3D /* Utility */ = {
isa = PBXGroup;
children = (
AF60A7F1289212CA00919EEB /* MVMError.swift */,
AF60A7F3289212EB00919EEB /* MVMCoreError.swift */,
5878F0B12BDAA63E00ADE23D /* ReadableDecodingErrors.swift */,
881D26911FCC9D180079C521 /* MVMCoreErrorObject.h */,
881D268F1FCC9D180079C521 /* MVMCoreErrorObject.m */,
881D26921FCC9D180079C521 /* MVMCoreOperation.h */,
@ -414,7 +397,6 @@
8876D5D91FB50AB000EB2E3D /* Categories */ = {
isa = PBXGroup;
children = (
5846ABF32B44BB9000FA6C76 /* Collection+Safe.swift */,
01DF561321F90ADC00CC099B /* Dictionary+MFConvenience.swift */,
D282AAB72240342D00C46919 /* NSNumber+Extension.swift */,
8876D5DA1FB50AB000EB2E3D /* NSArray+MFConvenience.h */,
@ -427,7 +409,6 @@
8876D5E51FB50AB000EB2E3D /* UIFont+MFSpacing.m */,
8876D5E61FB50AB000EB2E3D /* UILabel+MFCustom.h */,
8876D5E71FB50AB000EB2E3D /* UILabel+MFCustom.m */,
2723337C28BD53C2004EAEE0 /* Date+Extension.swift */,
);
path = Categories;
sourceTree = "<group>";
@ -481,7 +462,19 @@
1DAD0FFD26AAB3FF00216E83 /* ActionRunJavaScriptModel.swift */,
016FF6EC259A4E3E00F5E4AA /* Client Parameters */,
01F2A03523A80A7300D954D8 /* ActionModelProtocol.swift */,
946EE1BB237B691A0036751F /* ActionOpenPageModel.swift */,
01DB1F2A26444F7F000F1AF4 /* ActionOpenPageProtocol.swift */,
01F2A04B23A82B1B00D954D8 /* ActionCallModel.swift */,
01C851D023CF97FE0021F976 /* ActionBackModel.swift */,
0AFF597923FC6E60005C24E8 /* ActionShareModel.swift */,
94C014D024211869005811A9 /* ActionRestartModel.swift */,
94C014D2242119E6005811A9 /* ActionPreviousSubmitModel.swift */,
94C014D424211AF0005811A9 /* ActionCancelModel.swift */,
94C014D824212360005811A9 /* ActionSettingModel.swift */,
BB780ADE250F8C890030BD2F /* ActionNoopModel.swift */,
D27073B625BB45C4001C7246 /* ActionActionsModel.swift */,
0AEBB84525FA75C000EA80EE /* ActionOpenSMSModel.swift */,
0ACC81A12613C73800A9C886 /* ActionContactModel.swift */,
);
path = ActionType;
sourceTree = "<group>";
@ -507,7 +500,8 @@
AF43A70C1FC4F42B008E9347 /* Session */ = {
isa = PBXGroup;
children = (
60CBD0532A02397A00056CB0 /* MVMCoreSessionTimeHandler.swift */,
30349BEF1FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h */,
30349BF01FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m */,
AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */,
AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */,
);
@ -524,7 +518,7 @@
D268D82926700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.h */,
D268D82A26700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.m */,
AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */,
6079EDCD2AD97AA5004B7A85 /* MVMCoreLoggingDelegateProtocol.swift */,
AFEEE8181FCDDE6B00B5EDD0 /* MVMCoreLoggingDelegateProtocol.h */,
AF43A7001FC4B227008E9347 /* MVMCoreGlobalLoadProtocol.h */,
);
path = MainProtocols;
@ -567,10 +561,8 @@
AFBB96371FBA39E70008D868 /* MVMCoreLoadDelegateProtocol.h */,
AFBB96391FBA3A550008D868 /* MVMCoreLoadHandler.h */,
AFBB964B1FBA3A560008D868 /* MVMCoreLoadHandler.m */,
AF6870282A9CD0E9008F666A /* MVMCoreLoadHandler.swift */,
AFBB964A1FBA3A560008D868 /* MVMCoreLoadRequestOperation.h */,
AFBB96521FBA3A570008D868 /* MVMCoreLoadRequestOperation.m */,
AF686FDD2A8D29CD008F666A /* MVMCoreLoadRequestOperation+Extension.swift */,
AFBB96471FBA3A560008D868 /* MVMCoreLoadObject.h */,
AFBB96481FBA3A560008D868 /* MVMCoreLoadObject.m */,
AFBB96491FBA3A560008D868 /* MVMCoreRequestParameters.h */,
@ -582,14 +574,33 @@
AFBB966B1FBA3A9A0008D868 /* PresentationHandling */ = {
isa = PBXGroup;
children = (
AF925DED28BD35A2008E8677 /* NavigationHandler.swift */,
AF686FD92A8A876A008F666A /* NavigationOperation.swift */,
AFBB966D1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.h */,
AFBB966E1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.m */,
AFBB966F1FBA3A9A0008D868 /* MVMCoreNavigationHandler.h */,
AFBB96701FBA3A9A0008D868 /* MVMCoreNavigationHandler.m */,
AFBB96711FBA3A9A0008D868 /* MVMCoreNavigationObject.h */,
AFBB96721FBA3A9A0008D868 /* MVMCoreNavigationObject.m */,
AFBB96731FBA3A9A0008D868 /* MVMCoreNavigationOperation.h */,
AFBB96741FBA3A9A0008D868 /* MVMCoreNavigationOperation.m */,
AFBB96751FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.h */,
AFBB96761FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.m */,
AFBB96771FBA3A9A0008D868 /* MVMCorePresentationDelegateProtocol.h */,
AFBB96781FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.h */,
AFBB96791FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.m */,
A332F33E20C7231600DCD9D9 /* MVMCoreViewControllerAnimatedTransitioning.h */,
);
path = PresentationHandling;
sourceTree = "<group>";
};
AFBB967A1FBA3A9A0008D868 /* AlertHandling */ = {
isa = PBXGroup;
children = (
AFBB967B1FBA3A9A0008D868 /* MVMCoreAlertController.h */,
AFBB967C1FBA3A9A0008D868 /* MVMCoreAlertController.m */,
);
path = AlertHandling;
sourceTree = "<group>";
};
AFBB96AB1FBA3B590008D868 /* Helpers */ = {
isa = PBXGroup;
children = (
@ -600,7 +611,6 @@
D282AAB52240085300C46919 /* MVMCoreGetterUtility+Extension.swift */,
AF43A5851FBB67D6008E9347 /* MVMCoreActionUtility.h */,
AF43A5861FBB67D6008E9347 /* MVMCoreActionUtility.m */,
AFA4931D29E5C988001A9663 /* MVMCoreActionUtility+Extension.swift */,
);
path = Helpers;
sourceTree = "<group>";
@ -609,35 +619,13 @@
isa = PBXGroup;
children = (
AFBB96B51FBA3CEC0008D868 /* MVMCoreActionDelegateProtocol.h */,
D27073D025BB844B001C7246 /* ActionDelegateProtocol.swift */,
D27073CC25BB4CEF001C7246 /* MVMCoreActionHandler.swift */,
946EE1BB237B691A0036751F /* ActionOpenPageModel.swift */,
AF70699F2880F0EB00077CF6 /* ActionOpenPageHandler.swift */,
D27073D025BB844B001C7246 /* MVMCoreActionDelegateProtocol+Extension.swift */,
AFBB96B61FBA3CEC0008D868 /* MVMCoreActionHandler.h */,
AFBB96B71FBA3CEC0008D868 /* MVMCoreActionHandler.m */,
D27073CC25BB4CEF001C7246 /* MVMCoreActionHandler+Extension.swift */,
AF130B8D2788DF6E00C6C03C /* OpenURLOptionsModel.swift */,
01F2A03823A812DD00D954D8 /* ActionOpenUrlModel.swift */,
AF8D13382774EA1D008AF4A9 /* ActionOpenUrlHandler.swift */,
01C851D023CF97FE0021F976 /* ActionBackModel.swift */,
AF787412286DEF8B00670588 /* ActionBackHandler.swift */,
01F2A04B23A82B1B00D954D8 /* ActionCallModel.swift */,
AF69D4E8286E54D500BC6862 /* ActionCallHandler.swift */,
94C014D024211869005811A9 /* ActionRestartModel.swift */,
AF69D4EA286E586200BC6862 /* ActionRestartHandler.swift */,
94C014D424211AF0005811A9 /* ActionCancelModel.swift */,
AF69D4EC286E5D8C00BC6862 /* ActionCancelHandler.swift */,
0AEBB84525FA75C000EA80EE /* ActionOpenSMSModel.swift */,
AF69D4EE286E612800BC6862 /* ActionOpenSMSHandler.swift */,
BB780ADE250F8C890030BD2F /* ActionNoopModel.swift */,
AF69D4F0286E9D8000BC6862 /* ActionNoopHandler.swift */,
D27073B625BB45C4001C7246 /* ActionActionsModel.swift */,
AF69D4F2286E9DCE00BC6862 /* ActionActionsHandler.swift */,
94C014D824212360005811A9 /* ActionSettingModel.swift */,
AF69D4F4286E9F5900BC6862 /* ActionSettingHandler.swift */,
94C014D2242119E6005811A9 /* ActionPreviousSubmitModel.swift */,
AF69D4F6286EA0B800BC6862 /* ActionPreviousSubmitHandler.swift */,
0ACC81A12613C73800A9C886 /* ActionContactModel.swift */,
AF706999287DD02400077CF6 /* ActionContactHandler.swift */,
0AFF597923FC6E60005C24E8 /* ActionShareModel.swift */,
AF70699D2880D01400077CF6 /* ActionShareHandler.swift */,
);
path = ActionHandling;
sourceTree = "<group>";
@ -650,6 +638,7 @@
AF43A6FC1FBE2F2A008E9347 /* Reachability */,
AF43A5C01FBB76D5008E9347 /* CoreGraphics.framework */,
AF43A5BF1FBB76C3008E9347 /* UIKit.framework */,
AFBB96D21FBA44420008D868 /* VZWAuthentication.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -659,11 +648,9 @@
children = (
AF43A7091FC4F415008E9347 /* MVMCoreCache.h */,
AF43A7081FC4F415008E9347 /* MVMCoreCache.m */,
AF4955E12BAB1EB200567276 /* MVMCoreCache+Extension.swift */,
605A9A292ABD712F00487E47 /* MVMCoreLoggingHandler.swift */,
6042E8FB2B3094680031644B /* MVMCoreLoggingHandlerHelper.h */,
AFEEE81C1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.h */,
AFEEE81D1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.m */,
D288D5F426C6EFE000A5C365 /* MVMCoreLoggingHandler+Extension.swift */,
2723337A28BD534D004EAEE0 /* MVMCoreEvent.swift */,
);
path = OtherHandlers;
sourceTree = "<group>";
@ -671,7 +658,8 @@
AFBB96DE1FBA48CE0008D868 /* Singletons */ = {
isa = PBXGroup;
children = (
AF3A3F3E2ACC716A005094B2 /* MVMCoreObject.swift */,
AF43A7041FC4D7A2008E9347 /* MVMCoreObject.h */,
AF43A7051FC4D7A2008E9347 /* MVMCoreObject.m */,
);
path = Singletons;
sourceTree = "<group>";
@ -717,22 +705,29 @@
AF43A7411FC5FA6F008E9347 /* MVMCoreViewProtocol.h in Headers */,
AFBB96EC1FBA4A260008D868 /* MFHardCodedServerResponse.h in Headers */,
AFBB96B21FBA3B590008D868 /* MVMCoreGetterUtility.h in Headers */,
6042E8FC2B317B190031644B /* MVMCoreLoggingHandlerHelper.h in Headers */,
8876D5F21FB50AB000EB2E3D /* UIFont+MFSpacing.h in Headers */,
30349BF11FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h in Headers */,
AFBB96931FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.h in Headers */,
AFBB96961FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.h in Headers */,
AFBB96911FBA3A9A0008D868 /* MVMCoreNavigationOperation.h in Headers */,
AFBB968F1FBA3A9A0008D868 /* MVMCoreNavigationObject.h in Headers */,
8876D5EC1FB50AB000EB2E3D /* NSDictionary+MFConvenience.h in Headers */,
AFFCFA651FCCC0D700FD0730 /* MVMCoreLoadingOverlayDelegateProtocol.h in Headers */,
AF1201832108C9B400E2F592 /* MVMCoreViewManagerViewControllerProtocol.h in Headers */,
AFBB96981FBA3A9A0008D868 /* MVMCoreAlertController.h in Headers */,
881D26961FCC9D180079C521 /* MVMCoreOperation.h in Headers */,
8876D5EA1FB50AB000EB2E3D /* NSDecimalNumber+MFConvenience.h in Headers */,
AF43A7201FC5D2BA008E9347 /* MVMCoreViewManagerProtocol.h in Headers */,
881D26951FCC9D180079C521 /* MVMCoreErrorObject.h in Headers */,
AFBB96341FBA34310008D868 /* MVMCoreErrorConstants.h in Headers */,
A332F33F20C7231700DCD9D9 /* MVMCoreViewControllerAnimatedTransitioning.h in Headers */,
AFBB968D1FBA3A9A0008D868 /* MVMCoreNavigationHandler.h in Headers */,
AFBB96951FBA3A9A0008D868 /* MVMCorePresentationDelegateProtocol.h in Headers */,
AFED77A51FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.h in Headers */,
AF43A5871FBB67D6008E9347 /* MVMCoreActionUtility.h in Headers */,
AF43A6FD1FBE2F65008E9347 /* Reachability.h in Headers */,
AFBB96601FBA3A570008D868 /* MVMCoreLoadObject.h in Headers */,
AF43A7061FC4D7A2008E9347 /* MVMCoreObject.h in Headers */,
AFED77A11FCCA29400BAE689 /* MVMCoreViewControllerNibMappingObject.h in Headers */,
AF43A71B1FC5BEBB008E9347 /* MVMCoreViewControllerProtocol.h in Headers */,
AFBB96381FBA39E70008D868 /* MVMCoreLoadDelegateProtocol.h in Headers */,
@ -740,6 +735,7 @@
8876D5E81FB50AB000EB2E3D /* NSArray+MFConvenience.h in Headers */,
AFBB96621FBA3A570008D868 /* MVMCoreRequestParameters.h in Headers */,
AFBB96531FBA3A570008D868 /* MVMCoreLoadHandler.h in Headers */,
AFEEE81E1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.h in Headers */,
AF43A7011FC4B227008E9347 /* MVMCoreGlobalLoadProtocol.h in Headers */,
AF43A5771FBA5B7C008E9347 /* MVMCoreJSONConstants.h in Headers */,
AFBB96631FBA3A570008D868 /* MVMCoreLoadRequestOperation.h in Headers */,
@ -747,10 +743,13 @@
8876D5CE1FB50A9E00EB2E3D /* MVMCore.h in Headers */,
AF43A57B1FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.h in Headers */,
AFBB96B81FBA3CEC0008D868 /* MVMCoreActionDelegateProtocol.h in Headers */,
AFBB968B1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.h in Headers */,
AF43A70B1FC4F415008E9347 /* MVMCoreCache.h in Headers */,
AFEA17A8209B6A1C00BC6740 /* MVMCoreBlockOperation.h in Headers */,
AF43A5831FBB66DE008E9347 /* MVMCoreConstants.h in Headers */,
AFED77A81FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.h in Headers */,
AFEEE8191FCDEB8D00B5EDD0 /* MVMCoreLoggingDelegateProtocol.h in Headers */,
AFBB96B91FBA3CEC0008D868 /* MVMCoreActionHandler.h in Headers */,
AF43A74C1FC6109F008E9347 /* MVMCoreSessionObject.h in Headers */,
8876D5F41FB50AB000EB2E3D /* UILabel+MFCustom.h in Headers */,
AFFCFA681FCCC0D700FD0730 /* MVMCoreLoadingViewControllerProtocol.h in Headers */,
@ -853,100 +852,83 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AF69D4ED286E5D8C00BC6862 /* ActionCancelHandler.swift in Sources */,
AFED77A71FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.m in Sources */,
946EE1A7237B5B1C0036751F /* ModelRegistry.swift in Sources */,
AFBB96641FBA3A570008D868 /* MVMCoreLoadHandler.m in Sources */,
AFFCFA671FCCC0D700FD0730 /* MVMCoreLoadingOverlayHandler.m in Sources */,
AF8D13392774EA1D008AF4A9 /* ActionOpenUrlHandler.swift in Sources */,
AF925DEE28BD35A2008E8677 /* NavigationHandler.swift in Sources */,
AF130B8E2788DF6E00C6C03C /* OpenURLOptionsModel.swift in Sources */,
AF69D4EB286E586200BC6862 /* ActionRestartHandler.swift in Sources */,
AFBB968E1FBA3A9A0008D868 /* MVMCoreNavigationHandler.m in Sources */,
BB780ADF250F8C890030BD2F /* ActionNoopModel.swift in Sources */,
D2E1FAD92260C3E400AEFD8C /* DelegateObject.swift in Sources */,
01F2A03623A80A7300D954D8 /* ActionModelProtocol.swift in Sources */,
AFBB96991FBA3A9A0008D868 /* MVMCoreAlertController.m in Sources */,
881D26941FCC9D180079C521 /* MVMCoreOperation.m in Sources */,
AFED77A41FCCA29400BAE689 /* MVMCoreViewControllerMappingObject.m in Sources */,
01C851CF23CF7B260021F976 /* JSONMap.swift in Sources */,
01F2A04C23A82B1B00D954D8 /* ActionCallModel.swift in Sources */,
8876D5ED1FB50AB000EB2E3D /* NSDictionary+MFConvenience.m in Sources */,
AF686FDA2A8A876A008F666A /* NavigationOperation.swift in Sources */,
605A9A2A2ABD712F00487E47 /* MVMCoreLoggingHandler.swift in Sources */,
AFBB968C1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.m in Sources */,
AFBB96BA1FBA3CEC0008D868 /* MVMCoreActionHandler.m in Sources */,
AFBB96ED1FBA4A260008D868 /* MFHardCodedServerResponse.m in Sources */,
AF43A74D1FC6109F008E9347 /* MVMCoreSessionObject.m in Sources */,
D282AAB62240085300C46919 /* MVMCoreGetterUtility+Extension.swift in Sources */,
AFA4931E29E5C988001A9663 /* MVMCoreActionUtility+Extension.swift in Sources */,
AFBB96901FBA3A9A0008D868 /* MVMCoreNavigationObject.m in Sources */,
1DAD0FFE26AAB40000216E83 /* ActionRunJavaScriptModel.swift in Sources */,
946EE1AB237B5C940036751F /* Decoder.swift in Sources */,
5878F0B22BDAA63E00ADE23D /* ReadableDecodingErrors.swift in Sources */,
2723337D28BD53C2004EAEE0 /* Date+Extension.swift in Sources */,
AF70699A287DD02400077CF6 /* ActionContactHandler.swift in Sources */,
AF69D4F3286E9DCE00BC6862 /* ActionActionsHandler.swift in Sources */,
946EE1BC237B691A0036751F /* ActionOpenPageModel.swift in Sources */,
0A42538F23F3414800554656 /* Codable+Helpers.swift in Sources */,
881D26931FCC9D180079C521 /* MVMCoreErrorObject.m in Sources */,
94C014D124211869005811A9 /* ActionRestartModel.swift in Sources */,
D288D5F526C6EFE000A5C365 /* MVMCoreLoggingHandler+Extension.swift in Sources */,
946EE1B0237B5EF70036751F /* JSONHelper.swift in Sources */,
30349BF21FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m in Sources */,
D2DEDCB923C6400600C44CC4 /* UnitInterval.swift in Sources */,
94C014D3242119E6005811A9 /* ActionPreviousSubmitModel.swift in Sources */,
6079EDCE2AD97AA5004B7A85 /* MVMCoreLoggingDelegateProtocol.swift in Sources */,
AF60A7F2289212CA00919EEB /* MVMError.swift in Sources */,
8876D5E91FB50AB000EB2E3D /* NSArray+MFConvenience.m in Sources */,
D27073B725BB45C4001C7246 /* ActionActionsModel.swift in Sources */,
946EE1B2237B5F260036751F /* JSONValue.swift in Sources */,
0ACC81A22613C73800A9C886 /* ActionContactModel.swift in Sources */,
AF69D4EF286E612800BC6862 /* ActionOpenSMSHandler.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 */,
AF69D4E9286E54D500BC6862 /* ActionCallHandler.swift in Sources */,
AFBB96351FBA34310008D868 /* MVMCoreErrorConstants.m in Sources */,
AF43A5881FBB67D6008E9347 /* MVMCoreActionUtility.m in Sources */,
AFED77A61FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.m in Sources */,
AF4955E22BAB1EB200567276 /* MVMCoreCache+Extension.swift in Sources */,
016CF36925FA6DD400B82A1F /* ClientParameterHandler.swift in Sources */,
5846ABF42B44BB9000FA6C76 /* Collection+Safe.swift in Sources */,
AF69D4F7286EA0B800BC6862 /* ActionPreviousSubmitHandler.swift in Sources */,
AF43A57C1FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.m in Sources */,
0AFF597A23FC6E60005C24E8 /* ActionShareModel.swift in Sources */,
AF69D4F5286E9F5900BC6862 /* ActionSettingHandler.swift in Sources */,
D27073CD25BB4CEF001C7246 /* MVMCoreActionHandler.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 */,
2723337B28BD534D004EAEE0 /* MVMCoreEvent.swift in Sources */,
AF43A7071FC4D7A2008E9347 /* MVMCoreObject.m in Sources */,
94C014D924212360005811A9 /* ActionSettingModel.swift in Sources */,
D2DEDCB723C63F3B00C44CC4 /* Clamping.swift in Sources */,
AF70699E2880D01400077CF6 /* ActionShareHandler.swift in Sources */,
01DF561421F90ADC00CC099B /* Dictionary+MFConvenience.swift in Sources */,
EA3B264C25FC0B7600008074 /* ModelHandlerProtocol.swift in Sources */,
AF7069A02880F0EB00077CF6 /* ActionOpenPageHandler.swift in Sources */,
AFBB96B11FBA3B590008D868 /* MVMCoreDispatchUtility.m in Sources */,
946EE1A3237B59C30036751F /* ModelProtocol.swift in Sources */,
AF6870292A9CD0E9008F666A /* MVMCoreLoadHandler.swift in Sources */,
AFEA17A9209B6A1C00BC6740 /* MVMCoreBlockOperation.m in Sources */,
AF43A70A1FC4F415008E9347 /* MVMCoreCache.m in Sources */,
AF43A6FF1FBE3252008E9347 /* Reachability.m in Sources */,
AF3A3F3F2ACC716A005094B2 /* MVMCoreObject.swift in Sources */,
EA09CD62282ACDDB00A7835F /* Decoder+UserInfo.swift in Sources */,
EA09CD99282BF83600A7835F /* DecodableDefault.swift in Sources */,
01C851D123CF97FE0021F976 /* ActionBackModel.swift in Sources */,
D27073D125BB844B001C7246 /* ActionDelegateProtocol.swift in Sources */,
D27073D125BB844B001C7246 /* MVMCoreActionDelegateProtocol+Extension.swift in Sources */,
AFBB96921FBA3A9A0008D868 /* MVMCoreNavigationOperation.m in Sources */,
AFBB96611FBA3A570008D868 /* MVMCoreLoadObject.m in Sources */,
AF787413286DEF8B00670588 /* ActionBackHandler.swift in Sources */,
946EE1B4237B619D0036751F /* Encoder.swift in Sources */,
AF60A7F4289212EB00919EEB /* MVMCoreError.swift in Sources */,
AFBB96941FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.m in Sources */,
94C014D524211AF0005811A9 /* ActionCancelModel.swift in Sources */,
AF43A5841FBB66DE008E9347 /* MVMCoreConstants.m in Sources */,
AF69D4F1286E9D8000BC6862 /* ActionNoopHandler.swift in Sources */,
016FF6F2259A4FCC00F5E4AA /* ClientParameterModel.swift in Sources */,
D2DEDCBB23C65BC300C44CC4 /* Percent.swift in Sources */,
AF686FDE2A8D29CD008F666A /* MVMCoreLoadRequestOperation+Extension.swift in Sources */,
60CBD0542A02397A00056CB0 /* MVMCoreSessionTimeHandler.swift in Sources */,
AFBB966A1FBA3A570008D868 /* MVMCoreLoadRequestOperation.m in Sources */,
D268D82C26700292008BD413 /* MVMCoreViewManagerViewControllerProtocolHelper.m in Sources */,
0AEBB84625FA75C000EA80EE /* ActionOpenSMSModel.swift in Sources */,
@ -976,8 +958,8 @@
/* Begin XCBuildConfiguration section */
8876D5CF1FB50A9E00EB2E3D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 581FABEE2A71D0E6003A8508 /* mvmcore_dev.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
@ -1033,7 +1015,6 @@
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
@ -1041,8 +1022,8 @@
};
8876D5D01FB50A9E00EB2E3D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5836B8E22A4338DF002553D9 /* mvmcore.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
@ -1102,6 +1083,7 @@
8876D5D21FB50A9E00EB2E3D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BITCODE_GENERATION_MODE = bitcode;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
@ -1113,10 +1095,10 @@
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../SharedFrameworks";
INFOPLIST_FILE = MVMCore/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
MARKETING_VERSION = 3.1;
MARKETING_VERSION = 3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCore;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
@ -1129,6 +1111,7 @@
8876D5D31FB50A9E00EB2E3D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BITCODE_GENERATION_MODE = bitcode;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
@ -1140,10 +1123,10 @@
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../SharedFrameworks";
INFOPLIST_FILE = MVMCore/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
MARKETING_VERSION = 3.1;
MARKETING_VERSION = 3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCore;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;

View File

@ -1,51 +0,0 @@
//
// ActionActionsHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 6/30/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
open class ActionActionsHandler: MVMCoreJSONActionHandlerProtocol {
required public init() {}
open func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
guard let model = model as? ActionActionsModel else { return }
let actions = JSON.arrayForKey("actions")
if model.concurrent {
await withThrowingTaskGroup(of: Void.self) { group in
for case let (index, action as [AnyHashable: Any]) in actions.enumerated() {
group.addTask{
try await MVMCoreActionHandler.shared()?.handleAction(with: model.actions[index], json: action, additionalData: additionalData, delegateObject: delegateObject)
}
}
}
} else {
for case let (index, action as [AnyHashable: Any]) in actions.enumerated() {
try Task.checkCancellation()
try await MVMCoreActionHandler.shared()?.handleAction(with: model.actions[index], json: action, additionalData: additionalData, delegateObject: delegateObject)
}
}
}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
guard let model = model as? ActionActionsModel else { return }
if model.concurrent {
// TODO: inspect warning.
await withThrowingTaskGroup(of: Void.self) { group in
for action in model.actions {
group.addTask{
try await (delegateObject?.actionDelegate as? ActionDelegateProtocol)?.performAction(with: action, additionalData: additionalData, delegateObject: delegateObject)
}
}
}
} else {
for action in model.actions {
try Task.checkCancellation()
try await (delegateObject?.actionDelegate as? ActionDelegateProtocol)?.performAction(with: action, additionalData: additionalData, delegateObject: delegateObject)
}
}
}
}

View File

@ -1,26 +0,0 @@
//
// ActionBackHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 6/30/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
open class ActionBackHandler: MVMCoreJSONActionHandlerProtocol {
required public init() {}
open func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
if let closure = delegateObject?.actionDelegate?.handleBackAction {
// Legacy code will use the old handler function and break the task chain here.
closure(JSON, additionalData)
} else {
try await execute(with: model, delegateObject: delegateObject, additionalData: additionalData)
}
}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
await NavigationHandler.shared().removeCurrentViewController(delegateObject: delegateObject)
}
}

View File

@ -1,19 +0,0 @@
//
// ActionCallHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 6/30/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
open class ActionCallHandler: MVMCoreActionHandlerProtocol {
required public init() {}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
guard let model = model as? ActionCallModel else { return }
// https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/PhoneLinks/PhoneLinks.html#//apple_ref/doc/uid/TP40007899-CH6-SW1
try await ActionOpenUrlHandler.openURL(with: "tel://\(model.callNumber)")
}
}

View File

@ -1,20 +0,0 @@
//
// ActionCancelHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 6/30/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
open class ActionCancelHandler: MVMCoreJSONActionHandlerProtocol {
required public init() {}
open func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
delegateObject?.actionDelegate?.handleCancel?(JSON, additionalData: additionalData)
}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
}
}

View File

@ -1,126 +0,0 @@
//
// ActionContactHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 7/12/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
import ContactsUI
open class ActionContactHandler: NSObject, MVMCoreActionHandlerProtocol, CNContactPickerDelegate, CNContactViewControllerDelegate {
/// A continuation to keep the process running until we are finished.
private var continuation: CheckedContinuation<Bool, Never>?
required public override init() {}
/// Sets the continuation and runs the closure.
private func continueInTask(with closure: @escaping () async -> Void) async {
let _: Bool = await withCheckedContinuation { continuation in
self.continuation = continuation
Task(priority: .userInitiated) {
await closure()
}
}
}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) async throws {
guard let model = model as? ActionContactModel else { return }
switch model.approach {
case .add:
await continueInTask {
await MainActor.run {
let controller = CNContactPickerViewController()
// Setting to accessibilityValue as a workaround to pass data via the delegate function.
controller.view.accessibilityIdentifier = model.phoneNumber
controller.delegate = self
Task(priority: .userInitiated) {
await NavigationHandler.shared().present(viewController: controller, delegateObject: delegateObject, animated: true)
}
}
}
case .create:
let contact = CNMutableContact()
let phone = CNLabeledValue(label: CNLabelOther, value: CNPhoneNumber(stringValue: model.phoneNumber))
contact.phoneNumbers = [phone]
if let givenName = model.firstName {
contact.givenName = givenName
}
if let familyName = model.lastName {
contact.familyName = familyName
}
let store = CNContactStore()
await continueInTask {
await MainActor.run {
let controller = CNContactViewController(forNewContact: contact)
controller.contactStore = store
controller.delegate = self
Task(priority: .userInitiated) {
await NavigationHandler.shared().push(viewController: controller, delegateObject: delegateObject, animated: true)
}
}
}
case .view:
let symbols = CharacterSet(charactersIn: ".+()-  ")
let contactPhoneNumber = model.phoneNumber.components(separatedBy: symbols).joined(separator: "")
let number = CNPhoneNumber(stringValue: contactPhoneNumber)
let contactPredicate = CNContact.predicateForContacts(matching: number)
let displayedKeys = [await CNContactViewController.descriptorForRequiredKeys()]
let store = CNContactStore()
let viewContact = try store.unifiedContacts(matching: contactPredicate, keysToFetch: displayedKeys).first
await continueInTask {
await MainActor.run {
let controller = CNContactViewController(forNewContact: viewContact)
controller.contactStore = store
controller.delegate = self
Task(priority: .userInitiated) {
await NavigationHandler.shared().push(viewController: controller, delegateObject: delegateObject, animated: true)
}
}
}
}
}
public func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
Task(priority: .userInitiated) {
await NavigationHandler.shared().popTopViewController()
self.continuation?.resume(returning: true)
}
}
public func contactViewController(_ viewController: CNContactViewController, shouldPerformDefaultActionFor property: CNContactProperty) -> Bool {
true
}
public func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
Task(priority: .userInitiated) {
await NavigationHandler.shared().dismissTopViewController()
self.continuation?.resume(returning: true)
}
}
public func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
// This is a means to pass the data to this delegate function.
guard let phoneNumber = picker.view.accessibilityIdentifier else { return }
let store = CNContactStore()
let existingContact = contact.mutableCopy() as! CNMutableContact
let number = CNPhoneNumber(stringValue: phoneNumber)
let labelValue = CNLabeledValue(label: CNLabelOther, value: number)
var phoneNumbers = [labelValue]
phoneNumbers.append(contentsOf: existingContact.phoneNumbers)
existingContact.phoneNumbers = phoneNumbers
Task { @MainActor in
let controller = CNContactViewController(forNewContact: existingContact)
controller.contactStore = store
controller.delegate = self
Task(priority: .userInitiated) {
await NavigationHandler.shared().push(viewController: controller)
}
}
}
}

View File

@ -1,27 +0,0 @@
//
// MVMCoreActionDelegateProtocolExtension.swift
// MVMCore
//
// Created by Scott Pfeil on 1/22/21.
// Copyright © 2021 myverizon. All rights reserved.
//
import Foundation
public protocol ActionDelegateProtocol: MVMCoreActionDelegateProtocol {
/// Asks the delegate to perform the action.
func performAction(with model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) async throws
}
public extension ActionDelegateProtocol {
func performAction(with model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) async throws {
try await MVMCoreActionHandler.shared()?.handleAction(with: model, additionalData: additionalData, delegateObject: delegateObject)
}
}
public extension MVMCoreActionDelegateProtocol {
func action(with model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) async throws {
try await (self as? ActionDelegateProtocol)?.performAction(with: model, additionalData: additionalData, delegateObject: delegateObject)
}
}

View File

@ -1,15 +0,0 @@
//
// ActionNoopHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 6/30/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
open class ActionNoopHandler: MVMCoreActionHandlerProtocol {
required public init() {}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {}
}

View File

@ -1,127 +0,0 @@
//
// OpenPageHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 7/14/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
open class ActionOpenPageHandler: MVMCoreJSONActionHandlerProtocol {
required public init() {}
func requestParamaters(for model: ActionOpenPageModel) -> MVMCoreRequestParameters {
let requestParameters = model.requestParameters.copy() as! MVMCoreRequestParameters
if let pageType = requestParameters.pageType {
// Re-evaluate required & optional modules as action models might have been generated prior to recent additions to the mapping.
requestParameters.modules = MVMCoreViewControllerMappingObject.shared()?.modulesRequired(forPageType: pageType) as? [String]
requestParameters.optionalModules = MVMCoreViewControllerMappingObject.shared()?.modulesOptional(forPageType: pageType) as? [String]
}
return requestParameters
}
open func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
guard let model = model as? ActionOpenPageModel else { return }
let requestParameters = requestParamaters(for: model)
do {
if let closure = delegateObject?.actionDelegate?.handleOpenPage {
// Legacy code will use the old handler function and break the task chain here.
closure(requestParameters, JSON, additionalData)
} else if let operation = try await performRequestAddingClientParameters(with: requestParameters, model: model, delegateObject: delegateObject, additionalData: additionalData) {
await withCheckedContinuation { continuation in
operation.completionBlock = {
continuation.resume()
}
}
if operation.error != nil, let fallbackResponseJson = model.fallbackResponse?.toJSON() {
await runFallback(response: fallbackResponseJson, requestParameters: requestParameters, delegateObject: delegateObject, additionalData: additionalData)
}
}
} catch {
try handle(error: error, model: model, delegateObject: delegateObject)
}
}
/// Given backup JSON data, run it through the load handler.
fileprivate func runFallback(response: JSONDictionary, requestParameters: MVMCoreRequestParameters, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async {
guard let loadHandler = MVMCoreLoadHandler.sharedGlobal(), let fallbackLoadObject = MVMCoreLoadObject(requestParameters: requestParameters, dataForPage: additionalData, delegateObject: delegateObject)
else { return }
let (loadObject, errorObject) = await MVMCoreLoadRequestOperation.processJSON(fromServer: response, loadObject: fallbackLoadObject)
if let errorObject {
MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
} else {
let operation = loadHandler.loadObject(loadObject)
await withCheckedContinuation { continuation in
operation.completionBlock = {
continuation.resume()
}
}
}
}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
guard let model = model as? ActionOpenPageModel else { return }
do {
// Pass through the old function for legacy open page.
let json = try MVMCoreActionHandler.convertActionToJSON(model)
try await performAction(with: json, model: model, delegateObject: delegateObject, additionalData: additionalData)
} catch {
try handle(error: error, model: model, delegateObject: delegateObject)
}
}
/// Adds client parameters and makes calls performRequest()
open func performRequestAddingClientParameters(with requestParameters: MVMCoreRequestParameters, model: ActionOpenPageModel, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws -> MVMCoreLoadRequestOperation? {
let actionUUID = MVMCoreActionHandler.getUUID(additionalData: additionalData) ?? UUID().uuidString
requestParameters.identifier = actionUUID
if !requestParameters.backgroundRequest, let pageType = requestParameters.pageType {
MVMCoreLoggingHandler.shared()?.logCoreEvent(.pageStarted(pageType: pageType, requestUUID: actionUUID))
}
// Adds any client parameters to the request parameters.
if let parametersToFetch = model.clientParameters,
let fetchedParameters = await ClientParameterHandler().getClientParameters(
with: parametersToFetch,
requestParameters: requestParameters.parameters as? [String : Any] ?? [:],
actionId: actionUUID,
showLoadingOverlay: !requestParameters.backgroundRequest) {
requestParameters.add(fetchedParameters)
}
try Task.checkCancellation()
let coreLoadRequestOperation = MVMCoreLoadHandler.sharedGlobal()?.loadRequest(requestParameters, dataForPage: additionalData, delegateObject: delegateObject)
coreLoadRequestOperation?.identifier = actionUUID
return coreLoadRequestOperation
}
/// Ensures background requests do not have showing errors.
private func handle(error: Error, model: ActionOpenPageModel, delegateObject: DelegateObject?) throws {
switch error {
case MVMCoreError.errorObject(let errorObject):
errorObject.silentError = model.background == true
throw MVMCoreError.errorObject(errorObject)
default:
let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: MVMCoreActionHandler.getErrorLocation(with: delegateObject?.actionDelegate, actionType: model.actionType))!
errorObject.silentError = model.background == true
throw MVMCoreError.errorObject(errorObject)
}
}
}
public extension ClientParameterHandler {
/// Iterates through the clientParameters list. Gets values from the individual handlers and attaches the parameters to extraParameters.
func getClientParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String, showLoadingOverlay: Bool) async -> [String: Any]? {
if showLoadingOverlay {
MVMCoreLoadingOverlayHandler.sharedLoadingOverlay()?.startLoading()
}
defer {
if showLoadingOverlay {
MVMCoreLoadingOverlayHandler.sharedLoadingOverlay()?.stopLoading(true)
}
}
return await getParameters(with: model, requestParameters: requestParameters, actionId: actionId)
}
}

View File

@ -1,145 +0,0 @@
//
// ActionModel.swift
// MVMCore
//
// Created by Suresh, Kamlesh on 10/3/19.
// Copyright © 2019 Suresh, Kamlesh. All rights reserved.
//
import Foundation
public struct ActionOpenPageModel: ActionModelProtocol, ActionOpenPageProtocol, ClientParameterActionProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "openPage"
public var actionType: String = identifier
public var pageType: String
public var modules: [String]?
public var baseURL: URL?
public var appContext: String?
public var requestURL: URL?
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
public var presentationStyle: String?
public var tabBarIndex: Int?
public var background: Bool?
public var clientParameters: ClientParameterModel?
public var customTimeoutTime: TimeInterval?
public var fallbackResponse: JSONValueDictionary?
public var requestParameters: MVMCoreRequestParameters
//--------------------------------------------------
// MARK: - Initialzier
//--------------------------------------------------
public init(pageType: String, presentationStyle: String? = nil, extraParameters: JSONValueDictionary? = nil, analyticsData: JSONValueDictionary? = nil, tabBarIndex: Int? = nil, background: Bool? = nil) {
self.pageType = pageType
self.presentationStyle = presentationStyle
self.extraParameters = extraParameters
self.analyticsData = analyticsData
self.tabBarIndex = tabBarIndex
self.background = background
requestParameters = MVMCoreRequestParameters(pageType: pageType, extraParameters: extraParameters.toJSON())!
if let background = background {
requestParameters.backgroundRequest = background
}
requestParameters.actionMap = toJSON()
}
//--------------------------------------------------
// MARK: - Codable
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case actionType
case pageType
case modules
case baseURL
case appContext
case requestURL
case extraParameters
case analyticsData
case presentationStyle
case tabBarIndex
case background
case clientParameters
case customTimeoutTime
case fallbackResponse
}
public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
pageType = try typeContainer.decode(String.self, forKey: .pageType)
baseURL = try typeContainer.decodeIfPresent(URL.self, forKey: .baseURL)
appContext = try typeContainer.decodeIfPresent(String.self, forKey: .appContext)
requestURL = try typeContainer.decodeIfPresent(URL.self, forKey: .requestURL)
modules = try typeContainer.decodeIfPresent([String].self, forKey: .modules)
presentationStyle = try typeContainer.decodeIfPresent(String.self, forKey: .presentationStyle)
tabBarIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .tabBarIndex)
background = try typeContainer.decodeIfPresent(Bool.self, forKey: .background)
clientParameters = try typeContainer.decodeIfPresent(ClientParameterModel.self, forKey: .clientParameters)
customTimeoutTime = try typeContainer.decodeIfPresent(TimeInterval.self, forKey: .customTimeoutTime)
extraParameters = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .extraParameters)
analyticsData = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .analyticsData)
fallbackResponse = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .fallbackResponse)
requestParameters = MVMCoreRequestParameters(pageType: pageType, additionalModules: modules ?? [], extraParameters: extraParameters.toJSON())!
requestParameters.contextRoot = appContext
requestParameters.alternateBaseURL = baseURL
requestParameters.url = requestURL
requestParameters.customTimeoutTime = customTimeoutTime as NSNumber?
if let background = background {
requestParameters.backgroundRequest = background
}
requestParameters.actionMap = toJSON()
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(pageType, forKey: .pageType)
try container.encode(actionType, forKey: .actionType)
try container.encodeIfPresent(baseURL, forKey: .baseURL)
try container.encodeIfPresent(appContext, forKey: .appContext)
try container.encodeIfPresent(requestURL, forKey: .requestURL)
try container.encodeIfPresent(modules, forKey: .modules)
try container.encodeIfPresent(presentationStyle, forKey: .presentationStyle)
try container.encodeIfPresent(tabBarIndex, forKey: .tabBarIndex)
try container.encodeIfPresent(background, forKey: .background)
try container.encodeIfPresent(clientParameters, forKey: .clientParameters)
try container.encodeIfPresent(customTimeoutTime, forKey: .customTimeoutTime)
try container.encodeIfPresent(extraParameters, forKey: .extraParameters)
try container.encodeIfPresent(analyticsData, forKey: .analyticsData)
try container.encodeIfPresent(fallbackResponse, forKey: .fallbackResponse)
}
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return pageType == model.pageType
&& baseURL == model.baseURL
&& appContext == model.appContext
&& requestURL == model.requestURL
&& modules == model.modules
&& presentationStyle == model.presentationStyle
&& tabBarIndex == model.tabBarIndex
&& background == model.background
&& clientParameters == model.clientParameters
&& customTimeoutTime == model.customTimeoutTime
&& extraParameters == model.extraParameters
&& analyticsData == model.analyticsData
&& fallbackResponse == model.fallbackResponse
}
}
extension ActionOpenPageModel: CustomDebugStringConvertible {
public var debugDescription: String {
if let requestURL {
return "\(Self.self) for \(pageType) @ \(requestURL)"
} else {
return "\(Self.self) for \(pageType)"
}
}
}

View File

@ -1,33 +0,0 @@
//
// ActionOpenSMSHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 6/30/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
extension String {
public enum StringError: Error {
case addingPercentEncoding(string: String)
}
func addingPercentEncodingThrowable(withAllowedCharacters characterSet: CharacterSet) throws -> String {
guard let string = addingPercentEncoding(withAllowedCharacters: characterSet) else {
throw StringError.addingPercentEncoding(string: self)
}
return string
}
}
open class ActionOpenSMSHandler: MVMCoreActionHandlerProtocol {
required public init() {}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
guard let model = model as? ActionOpenSMSModel else { return }
// https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/SMSLinks/SMSLinks.html#:~:text=Note%3A%20SMS%20text%20links%20are,number%20of%20the%20SMS%20message.
let string = try "sms:\(model.phoneNumber)&body=\(model.message ?? "")".addingPercentEncodingThrowable(withAllowedCharacters: CharacterSet.urlQueryAllowed)
try await ActionOpenUrlHandler.openURL(with: string)
}
}

View File

@ -8,89 +8,43 @@
import Foundation
public extension URL {
enum URLError: MVMError, CustomStringConvertible {
case invalid(string: String)
public var description: String {
switch self {
case URLError.invalid(let string):
return "Failed to create url with string: \(string)"
}
}
}
static func createURL(with string: String) throws -> URL {
guard let url = URL(string: string) else {
throw URL.URLError.invalid(string: string)
}
return url
}
}
open class ActionOpenUrlHandler: MVMCoreJSONActionHandlerProtocol {
open class ActionOpenUrlHandler: MVMCoreActionHandlerProtocol {
required public init() {}
public enum URLError: MVMError, CustomStringConvertible {
case failedToOpen(url: URL)
public var description: String {
switch self {
case ActionOpenUrlHandler.URLError.failedToOpen(let url):
return "Failed to open url: \(url.absoluteString)"
}
}
public var errorCode: Int {
return ErrorCode.linkawayFailed.rawValue
}
}
/// Creates a url and calls open(url: URL)
public static func openURL(with string: String) async throws {
let url = try URL.createURL(with: string)
try await ActionOpenUrlHandler.open(url: url)
}
/// Opens the url using UIApplication open(url:). Throws URLError.failedToOpen if it fails.
@MainActor
public static func open(url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:]) async throws {
try await withCheckedThrowingContinuation { continuation in
UIApplication.shared.open(url, options: options) { successful in
if successful {
continuation.resume()
} else {
continuation.resume(throwing: ActionOpenUrlHandler.URLError.failedToOpen(url: url))
}
}
} as Void
}
open func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
try await execute(with: model, delegateObject: delegateObject, additionalData: additionalData)
}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
open func handleAction(_ model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) {
guard let model = model as? ActionOpenUrlModel else { return }
// Try loading the app url first, otherwise fall back to browser url.
if let appURL = model.appURL {
do {
try await ActionOpenUrlHandler.open(url: appURL, options: model.appURLOptions?.options ?? [:])
MVMCoreDispatchUtility.performBlock(onMainThread: { [self] in
// Try loading the app url first, otherwise fall back to browser url.
guard let appURL = model.appURL else {
openURL(model: model, additionalData: additionalData, delegateObject: delegateObject)
return
} catch {
// Log error and continue
MVMCoreLoggingHandler.shared()?.handleDebugMessage("Failed to open app url: \(appURL)")
let isUsingUniversalLinksOnly = model.appURLOptions?.options[.universalLinksOnly] as? Bool ?? false
if !isUsingUniversalLinksOnly, let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: MVMCoreActionHandler.getErrorLocation(with: delegateObject?.actionDelegate, actionType: model.actionType)) {
MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
}
}
}
try await openURL(model: model, additionalData: additionalData, delegateObject: delegateObject)
UIApplication.shared.open(appURL, options: model.appURLOptions?.options ?? [:]) { loaded in
guard !loaded else { return }
MVMCoreLoggingHandler.shared()?.handleDebugMessage("Failed to open app url: \(appURL)")
openURL(model: model, additionalData: additionalData, delegateObject: delegateObject)
}
})
}
open func openURL(model: ActionOpenUrlModel, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) async throws {
try await ActionOpenUrlHandler.open(url: model.browserUrl)
/// Opens the url.
open func openURL(model: ActionOpenUrlModel, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) {
UIApplication.shared.open(model.browserUrl, options: [:]) { [self] loaded in
guard !loaded else { return }
handleError(model: model, additionalData: additionalData, delegateObject: delegateObject)
}
}
/// Handles any url loading errors.
open func handleError(model: ActionOpenUrlModel, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) {
if let error = MVMCoreErrorObject(title: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorTitle),
message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess),
messageToLog: "Unable to load URL: \(String(describing: model.browserUrl))", code: ErrorCode.linkawayFailed.rawValue,
domain: ErrorDomainNative,
location: MVMCoreActionHandler.getErrorLocation(with: delegateObject?.actionDelegate, actionType: ActionOpenUrlModel.identifier)) {
MVMCoreDispatchUtility.performBlock(inBackground: {
MVMCoreActionHandler.shared()?.handleActionError(error, actionInformation: model.toJSON(), additionalData: additionalData, delegateObject: delegateObject)
})
}
}
}

View File

@ -8,18 +8,18 @@
import Foundation
open class ActionOpenUrlModel: ActionModelProtocol {
@objcMembers open class ActionOpenUrlModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
open class var identifier: String { "openURL" }
open var actionType: String = ActionOpenUrlModel.identifier
open var browserUrl: URL
open var appURL: URL?
open var appURLOptions: OpenUrlOptionsModel?
open var extraParameters: JSONValueDictionary?
open var analyticsData: JSONValueDictionary?
public static var identifier: String = "openURL"
public var actionType: String = ActionOpenUrlModel.identifier
public var browserUrl: URL
public var appURL: URL?
public var appURLOptions: OpenUrlOptionsModel?
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
//--------------------------------------------------
// MARK: - Initialzier
@ -60,13 +60,4 @@ open class ActionOpenUrlModel: ActionModelProtocol {
try container.encodeIfPresent(extraParameters, forKey: .extraParameters)
try container.encodeIfPresent(analyticsData, forKey: .analyticsData)
}
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return extraParameters == model.extraParameters
&& analyticsData == model.analyticsData
&& browserUrl == model.browserUrl
&& appURL == model.appURL
&& appURLOptions == model.appURLOptions
}
}

View File

@ -1,47 +0,0 @@
//
// ActionPreviousSubmitHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 6/30/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
/// Makes the previous request, needs the delegate for this
open class ActionPreviousSubmitHandler: MVMCoreJSONActionHandlerProtocol {
required public init() {}
private var json: [AnyHashable: Any]?
// Conform to MVMCoreJSONActionHandlerProtocol To allow for legacy handleOpenPage delegate
open func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
json = JSON
try await execute(with: model, delegateObject: delegateObject, additionalData: additionalData)
}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
guard let loadObject = (delegateObject?.actionDelegate as? MVMCoreViewControllerProtocol)?.loadObject,
let previousRequest = loadObject?.requestParameters else { return }
// Combines data
var data = loadObject?.dataForPage
if let previousData = data,
let additionalData = additionalData {
data = previousData.merging(additionalData) {(new,_) in new}
}
if let _ = delegateObject?.actionDelegate?.handleOpenPage {
// Legacy handling. Will lose the task.
let json = try json ?? MVMCoreActionHandler.convertActionToJSON(model)
delegateObject?.actionDelegate?.handleOpenPage?(for: previousRequest, actionInformation: json, additionalData: additionalData)
} else {
guard let operation = MVMCoreLoadHandler.sharedGlobal()?.loadRequest(previousRequest, dataForPage: additionalData, delegateObject: delegateObject) else { return }
await withCheckedContinuation { continuation in
operation.completionBlock = {
continuation.resume()
}
}
}
}
}

View File

@ -1,42 +0,0 @@
//
// ActionRestartHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 6/30/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
open class ActionRestartHandler: MVMCoreActionHandlerProtocol {
required public init() {}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
guard let model = model as? ActionRestartModel else { return }
let _: Void = try await withCheckedThrowingContinuation { continuation in
// Invalidates the session before restarting.
Task { @MainActor in
MVMCoreSessionTimeHandler.shared().invalidateSession({ error in
if let error = error {
guard error.code != NSURLErrorCancelled else {
continuation.resume()
return
}
continuation.resume(throwing: MVMCoreError.errorObject(error))
return
}
if let sessionObject = MVMCoreSessionObject.sharedGlobal() {
if model.hardReset {
sessionObject.clearPersistentCache()
}
// Restarts the app (forcing any passed in page types).
sessionObject.restartSession(withPageType: model.pageType, request: model.requestURL, parameters: model.extraParameters?.toJSON(), clearAllVariables: true)
}
continuation.resume()
})
}
}
}
}

View File

@ -1,17 +0,0 @@
//
// ActionSettingHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 6/30/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
open class ActionSettingHandler: MVMCoreActionHandlerProtocol {
required public init() {}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws {
try await ActionOpenUrlHandler.openURL(with: await UIApplication.openSettingsURLString)
}
}

View File

@ -1,54 +0,0 @@
//
// ActionShareHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 7/14/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
open class ActionShareHandler: MVMCoreActionHandlerProtocol {
required public init() {}
open func execute(with model: ActionModelProtocol, delegateObject: DelegateObject? = nil, additionalData: [AnyHashable: Any]? = nil) async throws {
guard let model = model as? ActionShareModel else { return }
try await shareWith(activityItems: model.items.map { $0.value }, model: model)
}
@MainActor
open func shareWith(activityItems: [Any], model: ActionShareModel, delegateObject: DelegateObject? = nil) async throws {
try await withCheckedThrowingContinuation { continuation in
let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
controller.popoverPresentationController?.sourceView = NavigationHandler.shared().viewControllerToPresentOn?.view
var originPoint: CGPoint = .zero
if let view = NavigationHandler.shared().viewControllerToPresentOn?.view {
originPoint = CGPoint(x: view.frame.midX, y: view.frame.maxY)
}
controller.popoverPresentationController?.sourceRect.origin = originPoint
controller.completionWithItemsHandler = {(activityType: UIActivity.ActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in
if completed {
// Activity was completed, considered finished.
continuation.resume()
} else if let error = error {
// Activity sharing failed.
continuation.resume(throwing: error)
} else {
// Activity was probably cancelled
if #available(iOS 16.4, *) {
continuation.resume()
} else if let _ = activityType {
// Pre iOS 16.4, this block gets called multiple times. Each time a specific activityType was selected and was cancelled, and when the whole share activity itself is cancelled.
return
} else {
continuation.resume()
}
}
}
Task(priority: .high) {
await NavigationHandler.shared().present(viewController: controller)
}
} as Void
}
}

View File

@ -1,131 +0,0 @@
//
// ActionShareModel.swift
// MVMCore
//
// Created by Kevin Christiano on 2/18/20.
// Copyright © 2020 myverizon. All rights reserved.
//
public struct ActionShareItemModel: Codable, Equatable {
public enum SharedType: String, Codable {
case text
case url
}
public var type: SharedType
public var value: AnyHashable // Common Equatable type between String and URL.
private enum CodingKeys: String, CodingKey {
case type
case value
}
public init(type: SharedType, value: AnyHashable) {
self.type = type
self.value = value
}
public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
type = try typeContainer.decode(SharedType.self, forKey: .type)
switch type {
case .text:
value = try typeContainer.decode(String.self, forKey: .value)
case .url:
value = try typeContainer.decode(URL.self, forKey: .value)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(type, forKey: .type)
switch type {
case .text:
try container.encode(value as! String, forKey: .value)
case .url:
try container.encode(value as! URL, forKey: .value)
}
}
public static func == (lhs: ActionShareItemModel, rhs: ActionShareItemModel) -> Bool {
return lhs.type == rhs.type
&& lhs.value == rhs.value
}
}
public struct ActionShareModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "share"
public var actionType: String = ActionShareModel.identifier
public var items: [ActionShareItemModel]
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(items: [ActionShareItemModel], _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.items = items
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
//--------------------------------------------------
// MARK: - Codable
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case actionType
case items
case sharedType
case sharedText
}
private enum DeprecatedCodingKeys: String, CodingKey {
case sharedType
case sharedText
}
public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let items = try typeContainer.decodeIfPresent([ActionShareItemModel].self, forKey: .items) {
self.init(items: items)
} else {
// Legacy
try self.init(deprecatedFrom: decoder)
}
}
private init(deprecatedFrom decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: DeprecatedCodingKeys.self)
let type = try typeContainer.decode(ActionShareItemModel.SharedType.self, forKey: .sharedType)
var value: AnyHashable
switch type {
case .url:
value = try typeContainer.decode(URL.self, forKey: .sharedText)
default:
value = try typeContainer.decode(String.self, forKey: .sharedText)
}
items = [ActionShareItemModel(type: type, value: value)]
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(actionType, forKey: .actionType)
try container.encode(items, forKey: .items)
}
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return extraParameters == model.extraParameters
&& analyticsData == model.analyticsData
&& items == model.items
}
}

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

@ -24,6 +24,9 @@
// Handles the back actions. Can overwrite for special loading.
- (void)handleBackAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData;
// Prepares to call the previous submit request again. Can overwrite for special loading. Be sure to call submit() block to perform the actual load.
- (void)prepareRequestForPreviousSubmission:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData submit:(nonnull void (^)(MVMCoreRequestParameters * _Nonnull requestParameters, NSDictionary * _Nullable dataForPage))submit;
// Handles the linkaway action. Call the block to continue to linkaway.
- (void)shouldLinkAwayWithURL:(nullable NSURL *)URL appURL:(nullable NSURL *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData linkAwayBlock:(nonnull void (^)(NSURL * _Nullable appURL, NSURL * _Nullable URL, NSDictionary * _Nullable actionInformation, NSDictionary * _Nullable additionalData))linkAwayBlock;
@ -33,6 +36,9 @@
// Handles any unknown action types. Can overwrite for more specific handling.
- (void)handleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData;
// Handles any action errors. Can overwrite for more specific handling.
- (void)handleActionError:(nonnull MVMCoreErrorObject *)error additionalData:(nullable NSDictionary *)additionalData;
// Lets the delegate know that another internal module app is about to be launched
- (void)prepareForOpenOtherAppModule:(nullable NSString *)module;

View File

@ -0,0 +1,84 @@
//
// MVMCoreActionHandler+Extension.swift
// MVMCore
//
// Created by Scott Pfeil on 1/22/21.
// Copyright © 2021 myverizon. All rights reserved.
//
import Foundation
public protocol MVMCoreActionHandlerProtocol: ModelHandlerProtocol {
init()
func handleAction(_ model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?)
}
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
}
}
@objc func hasActionHandler(actionType: String?, actionInformation: [String: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> Bool {
guard //ensure there is a actinType
let actionType = actionType,
//ensure there is a serialized version of the Action
let actionInformation = actionInformation,
//esnure the actionModelType
let actionModelType = ModelRegistry.getType(for: actionType, with: ActionModelProtocol.self),
//ensure there is handlerType for the action of MVMCoreActionHandlerProtocol
let actionHandlerType = try? ModelRegistry.getHandlerType(for: actionModelType) as? MVMCoreActionHandlerProtocol.Type
else { return false }
do {
//ensure the decoded actionModel is of ActionModelProtocol
guard let actionModel = try actionModelType.decode(jsonDict: actionInformation) as? ActionModelProtocol else {
throw ModelRegistry.Error.decoderOther(message: "Could not decode to ActionModelProtocol")
}
//create the handler since we know it can initialize
let actionHandler = actionHandlerType.init()
//call the handleAction of the handler
actionHandler.handleAction(actionModel, additionalData: additionalData, delegateObject: delegateObject)
} catch {
//log the error
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "") {
MVMCoreActionHandler.shared()?.defaultHandleActionError(errorObject, additionalData: additionalData)
}
}
//found the handler, returning true no matter if there was a failure in the do...catch
return true
}
/// 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)
}
@objc static func getErrorLocation(with delegate: MVMCoreActionDelegateProtocol?, actionType: String) -> String {
return "\(String(describing: delegate))_\(actionType)"
}
}

View File

@ -0,0 +1,97 @@
//
// MVMCoreActionHandler.h
// myverizon
//
// Created by Scott Pfeil on 11/20/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
// Can be subclassed to handle app specific actions as well.
#import <Foundation/Foundation.h>
#import <MVMCore/MVMCoreActionDelegateProtocol.h>
#import <MVMCore/MVMCoreLoadDelegateProtocol.h>
#import <MVMCore/MVMCorePresentationDelegateProtocol.h>
@class DelegateObject;
extern NSString * _Nonnull const KeyActionType;
extern NSString * _Nonnull const KeyActionTypeLinkAway;
extern NSString * _Nonnull const KeyActionTypeOpen;
@interface MVMCoreActionHandler : NSObject
/// 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
- (void)handleActionWithDictionary:(nullable NSDictionary *)dictionary additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// 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.
- (void)updateRequestParametersBeforeHandleOpenPageAction:(nonnull MVMCoreRequestParameters *)requestParameters callBack:(void (^_Nonnull)(MVMCoreRequestParameters * _Nonnull requestParameters))callback;
/// 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
- (void)openPageAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// restarts the app
- (void)restartAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// Goes back
- (void)backAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// 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
- (void)previousSubmitAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// Redirects to another experience
- (void)redirectAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// Cancels (like in a popup)
- (void)cancelAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// Goes to settings app
- (void)settingsAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// 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
- (void)unknownAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// Handles action errors.
- (void)handleActionError:(nullable MVMCoreErrorObject *)error 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.
+ (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 actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject;
/// 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.
- (void)defaultHandleActionError:(nonnull MVMCoreErrorObject *)error additionalData:(nullable NSDictionary *)additionalData;
@end

View File

@ -0,0 +1,486 @@
//
// MVMCoreActionHandler.m
// myverizon
//
// Created by Scott Pfeil on 11/20/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
#import <MVMCore/MVMCoreActionHandler.h>
#import "MVMCoreLoggingHandler.h"
#import "MVMCoreCache.h"
#import "MVMCoreSessionTimeHandler.h"
#import "MVMCoreLoadHandler.h"
#import "MVMCoreNavigationHandler.h"
#import "MVMCoreDispatchUtility.h"
#import "NSDictionary+MFConvenience.h"
#import "MVMCoreGetterUtility.h"
#import "MVMCoreRequestParameters.h"
#import "MVMCoreErrorObject.h"
#import "MVMCoreJSONConstants.h"
#import "MVMCoreHardcodedStringsConstants.h"
#import "MVMCoreErrorConstants.h"
#import "MVMCoreActionUtility.h"
#import "MVMCoreSessionObject.h"
#import "MVMCoreObject.h"
#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";
@interface MVMCoreActionHandler() <CNContactViewControllerDelegate, CNContactPickerDelegate>
@end
@implementation MVMCoreActionHandler
+ (nullable instancetype)sharedActionHandler {
return [MVMCoreActionUtility initializerClassCheck:[MVMCoreObject sharedInstance].actionHandler classToVerify:self];
}
- (void)handleActionWithDictionary:(nullable NSDictionary *)dictionary additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
NSString *action = [dictionary stringForKey:KeyActionType];
[self handleAction:action actionInformation:dictionary additionalData:additionalData delegateObject:delegateObject];
}
- (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), ^{
[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: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 {
//does not do anything by default, can be override
callback(requestParameters);
}
- (void)logAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
if ([delegateObject.actionDelegate respondsToSelector:@selector(logActionWithActionInformation:additionalData:)]) {
[delegateObject.actionDelegate logActionWithActionInformation:actionInformation additionalData:additionalData];
} else {
[[self class] defaultLogAction:actionInformation additionalData:additionalData delegateObject:delegateObject];
}
}
- (void)openPageAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
// Loads the given page type.
NSString *pageType = [actionInformation stringForKey:KeyPageType];
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 {
NSString *shareType = [actionInformation string:KeyShareType];
NSString *shareText = [actionInformation string:KeyShareText];
if (!shareText || !shareType) {
return;
}
NSArray *shareData = nil;
if ([shareType isEqualToString:@"text"]) {
shareData = @[shareText];
} else if ([shareType isEqualToString:@"url"]) {
NSURL *url = [NSURL URLWithString:shareText];
if (url) {
shareData = @[shareText, url];
} else {
shareData = @[shareText];
}
} else if ([shareType isEqualToString:@"image"]) {
// TODO: Implement image parsing. 🏂
} else if ([shareType isEqualToString:@"file"]) {
// TODO: Implement file parsing. 🌋
}
if (shareData.count > 0) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:shareData applicationActivities:nil];
void(^activityCompletion)(UIActivityType, BOOL, NSArray*, NSError*) = ^(UIActivityType activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
if (activityType == UIActivityTypeCopyToPasteboard) {
[[MVMCoreSessionObject sharedGlobal] copyStringToClipboard:shareText];
}
};
activityViewController.completionWithItemsHandler = activityCompletion;
activityViewController.popoverPresentationController.sourceView = [MVMCoreNavigationHandler sharedNavigationHandler].viewControllerToPresentOn.view;
[[MVMCoreNavigationHandler sharedNavigationHandler] presentViewController:activityViewController animated:YES];
}];
}
}
- (void)restartAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
// 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 delegateObject:delegateObject];
} 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 delegateObject:(nullable DelegateObject *)delegateObject {
// Go back.
if ([delegateObject.actionDelegate respondsToSelector:@selector(handleBackAction:additionalData:)]) {
[delegateObject.actionDelegate handleBackAction:actionInformation additionalData:additionalData];
} else {
[[MVMCoreNavigationHandler sharedNavigationHandler] removeCurrentViewController];
}
}
- (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];
[MVMCoreActionUtility linkAway:[@"tel://" stringByAppendingString:callNumber] appURLString:nil];
}
- (void)previousSubmitAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
// Perform the previous submission.
__weak typeof(self) weakSelf = self;
if ([delegateObject.actionDelegate respondsToSelector:@selector(prepareRequestForPreviousSubmission:additionalData:submit:)]) {
[delegateObject.actionDelegate 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 ([delegateObject.actionDelegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) {
[delegateObject.actionDelegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:dataForPage];
} else {
[MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters
actionInformation:actionInformation
additionalData:additionalData
delegateObject:delegateObject];
}
}];
}];
}
}
- (void)redirectAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
// Have delegate redirect.
[[MVMCoreSessionObject sharedGlobal] redirectWithInfo:actionInformation];
}
- (void)cancelAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
if ([delegateObject.actionDelegate respondsToSelector:@selector(handleCancel:additionalData:)]) {
[delegateObject.actionDelegate handleCancel:actionInformation additionalData:additionalData];
}
}
- (void)settingsAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
[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 [self hasActionHandlerWithActionType:actionType actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject];
}
- (void)unknownAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
if ([delegateObject.actionDelegate respondsToSelector:@selector(handleUnknownActionType:actionInformation:additionalData:)]) {
[delegateObject.actionDelegate handleUnknownActionType:actionType actionInformation:actionInformation additionalData:additionalData];
} else {
[MVMCoreActionHandler defaultHandleUnknownActionType:actionType actionInformation:actionInformation additionalData:additionalData delegateObject:delegateObject];
}
}
- (void)handleActionError:(nullable MVMCoreErrorObject *)error actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegateObject:(nullable DelegateObject *)delegateObject {
if (error) {
if ([delegateObject.actionDelegate respondsToSelector:@selector(handleActionError:additionalData:)]) {
[delegateObject.actionDelegate handleActionError:error additionalData:additionalData];
} else {
[self defaultHandleActionError:error additionalData:additionalData];
}
}
}
#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 - 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 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 {
MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeUnknownActionType domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@Requests_%@",NSStringFromClass([delegateObject.actionDelegate class]),actionType]];
[[self sharedActionHandler] defaultHandleActionError:error additionalData:additionalData];
}
- (void)defaultHandleActionError:(nonnull MVMCoreErrorObject *)error additionalData:(nullable NSDictionary *)additionalData {
// Logs the error.
if (error.logError) {
[MVMCoreLoggingHandler addErrorToLog:error];
}
}
@end

View File

@ -1,228 +0,0 @@
//
// MVMCoreActionHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 1/22/21.
// Copyright © 2021 myverizon. All rights reserved.
//
import Foundation
/// Handlers that can be registered and used by the MVMCoreActionHandler to handle actions.
public protocol MVMCoreActionHandlerProtocol: ModelHandlerProtocol {
init()
func execute(with model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws
}
/// Protocol used to bridge legacy, non model based code. Allows us to keep the original json intact and not lose key values during decode/encode. Should not be used for new actions.
public protocol MVMCoreJSONActionHandlerProtocol: MVMCoreActionHandlerProtocol {
/// Perform the function using the original json and model.
func performAction(with JSON: [AnyHashable : Any], model: ActionModelProtocol, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) async throws
}
@objc open class MVMCoreActionHandler: NSObject {
enum ActionError: MVMError, CustomStringConvertible {
case unknownAction(type: String)
public var description: String {
switch self {
case MVMCoreActionHandler.ActionError.unknownAction(let type):
return "Couldn't perform action: \(type)"
}
}
public var errorCode: Int {
ErrorCode.unknownActionType.rawValue
}
}
/// Used to temporarily store the json in additionalData.
private let jsonKey = "MVMCore.JSON"
/// Returns the action handler stored in the MVMCoreObject
@objc(sharedActionHandler)
public static func shared() -> Self? {
return MVMCoreActionUtility.initializerClassCheck(MVMCoreObject.sharedInstance()?.actionHandler, classToVerify: self) as? Self
}
// MARK: - Conversions
/// Converts the action to json for legacy functions.
static public func convertActionToJSON(_ model: ActionModelProtocol) throws -> [AnyHashable: Any] {
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
}
/// Creates a model from the action json.
static public func createModel(with json: [AnyHashable: Any], delegateObject: DelegateObject? = nil) throws -> ActionModelProtocol {
guard let castedSelf = json as? [String: Any] else {
throw ModelRegistry.Error.decoderOther(message: "Dictionary is not of type [String: Any]")
}
guard let actionType = ModelRegistry.getType(for: castedSelf.stringForkey(KeyActionType), with: ActionModelProtocol.self) else {
throw ModelRegistry.Error.decoderErrorModelNotMapped()
}
guard let actionModel = try actionType.decode(jsonDict: castedSelf, delegateObject: delegateObject) as? ActionModelProtocol else {
throw ModelRegistry.Error.decoderOther(message: "Could not decode to ActionModelProtocol")
}
return actionModel
}
// MARK: - Error Handling
/// Returns a common description for the error location.
@objc public static func getErrorLocation(with delegate: MVMCoreActionDelegateProtocol?, actionType: String) -> String {
return "\(String(describing: delegate))_\(actionType)"
}
// MARK: - Action Handling
/// Handle an action with the given model.
open func handleAction(with model: ActionModelProtocol, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) async throws {
try Task.checkCancellation()
var (additionalData, uuid) = MVMCoreActionHandler.setUUID(additionalData: additionalData)
MVMCoreActionHandler.log(string: "Begin Action: \(model)", additionalData: additionalData)
defer {
MVMCoreActionHandler.log(string: "End Action: \(model)", additionalData: additionalData)
}
let json = try additionalData.removeValue(forKey: jsonKey) as? [AnyHashable : Any] ?? MVMCoreActionHandler.convertActionToJSON(model)
// Log the action
logAction(with: json, additionalData: additionalData, delegateObject: delegateObject)
do {
let handlerType = try ModelRegistry.getHandler(model) as! MVMCoreActionHandlerProtocol.Type
let handler = handlerType.init()
MVMCoreLoggingHandler.shared()?.logCoreEvent(.actionInvoked(name: model.actionType, pageType: pageType(from: delegateObject), uuid: uuid, model: model))
if let handler = handler as? MVMCoreJSONActionHandlerProtocol {
// Needed until we can remove legacy delegate functions.
try await handler.performAction(with: json, model: model, delegateObject: delegateObject, additionalData: additionalData)
} else {
try await handler.execute(with: model, delegateObject: delegateObject, additionalData: additionalData)
}
MVMCoreLoggingHandler.shared()?.logCoreEvent(.actionComplete(name: model.actionType, pageType: pageType(from: delegateObject), uuid: uuid, model: model))
} catch ModelRegistry.Error.handlerNotMapped {
try Task.checkCancellation()
// Allows custom handling if there no handler for the action.
guard try await handleUnregisteredAction(with: model, json: json, additionalData: additionalData, delegateObject: delegateObject) else {
MVMCoreActionHandler.log(string: "Failed Action Unknown", additionalData: additionalData)
MVMCoreLoggingHandler.shared()?.logCoreEvent(.actionNotFound(name: model.actionType , pageType: pageType(from: delegateObject)))
throw ActionError.unknownAction(type: model.actionType)
}
} catch {
MVMCoreLoggingHandler.shared()?.logCoreEvent(.actionFailed(name: model.actionType, pageType: pageType(from: delegateObject), uuid: uuid, model: model, error: error))
MVMCoreActionHandler.log(string: "Failed Action \(error)", additionalData: additionalData)
throw error
}
}
// MARK: - Subclassables
/// Subclass to log the action was fired.
open func logAction(with JSON: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
// Calls legacy log action function.
delegateObject?.actionDelegate?.logAction?(withActionInformation: JSON, additionalData: additionalData)
}
/// Logs the error.
@objc open func defaultHandleActionError(_ error: MVMCoreErrorObject, additionalData: [AnyHashable: Any]? = nil, delegateObject: DelegateObject? = nil) {
guard error.logError else { return }
MVMCoreLoggingHandler.shared()?.addError(toLog: error)
error.logError = false // Further attempts to log the same error will be skipped. (Legacy action flow.)
}
// MARK: - Legacy Holdovers
static public func setUUID(additionalData: [AnyHashable: Any]?, force: Bool = false) -> ([AnyHashable: Any], String) {
if !force, let uuid = getUUID(additionalData: additionalData) { return (additionalData!, uuid) }
let newUUID = UUID().uuidString
return (additionalData.dictionaryAdding(key: "Action-UUID", value: newUUID), newUUID)
}
@objc static public func getUUID(additionalData: [AnyHashable: Any]?) -> String? {
return additionalData?.optionalStringForKey("Action-UUID")
}
static public func log(string: String, additionalData: [AnyHashable: Any]?) {
MVMCoreLoggingHandler.shared()?.handleDebugMessage("ActionHandler: UUID: \(getUUID(additionalData: additionalData) ?? "untracked"), \(string)", category: String(describing: Self.self))
}
fileprivate func logActionError(_ error: Error, _ actionType: String?, _ additionalData: [AnyHashable: Any]?, _ delegateObject: DelegateObject?) {
MVMCoreActionHandler.log(string: "Failed Action: Error \(error)", additionalData: additionalData)
if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: MVMCoreActionHandler.getErrorLocation(with: delegateObject?.actionDelegate, actionType: actionType ?? "noAction")) {
if let fromBridge = additionalData?["fromBridge"] as? Bool, fromBridge, let browserUrl = additionalData?["browserUrl"] as? String {
errorObject.requestUrl = browserUrl
}
if let humanReadableMessage = (error as? HumanReadableDecodingErrorProtocol)?.readableDescription {
errorObject.messageToLog = "Failed to decode the \(actionType ?? "") action model. " + humanReadableMessage
}
defaultHandleActionError(errorObject, additionalData: additionalData)
}
}
fileprivate func pageType(from delegateObject: DelegateObject?) -> String {
return (delegateObject?.loadDelegate as? MVMCoreViewControllerProtocol)?.pageType ?? "unknown"
}
/// Legacy handle action with json.
@objc(handleActionWithDictionary:additionalData:delegateObject:)
open func handleAction(with json: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
_ = Task(priority: .userInitiated) {
try Task.checkCancellation()
do {
guard let json = json else {
throw ModelRegistry.Error.keyNotFound
}
let model = try MVMCoreActionHandler.createModel(with: json, delegateObject: delegateObject)
try await handleAction(with: model, json: json, additionalData: additionalData, delegateObject: delegateObject)
} catch {
let actionType = json?.optionalStringForKey(KeyActionType)
switch error {
case is ModelRegistry.Error, is DecodingError:
MVMCoreLoggingHandler.shared()?.logCoreEvent(.actionFailedToDecode(pageType: pageType(from: delegateObject), error: error))
logActionError(error, actionType, additionalData, delegateObject)
case ModelRegistry.Error.decoderErrorModelNotMapped:
// If the model is not mapped, give the legacy classes a chance to handle it.
if try await handleUnregisteredAction(with: nil, json: json!, additionalData: additionalData, delegateObject: delegateObject) == false {
MVMCoreLoggingHandler.shared()?.logCoreEvent(.actionNotFound(name: actionType ?? "noAction", pageType: pageType(from: delegateObject)))
logActionError(error, actionType, additionalData, delegateObject)
}
default:
logActionError(error, actionType, additionalData, delegateObject)
}
}
}
}
/// Bridges the legacy json using functions and the new model using functions.
open func handleAction(with model: ActionModelProtocol, json: [AnyHashable: Any], additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) async throws {
try Task.checkCancellation()
let additionalDataWithJSON = additionalData.dictionaryAdding(key: jsonKey, value: json)
let (additionalData, _) = MVMCoreActionHandler.setUUID(additionalData: additionalDataWithJSON, force: true)
MVMCoreActionHandler.log(string: "JSON \(json)", additionalData: additionalData)
if let closure = (delegateObject?.actionDelegate as? ActionDelegateProtocol)?.performAction {
// Allow newer delegates to handle calls from legacy functions
try await closure(model, additionalData, delegateObject)
} else {
try await handleAction(with: model, additionalData: additionalData, delegateObject: delegateObject)
}
}
/// Subclass to handle and any actions where a handler was not registered. Checks with the delegate handlesUnknownAction function
open func handleUnregisteredAction(with model: ActionModelProtocol?, json: [AnyHashable: Any], additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) async throws -> Bool {
// Check if the delegate handles the action.
let actionType = json.optionalStringForKey(KeyActionType)
if let closure = delegateObject?.actionDelegate?.handleUnknownActionType {
MVMCoreActionHandler.log(string: "Unknown handled (Handler not registered): \(String(describing: actionType)) \(String(describing: delegateObject?.actionDelegate))", additionalData: additionalData)
// Check if the legacy delegate handles the action.
closure(actionType, json, additionalData)
return true
} else {
return false
}
}
}

View File

@ -9,8 +9,7 @@
import Foundation
/// A model for UIApplication.OpenExternalURLOptionsKey
open class OpenUrlOptionsModel: Codable, Equatable {
open class OpenUrlOptionsModel: Codable {
public var options: [UIApplication.OpenExternalURLOptionsKey: Any]
//--------------------------------------------------
@ -43,16 +42,4 @@ open class OpenUrlOptionsModel: Codable, Equatable {
try container.encode(universalLinksValue, forKey: .universalLinksOnly)
}
}
public static func == (lhs: OpenUrlOptionsModel, rhs: OpenUrlOptionsModel) -> Bool {
return lhs.options.allSatisfy { pair in
switch(pair.key) {
case .universalLinksOnly:
return rhs.options[pair.key] as? Bool == pair.value as? Bool
default:
return true
}
}
}
}

View File

@ -0,0 +1,18 @@
//
// MVMCoreAlertController.h
// alerts
//
// Created by Scott Pfeil on 10/22/14.
// Copyright (c) 2014 Verizon Wireless. All rights reserved.
//
// Used by our alert handler. Not for subclassing. Simply keeps track of if it's visible. Tries to parallel the UIAlertView to make it easier for the MVMCoreAlertHandler.
#import <UIKit/UIKit.h>
@class MVMCoreAlertObject;
@interface MVMCoreAlertController : UIAlertController
@property (nonatomic, readonly, getter=isVisible) BOOL visible;
@property (nullable, nonatomic, strong) MVMCoreAlertObject *alertObject;
@end

View File

@ -0,0 +1,40 @@
//
// MVMAlertController.m
// alerts
//
// Created by Scott Pfeil on 10/22/14.
// Copyright (c) 2014 Verizon Wireless. All rights reserved.
//
#import "MVMCoreAlertController.h"
#import "MVMCoreLoggingHandler.h"
@interface MVMCoreAlertController ()
@property (nonatomic, readwrite, getter=isVisible) BOOL visible;
@end
@implementation MVMCoreAlertController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self willChangeValueForKey:@"isVisible"];
self.visible = YES;
[self didChangeValueForKey:@"isVisible"];
[MVMCoreLoggingHandler logAlertForAlertController:self];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self willChangeValueForKey:@"isVisible"];
self.visible = NO;
[self didChangeValueForKey:@"isVisible"];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@|title=%@|message=%@", [super description],self.title,self.message];
}
@end

View File

@ -1,16 +0,0 @@
//
// Collection+Safe.swift
// MVMCore
//
// Created by Kyle on 2/13/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public extension Collection {
/// Returns the element at the specified index if it is within bounds, otherwise nil.
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}

View File

@ -1,20 +0,0 @@
//
// Date+Extension.swift
// MVMCore
//
// Created by Kyle on 8/29/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
public extension Date {
static func unixMillisecondsNow() -> Int64 {
return Self().unixMilliseconds()
}
func unixMilliseconds() -> Int64 {
return Int64(self.timeIntervalSince1970 * 1000)
}
}

View File

@ -192,61 +192,6 @@ public extension Dictionary {
return previousObject
}
/// Transforms dictionary keys without modifying values.
/// Deduplicates transformed keys.
///
/// Example:
/// ```
/// ["one": 1, "two": 2, "three": 3, "": 4].mapKeys({ $0.first }, uniquingKeysWith: { max($0, $1) })
/// // [Optional("o"): 1, Optional("t"): 3, nil: 4]
/// ```
///
/// - Parameters:
/// - transform: A closure that accepts each key of the dictionary as
/// its parameter and returns a transformed key of the same or of a different type.
/// - combine:A closure that is called with the values for any duplicate
/// keys that are encountered. The closure returns the desired value for
/// the final dictionary.
/// - Returns: A dictionary containing the transformed keys and values of this dictionary.
func mapKeys<T>(_ transform: (Key) throws -> T, uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows -> [T: Value] {
try .init(map { (try transform($0.key), $0.value) }, uniquingKeysWith: combine)
}
/// Transforms dictionary keys without modifying values.
/// Drops (key, value) pairs where the transform results in a nil key.
/// Deduplicates transformed keys.
///
/// Example:
/// ```
/// ["one": 1, "two": 2, "three": 3, "": 4].compactMapKeys({ $0.first }, uniquingKeysWith: { max($0, $1) })
/// // ["o": 1, "t": 3]
/// ```
///
/// - Parameters:
/// - transform: A closure that accepts each key of the dictionary as
/// its parameter and returns an optional transformed key of the same or of a different type.
/// - combine: A closure that is called with the values for any duplicate
/// keys that are encountered. The closure returns the desired value for
/// the final dictionary.
/// - Returns: A dictionary containing the non-nil transformed keys and values of this dictionary.
func compactMapKeys<T>(_ transform: (Key) throws -> T?, uniquingKeysWith combine: (Value, Value) throws -> Value) rethrows -> [T: Value] {
try .init(compactMap { (try transform($0.key), $0.value) as? (T, Value) }, uniquingKeysWith: combine)
}
/// Unconditionally merge another dictionary into this one with preference to the incoming keys.
func mergingLeft(_ other: Self) -> Self {
return merging(other) { first, second in
second
}
}
/// Unconditionally merge this dictionary into another with preference to the existing keys.
func mergingRight(_ other: Self) -> Self {
return merging(other) { first, second in
first
}
}
}
public extension Optional where Wrapped == Dictionary<AnyHashable, Any> {

View File

@ -34,6 +34,8 @@ extern NSString * const URLComponentKeepAlive;
#pragma mark - Notification Names
extern NSString * const NotificationResponseLoaded;
extern NSString * const MVMCoreNotificationGoingToServer;
extern NSString * const MVMCoreNotificationViewControllerChanged;
#pragma mark - Image Cache
extern NSTimeInterval const ImageTimeOut;

View File

@ -28,6 +28,8 @@ NSString * const URLComponentKeepAlive = @"isAlive.jsp";
#pragma mark - Notification Names
NSString * const NotificationResponseLoaded = @"responseLoaded";
NSString * const MVMCoreNotificationGoingToServer = @"MVMCoreGoServer";
NSString * const MVMCoreNotificationViewControllerChanged = @"MVMCoreNotificationViewControllerChanged";
#pragma mark - Image Cache
NSTimeInterval const ImageTimeOut = 60;

View File

@ -40,6 +40,5 @@ typedef NS_ENUM(NSInteger, ErrorCode) {
ErrorCodeSSL,//23
ErrorCodeNoViewControllerToPresentOn,//24
ErrorCodeNoErrorPageSent, //25
ErrorCodeFontNotFound, //26
ErrorCodePoorPerformance //27
ErrorCodeFontNotFound //26
};

View File

@ -9,10 +9,6 @@
#import "MVMCoreErrorConstants.h"
// Error Domains
/// Communication breakdowns.
NSString * const ErrorDomainSystem = @"ErrorDomainSystem";
/// Any sort of native error not due to server directives.
NSString * const ErrorDomainNative = @"ErrorDomainNative";
/// Specific errors defined by the server.
NSString * const ErrorDomainServer = @"ErrorDomainServer";

View File

@ -33,10 +33,6 @@ extern NSString * const KeyView;
extern NSString * const KeyLinks;
extern NSString * const KeyTitle;
extern NSString * const KeyMessage;
extern NSString * const KeyActionType;
extern NSString * const KeyActionTypeLinkAway;
extern NSString * const KeyActionTypeOpen;
extern NSString * const KeyActionTypeRestart;
extern NSString * const KeyActionTypeBack;
extern NSString * const KeyActionTypeShare;

View File

@ -35,10 +35,6 @@ NSString * const KeyView = @"view";
NSString * const KeyLinks = @"Links";
NSString * const KeyTitle = @"title";
NSString * const KeyMessage = @"message";
NSString * const KeyActionType = @"actionType";
NSString * const KeyActionTypeLinkAway = @"openURL";
NSString * const KeyActionTypeOpen = @"openPage";
NSString * const KeyActionTypeRestart = @"restart";
NSString * const KeyActionTypeBack = @"back";
NSString * const KeyActionTypeShare = @"share";

View File

@ -17,9 +17,6 @@
// Starts Loading. Every start loading call must be terminated with an end loading call.
- (void)startLoading;
// Starts Loading, by showing the text in the center
- (void)startLoadingWith:(nullable NSAttributedString *) text;
// Returns if it is showing.
- (BOOL)isShowing;

View File

@ -6,12 +6,11 @@
// Copyright (c) 2014 Verizon Wireless. All rights reserved.
//
@import UIKit;
#import "MVMCoreLoadingOverlayHandler.h"
#import "MVMCoreSessionObject.h"
#import <MVMCore/MVMCoreDispatchUtility.h>
#import "MVMCoreLoadingViewControllerProtocol.h"
#import <MVMCore/MVMCore-Swift.h>
#import "MVMCoreObject.h"
@interface MVMCoreLoadingOverlayHandler ()
// The loading ui elements
@ -52,10 +51,6 @@
#pragma mark - Overlay Functions
- (void)startLoading {
[self startLoadingWith:nil];
}
- (void)startLoadingWith:(nullable NSAttributedString *)text {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
@ -77,7 +72,7 @@
}
// Restarts the loading animation.
[self.loadingViewController startLoadingWith:text];
[self.loadingViewController startLoading];
if (self.animatingOut) {
@ -144,7 +139,7 @@
if (animate) {
self.animatingOut = YES;
self.animatingIn = NO;
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.loadingViewController.view.alpha = 0;
} completion:^(BOOL finished) {

View File

@ -14,8 +14,4 @@
// Called when the view controller should stop animating loading.
- (void)stopLoading;
@optional
// Called when the view controller should animate loading with custom text
- (void)startLoadingWith:(nullable NSAttributedString *) text;
@end

View File

@ -11,6 +11,7 @@
#import <MVMCore/MVMCoreViewControllerProtocol.h>
@class MVMCoreRequestParameters;
@class MVMCoreErrorObject;
@class MVMCoreAlertObject;
@class MVMCoreLoadObject;
@protocol MVMCoreLoadDelegateProtocol

View File

@ -33,33 +33,18 @@
// Returns the error location for the given requesting object and page type and modules. Important for proper logging.
- (nonnull NSString *)errorLocationForRequest:(nonnull id)requestingObject pageType:(nonnull NSString *)pageType modules:(nonnull NSString *)modules;
// Returns an error given a load object and error details. Attaches session data related to the load. Important for proper logging.
- (nonnull MVMCoreErrorObject *)errorForLoadObject:(nonnull MVMCoreLoadObject *)loadObject withTitle:(nullable NSString *)title message:(nullable NSString *)message code:(NSInteger)code domain:(nonnull NSString *)domain;
- (nonnull MVMCoreErrorObject *)errorForLoadObject:(nonnull MVMCoreLoadObject *)loadObject withTitle:(nullable NSString *)title message:(nullable NSString *)message messageToLog:(nullable NSString *)messageToLog code:(NSInteger)code domain:(nonnull NSString *)domain;
// Returns an error given a load object and NSError. Attaches session data related to the load. Important for proper logging.
- (nonnull MVMCoreErrorObject *)errorForLoadObject:(nonnull MVMCoreLoadObject *)loadObject causedBy:(nonnull NSError *)error;
// Decorates an error object given a load object.
- (nonnull MVMCoreErrorObject *)attachLoadInformation:(nonnull MVMCoreLoadObject *)loadObject toError:(nonnull MVMCoreErrorObject *)error;
#pragma mark - Request Functions.
- (void)setHeadersForRequest:(nonnull NSMutableURLRequest *)request requestParameters:(nonnull MVMCoreRequestParameters *)requestParameters;
// Converts the core request parameters into a JSON object dictionary.
- (void)getJsonDictionary:(nonnull MVMCoreRequestParameters *)requestParameters completion:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completion;
- (void)getJsonData:(nonnull MVMCoreRequestParameters *)requestParameters forUrl:(nonnull NSURL *)url completion:(nonnull void (^)(NSData * _Nullable data, MVMCoreErrorObject *_Nullable error))completion;
- (void)getInitialParametersExcludingSections:(NSSet<NSString *> *_Nullable)excludeSections actionId:(nonnull NSString *)actionId completion:(nonnull void (^)(NSDictionary *_Nullable parameters))completion NS_SWIFT_NAME(initialParameters(excludingSections:actionId:completion:));
- (nonnull NSDictionary *)convertToJsonDictionary:(nonnull MVMCoreRequestParameters *)requestParameters;
// Creates a request object with the given parameters.
- (void)transformToRequestWithParameters:(nonnull MVMCoreRequestParameters *)requestParameters completion:(nonnull void (^)(NSURLRequest * _Nullable request, MVMCoreErrorObject *_Nullable error))completion;
- (nullable NSURLRequest *)requestWithParameters:(nonnull MVMCoreRequestParameters *)requestParameters error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error;
// Sends a given request to the server. When it is finished, it calls request finished, passing along the json object or nil if there is an error.
- (void)sendRequest:(nonnull MVMCoreRequestParameters *)requestParameters locationForError:(nonnull NSString *)locationForError requestFinished:(nullable void (^)(id _Nullable jsonObject, MVMCoreErrorObject * _Nullable error))requestFinished;
- (nullable NSURLSessionTask *)sendRequest:(nonnull NSURLRequest *)request requestParameters:(nonnull MVMCoreRequestParameters *)requestParameters locationForError:(nonnull NSString *)locationForError requestFinished:(nullable void (^)(id _Nullable jsonObject, MVMCoreErrorObject *_Nullable error))requestFinished;
- (nullable NSURLSessionTask *)sendRequest:(nonnull MVMCoreRequestParameters *)requestParameters locationForError:(nonnull NSString *)locationForError requestFinished:(nullable void (^)(id _Nullable jsonObject, MVMCoreErrorObject * _Nullable error))requestFinished;
#pragma mark - Loading Functions

View File

@ -8,18 +8,20 @@
#import "MVMCoreLoadHandler.h"
#import "MVMCoreGetterUtility.h"
#import "MVMCoreLoggingHandler.h"
#import "MVMCoreLoadRequestOperation.h"
#import "MVMCoreCache.h"
#import "MFHardCodedServerResponse.h"
#import "MVMCoreNavigationHandler.h"
#import "MVMCoreLoadObject.h"
#import "MVMCoreSessionObject.h"
#import "MVMCoreJSONConstants.h"
#import "MVMCoreErrorObject.h"
#import "MVMCoreErrorConstants.h"
#import "MVMCoreHardcodedStringsConstants.h"
#import "MVMCoreObject.h"
#import "MVMCoreConstants.h"
#import "MVMCoreActionUtility.h"
#import "MVMCoreLoggingHandlerHelper.h"
#import <MVMCore/MVMCore-Swift.h>
@interface MVMCoreLoadHandler ()
@ -93,34 +95,9 @@
}
}
- (nonnull MVMCoreErrorObject *)errorForLoadObject:(MVMCoreLoadObject *)loadObject withTitle:(NSString *)title message:(NSString *)message code:(NSInteger)code domain:(NSString *)domain {
return [self attachLoadInformation:loadObject toError:[[MVMCoreErrorObject alloc] initWithTitle:title messageToLog:message code:code domain:domain location:[self errorLocationForRequest:loadObject]]];
}
- (nonnull MVMCoreErrorObject *)errorForLoadObject:(MVMCoreLoadObject *)loadObject withTitle:(NSString *)title message:(NSString *)message messageToLog:(NSString *)messageToLog code:(NSInteger)code domain:(NSString *)domain {
return [self attachLoadInformation:loadObject toError:[[MVMCoreErrorObject alloc] initWithTitle:title message:message messageToLog:messageToLog code:code domain:domain location:[self errorLocationForRequest:loadObject]]];
}
- (nonnull MVMCoreErrorObject *)errorForLoadObject:(MVMCoreLoadObject *)loadObject causedBy:(NSError *)error {
return [self attachLoadInformation:loadObject toError:[MVMCoreErrorObject createErrorObjectForNSError:error location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]];
}
- (nonnull MVMCoreErrorObject *)attachLoadInformation:(MVMCoreLoadObject *)loadObject toError:(MVMCoreErrorObject *)error {
// Native and system errors have an error screen.
if (![error.domain isEqualToString:ErrorDomainServer] && [[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(getNativeScreenForRequestError:requestObject:)]) {
error.errorScreenError = YES;
error.nativeDrivenErrorScreen = YES;
error.silentError = NO;
error.messageToDisplay = error.messageToDisplay ?: [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess];
error.requestId = loadObject.identifier; //To track for errors due to invalid JSON or any errors thrown at viewcontroller level.
}
return error;
}
- (nonnull NSString *)errorLocationForRequest:(nonnull MVMCoreLoadObject *)loadObject {
return [self errorLocationForRequest:loadObject.delegateObject.loadDelegate
pageType:loadObject.requestParameters.pageType
modules:[NSString stringWithFormat:@"%@", loadObject.requestParameters.modules]];
return [self errorLocationForRequest:loadObject.delegateObject.loadDelegate pageType:loadObject.requestParameters.pageType modules:[NSString stringWithFormat:@"%@",loadObject.requestParameters.modules]];
}
- (nonnull NSString *)errorLocationForRequest:(nonnull id)requestingObject pageType:(nonnull NSString *)pageType modules:(nonnull NSString *)modules {
@ -131,9 +108,33 @@
#pragma mark - Request Functions.
- (void)transformToRequestWithParameters:(nonnull MVMCoreRequestParameters *)requestParameters completion:(nonnull void (^)(NSURLRequest * _Nullable request, MVMCoreErrorObject *_Nullable error))completion {
- (nullable NSURLRequest *)requestWithParameters:(nonnull MVMCoreRequestParameters *)requestParameters error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error {
NSURL *url = [requestParameters resolveURL:[MVMCoreSessionObject sharedGlobal]];
NSURL *url = requestParameters.URL;
if (!url) {
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];
}*/
}
// Adds modules needed to the request parameters.
if (requestParameters.modules.count > 0) {
@ -151,15 +152,26 @@
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:timeOutInterval];
[self setHeadersForRequest:request requestParameters:requestParameters];
[self getJsonData:requestParameters forUrl:url completion:^(NSData * _Nullable jsonData, MVMCoreErrorObject * _Nullable error) {
/// Ensures that something in the body is always set.
if (!jsonData) {
completion(nil, error ?: [[MVMCoreErrorObject alloc] initWithTitle:nil message:nil code:ErrorCodeParsingJSON domain:ErrorDomainNative location:[NSString stringWithFormat:@"requestWithParameters_%@",requestParameters.pageType]]);
return;
}
[request setHTTPBody:jsonData];
completion(request, nil);
}];
MVMCoreErrorObject *jsonError;
NSData *jsonData = [self convertToJsonData:requestParameters forUrl:url error:&jsonError];
if (!jsonData) {
*error = jsonError;
return nil;
}
NSError *bodyError;
NSData *body = jsonData;
if (requestParameters.imageData) {
body = [self createMultipartFormBodyForRequestJsonData:jsonData imageData:requestParameters.imageData error:&bodyError];
}
if (body) {
[request setHTTPBody:body];
} else {
*error = [[MVMCoreErrorObject alloc] initWithTitle:nil message:nil code:ErrorCodeParsingJSON domain:ErrorDomainNative location:[NSString stringWithFormat:@"requestWithParameters_%@",requestParameters.pageType]];
return nil;
}
return request;
}
- (NSString *)boundaryForMultipartRequest {
@ -190,67 +202,64 @@
}
}
- (void)getInitialParametersExcludingSections:(NSSet<NSString *> *)excludeSections actionId:(NSString *)actionId completion:(void (^)(NSDictionary * _Nullable))completion {}
- (void)getJsonDictionary:(nonnull MVMCoreRequestParameters *)requestParameters completion:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completion {
- (nonnull NSDictionary *)convertToJsonDictionary:(nonnull MVMCoreRequestParameters *)requestParameters {
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
// Sets up the Initial parameters.
if (requestParameters.addInitialRequestParameters) {
NSDictionary *initialParameters = [[MVMCoreSessionObject sharedGlobal] getInitialParametersExcludingSections:requestParameters.excludedInitialParameters];
if (initialParameters) {
[parameters setObject:initialParameters forKey:@"InitialParams"];
}
}
// Adds request specific parameters
if (requestParameters.parameters.count) {
[parameters setObject:requestParameters.parameters forKey:@"RequestParams"];
}
if (!requestParameters.addInitialRequestParameters) {
completion(parameters);
return;
return parameters;
}
- (nullable NSData *)convertToJsonData:(nonnull MVMCoreRequestParameters *)requestParameters forUrl:(nonnull NSURL *)url error:(MVMCoreErrorObject *_Nullable *_Nullable)error {
NSDictionary *parameters = [self convertToJsonDictionary:requestParameters];
// Ensure the parameters are valid json.
if (![NSJSONSerialization isValidJSONObject:parameters]) {
if (error) {
*error = [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeParsingJSON domain:ErrorDomainNative location:[NSString stringWithFormat:@"requestWithParameters:pageType_%@:parameters_%@",requestParameters.pageType,parameters.description]];
}
return nil;
}
// Sets up the Initial parameters.
[self getInitialParametersExcludingSections:requestParameters.excludedInitialParameters actionId:requestParameters.identifier ?: [NSUUID UUID].UUIDString completion:^(NSDictionary * _Nullable initialParameters) {
if (initialParameters) {
[parameters setObject:initialParameters forKey:@"InitialParams"];
NSError *jsonError = nil;
NSData *data = nil;
#if DEBUG
// Pretty print for logging the request parameters.
data = [NSJSONSerialization dataWithJSONObject:parameters options:NSJSONWritingPrettyPrinted error:&jsonError];
if (!data) {
if (error) {
*error = [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] messageToLog:jsonError.localizedDescription code:jsonError.code domain:ErrorDomainSystem location:[NSString stringWithFormat:@"requestWithParameters:pageType_%@:parameters_%@",requestParameters.pageType,parameters.description]];
}
completion(parameters);
}];
return nil;
}
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
MVMCoreLog(@"Request Parameters for URL %@:\n%@", [url absoluteString], jsonString);
#endif
// Standard condensed to send to the server.
data = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:&jsonError];
if (!data) {
if (error) {
*error = [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] messageToLog:jsonError.localizedDescription code:jsonError.code domain:ErrorDomainSystem location:[NSString stringWithFormat:@"requestWithParameters:pageType_%@:parameters_%@",requestParameters.pageType,parameters.description]];
}
return nil;
}
return data;
}
- (void)getJsonData:(nonnull MVMCoreRequestParameters *)requestParameters forUrl:(nonnull NSURL *)url completion:(nonnull void (^)(NSData * _Nullable data, MVMCoreErrorObject * _Nullable error))completion {
[self getJsonDictionary:requestParameters completion:^(NSDictionary * _Nullable parameters) {
// Ensure the parameters are valid json.
if (![NSJSONSerialization isValidJSONObject:parameters]) {
completion(nil, [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeParsingJSON domain:ErrorDomainNative location:[NSString stringWithFormat:@"requestWithParameters:pageType_%@:parameters_%@",requestParameters.pageType,parameters.description]]);
return;
}
NSError *jsonError = nil;
NSData *data = nil;
#if LOGGING
// Pretty print for logging the request parameters.
data = [NSJSONSerialization dataWithJSONObject:parameters options:NSJSONWritingPrettyPrinted error:&jsonError];
if (!data) {
completion(nil, [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] messageToLog:jsonError.localizedDescription code:jsonError.code domain:ErrorDomainSystem location:[NSString stringWithFormat:@"requestWithParameters:pageType_%@:parameters_%@",requestParameters.pageType,parameters.description]]);
return;
}
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
MVMCoreNetworkLog(@"Request Parameters for URL %@:\n%@", [url absoluteString], jsonString);
#endif
// Standard condensed to send to the server.
data = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:&jsonError];
if (!data) {
completion(nil, [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] messageToLog:jsonError.localizedDescription code:jsonError.code domain:ErrorDomainSystem location:[NSString stringWithFormat:@"requestWithParameters:pageType_%@:parameters_%@",requestParameters.pageType,parameters.description]]);
return;
}
// Add any image data
if (requestParameters.imageData) {
data = [self createMultipartFormBodyForRequestJsonData:data imageData:requestParameters.imageData];
}
completion(data, nil);
}];
}
- (nonnull NSData *)createMultipartFormBodyForRequestJsonData:(nonnull NSData *)jsonData imageData:(nonnull NSData *)imageData {
- (nonnull NSData *)createMultipartFormBodyForRequestJsonData:(nonnull NSData *)jsonData imageData:(nonnull NSData *)imageData error:(NSError *_Nullable *_Nullable)error {
NSMutableData *body = [[NSMutableData alloc] init];
@ -271,26 +280,11 @@
return body;
}
- (void)sendRequest:(nonnull MVMCoreRequestParameters *)requestParameters locationForError:(nonnull NSString *)locationForError requestFinished:(nullable void (^)(id _Nullable jsonObject, MVMCoreErrorObject *_Nullable error))requestFinished {
[self transformToRequestWithParameters:requestParameters completion:^(NSURLRequest * _Nullable request, MVMCoreErrorObject * _Nullable error) {
if (error) {
if (requestFinished) {
requestFinished(nil, error);
}
return;
}
[self sendRequest:request requestParameters:requestParameters locationForError:locationForError requestFinished:requestFinished];
}];
}
- (nullable NSURLSessionTask *)sendRequest:(nonnull NSURLRequest *)request requestParameters:(nonnull MVMCoreRequestParameters *)requestParameters locationForError:(nonnull NSString *)locationForError requestFinished:(nullable void (^)(id _Nullable jsonObject, MVMCoreErrorObject *_Nullable error))requestFinished {
- (nullable NSURLSessionTask *)sendRequest:(nonnull MVMCoreRequestParameters *)requestParameters locationForError:(nonnull NSString *)locationForError requestFinished:(nullable void (^)(id _Nullable jsonObject, MVMCoreErrorObject *_Nullable error))requestFinished {
#if ENABLE_HARD_CODED_RESPONSE
NSDictionary *response = [[MFHardCodedServerResponse sharedInstance] getHardCodedResponseForRequest:requestParameters];
if (response) {
#if HARD_CODED_RESPONSE_DELAY > 0
[NSThread sleepForTimeInterval:HARD_CODED_RESPONSE_DELAY];
#endif
if (requestFinished) {
requestFinished(response, nil);
}
@ -298,26 +292,34 @@
}
#endif
NSURLSession *session = [MVMCoreSessionObject sharedGlobal].session;
if (!session) {
#warning Hotfix for when session is nil. We should switch to logging these errors once we fix how session handler misses canceling certain calls because they happen at the same time.
MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] messageToLog:nil code:ErrorCodeDefault domain:ErrorDomainNative location:locationForError];
error.logError = NO;
MVMCoreErrorObject *error = nil;
NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate];
NSURLRequest *request = [self requestWithParameters:requestParameters error:&error];
if (!request) {
if (requestFinished) {
requestFinished(nil, error);
requestFinished(nil,error);
}
return nil;
}
MVMCoreNetworkLog(@"********************************* Cookie Sent *********************************");
NSURLSession *session = [MVMCoreSessionObject sharedGlobal].session;
if (!session) {
#warning Hotfix for when session is nil. We should switch to logging these errors once we fix how session handler misses canceling certain calls because they happen at the same time.
error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] messageToLog:nil code:ErrorCodeDefault domain:ErrorDomainNative location:locationForError];
error.logError = NO;
requestFinished(nil,error);
}
MVMCoreLog(@"********************************* Cookie Sent *********************************");
[[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:request.URL] enumerateObjectsUsingBlock:^(NSHTTPCookie * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
MVMCoreNetworkLog(@"Cookie Name: %@, Cookie Value: %@, Domain: %@", obj.name, obj.value, obj.domain);
MVMCoreLog(@"Cookie Name: %@, Cookie Value: %@, Domain: %@", obj.name, obj.value, obj.domain);
}];
NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate];
[[NSNotificationCenter defaultCenter] postNotificationName:MVMCoreNotificationGoingToServer object:nil];
NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
MVMCoreNetworkLog(@"Request Time %f", [NSDate timeIntervalSinceReferenceDate] - startTime);
MVMCoreLog(@"Request Time %f", [NSDate timeIntervalSinceReferenceDate] - startTime);
NSDate *startTimeDate = [NSDate dateWithTimeIntervalSinceReferenceDate:startTime];
@ -330,7 +332,9 @@
[trackInfo setObject:error.localizedDescription forKey:@"error"];
}
MVMCoreNetworkLog(@"Set-Cookie %@ Value: %@", requestParameters.pageType, [(NSHTTPURLResponse *)response allHeaderFields][@"Set-Cookie"]);
MVMCoreLog(@"Set-Cookie %@ Value: %@", requestParameters.pageType, [(NSHTTPURLResponse *)response allHeaderFields][@"Set-Cookie"]);
[MVMCoreLoggingHandler logWithDelegateWithObject:nil withName:@"httpRequestStatus" withExtraInfo:trackInfo];
id jsonObject = nil;
MVMCoreErrorObject *errorObject = nil;
@ -345,7 +349,7 @@
if (!jsonObject) {
// Error serializing json.
errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] messageToLog:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] code:ErrorCodeParsingJSON domain:ErrorDomainSystem location:locationForError];
errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] messageToLog:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] code:ErrorCodeParsingJSON domain:ErrorDomainNative location:locationForError];
} else {
// Uncomment to get the raw UTF-8 string response from server before it is parsed. Useful for identifying issues such as duplicate definitions which are removed after parsing. (Also, be careful of parsing tools such as jsoneditoronline.org which will autoresolve some of these issues as well.)
@ -354,11 +358,11 @@
// Log the response pretty.
NSData *prettyData = [NSJSONSerialization dataWithJSONObject:jsonObject options:NSJSONWritingPrettyPrinted error:&error];
NSString *responseString = [[NSString alloc] initWithData:prettyData encoding:NSUTF8StringEncoding];
MVMCoreNetworkLog(@"Response for Request Page Type %@:\n%@",requestParameters.pageType, responseString);
MVMCoreLog(@"Response for Request Page Type %@:\n%@",requestParameters.pageType, responseString);
}
} else {
// Empty response.
errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeEmptyResponse domain:ErrorDomainSystem location:locationForError];
errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeEmptyResponse domain:ErrorDomainNative location:locationForError];
}
} else if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(createErrorObjectForRequestNSError:forRequest:location:)]) {
@ -384,8 +388,6 @@
return [self loadBackgroundRequest:requestParameters dataForPage:dataForPage delegateObject:delegateObject];
} else {
MVMCoreLoadRequestOperation *loadOperation = [[MVMCoreLoadRequestOperation alloc] initWithRequestParameters:requestParameters dataForPage:dataForPage delegateObject:delegateObject backgroundLoad:NO];
loadOperation.identifier = requestParameters.identifier;
[loadOperation startLoadingAnimationIfNeeded];
[self.blockingLoadQueue addOperation:loadOperation];
return loadOperation;
}
@ -393,15 +395,12 @@
- (MVMCoreLoadRequestOperation *)loadBackgroundRequest:(nonnull MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegateObject:(nullable DelegateObject *)delegateObject {
MVMCoreLoadRequestOperation *loadOperation = [[MVMCoreLoadRequestOperation alloc] initWithRequestParameters:requestParameters dataForPage:dataForPage delegateObject:delegateObject backgroundLoad:YES];
loadOperation.identifier = requestParameters.identifier;
[self.backgroundLoadQueue addOperation:loadOperation];
return loadOperation;
}
- (MVMCoreLoadRequestOperation *)loadObject:(nonnull MVMCoreLoadObject *)loadObject {
MVMCoreLoadRequestOperation *loadOperation = [[MVMCoreLoadRequestOperation alloc] initWithLoadObject:loadObject backgroundLoad:NO];
loadOperation.identifier = loadObject.requestParameters.identifier;
[loadOperation startLoadingAnimationIfNeeded];
[self.blockingLoadQueue addOperation:loadOperation];
return loadOperation;
}

View File

@ -1,22 +0,0 @@
//
// MVMCoreLoadHandler.swift
// MVMCore
//
// Created by Scott Pfeil on 8/28/23.
// Copyright © 2023 myverizon. All rights reserved.
//
import Foundation
public extension MVMCoreLoadHandler {
func sendRequest(with parameters: MVMCoreRequestParameters, locationForError: String, requestFinished: @escaping (Any?, MVMCoreErrorObject?) -> Void) async -> URLSessionTask? {
let result = await transformToRequest(with: parameters)
guard let request = result.0 else {
defer {
requestFinished(nil, result.1)
}
return nil
}
return self.send(request, requestParameters: parameters, locationForError: locationForError, requestFinished: requestFinished)
}
}

View File

@ -11,7 +11,6 @@
#import <MVMCore/MVMCoreLoadDelegateProtocol.h>
#import <MVMCore/MVMCorePresentationDelegateProtocol.h>
#import <MVMCore/MVMCoreActionDelegateProtocol.h>
#import <MVMCore/MVMCoreErrorObject.h>
@class MVMCoreLoadRequestOperation;
@class MVMCoreRequestParameters;
@ -47,6 +46,7 @@
@property (nullable, strong, nonatomic) NSDictionary *dataForPage;
// The load delegate
@property (nullable, weak, nonatomic) NSObject <MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol> *delegate __deprecated;
@property (nullable, strong, nonatomic) DelegateObject *delegateObject;
// The operation that is loading.
@ -59,12 +59,6 @@
// The full response json
@property (nullable, strong, nonatomic) NSDictionary *responseJSON;
// Unique Identifier for event tracking
@property (nullable, strong, nonatomic) NSString *identifier;
// If any error happened during the load.
@property (nullable, strong, nonatomic) MVMCoreErrorObject *error;
- (nullable instancetype)initWithPageJSON:(nullable NSDictionary *)pageJSON modulesJSON:(nullable NSDictionary *)modulesJSON requestParameters:(nullable MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegateObject:(nullable DelegateObject *)delegateObject;
- (nullable instancetype)initWithRequestParameters:(nullable MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegateObject:(nullable DelegateObject *)delegateObject;
@ -76,4 +70,12 @@
// Returns whether the load will extend the app session timer based on the response provided by the server.
- (BOOL)extendsAppSession;
#pragma mark - Deprecated
- (nullable instancetype)initWithPageJSON:(nullable NSDictionary *)pageJSON modulesJSON:(nullable NSDictionary *)modulesJSON requestParameters:(nullable MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject<MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
- (nullable instancetype)initWithRequestParameters:(nullable MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject<MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
- (nullable instancetype)initWithDelegate:(nullable NSObject<MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate __deprecated;
@end

View File

@ -55,9 +55,6 @@
if (errorObject.messageToDisplay) {
[responseInfo setObject:errorObject.messageToDisplay forKey:KeyUserMessage];
}
if (errorObject.requestId) {
self.identifier = errorObject.requestId;
}
self.responseInfoMap = responseInfo;
}
return self;
@ -71,4 +68,44 @@
return NO;
}
#pragma mark - Deprecated
- (void)setDelegateObject:(DelegateObject *)delegateObject {
_delegateObject = delegateObject;
_delegate = delegateObject.loadDelegate;
}
- (void)setDelegate:(NSObject<MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol> *)delegate {
_delegate = delegate;
if ([delegate respondsToSelector:@selector(delegateObject)]) {
_delegateObject = [delegate performSelector:@selector(delegateObject)];
} else {
_delegateObject = [DelegateObject createWithDelegateForAll:delegate];
}
}
- (nullable instancetype)initWithPageJSON:(nullable NSDictionary *)pageJSON modulesJSON:(nullable NSDictionary *)modulesJSON requestParameters:(nullable MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject<MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate {
if (self = [self initWithRequestParameters:requestParameters dataForPage:dataForPage delegate:delegate]) {
self.pageJSON = pageJSON;
self.modulesJSON = modulesJSON;
}
return self;
}
- (nullable instancetype)initWithRequestParameters:(nullable MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject<MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate {
if (self = [self initWithDelegate:delegate]) {
self.requestParameters = requestParameters;
self.dataForPage = dataForPage;
}
return self;
}
- (nullable instancetype)initWithDelegate:(nullable NSObject<MVMCoreLoadDelegateProtocol,MVMCorePresentationDelegateProtocol,MVMCoreActionDelegateProtocol>*)delegate {
if (self = [super init]) {
self.delegate = delegate;
}
return self;
}
@end

View File

@ -1,137 +0,0 @@
//
// MVMCoreLoadRequestOperation+Extension.swift
// MVMCore
//
// Created by Scott Pfeil on 8/16/23.
// Copyright © 2023 myverizon. All rights reserved.
//
import Foundation
public enum PopBackError: MVMError, CustomStringConvertible {
case noPageType
case noNavigationHandler
case noViewController
public var description: String {
switch self {
case .noPageType:
return "Cannot pop with a pageType."
case .noNavigationHandler:
return "Cannot pop with a navigation controller."
case .noViewController:
return "No View Controller of popBackPageType in the hierarchy"
}
}
public var errorCode: Int {
switch self {
case .noPageType:
return 55
case .noNavigationHandler:
return 56
case .noViewController:
return 57
}
}
}
@objc
public extension MVMCoreLoadRequestOperation {
/// Attempt to navigate to the controller with the given load object. Return the controller that we navigated to if successful.
@objc
@MainActor
func goToViewController(loadObject: MVMCoreLoadObject) async -> UIViewController? {
guard loadObject.requestParameters?.replaceViewIfOnStackElseLoadWithStyle == true,
loadObject.requestParameters?.replaceViewControllerIfOnStackGoToOnly == true,
let pageType = loadObject.pageType else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "\(type(of: self)): A new controller should be made. pageType:\(String(describing: loadObject.pageType))")
return nil
}
let template = loadObject.pageJSON?.optionalStringForKey("template")
guard let controllerMappingObject = MVMCoreViewControllerMappingObject.shared()?.getViewControllerMapping(forTemplate: template, pageType: pageType) else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "\(type(of: self)): Failed to create a new controller. template:\(String(describing: template)) page:\(pageType)")
return nil
}
var controllerType: AnyClass?
if let programmaticMapping = controllerMappingObject as? MVMCoreViewControllerProgrammaticMappingObject {
controllerType = programmaticMapping.viewControllerClass
} else if let newVC = MVMCoreViewControllerMappingObject.shared()?.createMFViewController(ofTemplate: loadObject.pageJSON?.optionalStringForKey("template"), pageType: pageType) {
// Need to create the view controller to fetch the type.
controllerType = type(of: newVC)
} else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "\(type(of: self)): Failed to create a new controller. template:\(String(describing: template)) page:\(pageType)")
return nil
}
guard let viewController = await NavigationHandler.shared().navigateToViewController(of: pageType, controllerType: controllerType) else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "\(type(of: self)): No matching controller found in the hierarchy. Will need to create a new controller. pageType:\(pageType) controllerType:\(String(describing: controllerType)).")
return nil
}
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "\(type(of: self)): Navigated to controller. pageType:\(pageType) controllerType:\(String(describing: controllerType))")
stopLoadingAnimationIfNeeded()
return viewController
}
@objc
func popBackToPage(for loadObject: MVMCoreLoadObject) {
Task(priority: .high) {
do {
guard let popBackPageType = loadObject.pageJSON?.optionalStringForKey("popBackPageType") else {
throw PopBackError.noPageType
}
let operation = try await NavigationHandler.shared().getOperationPopToViewController(with: popBackPageType, navigationController: loadObject.requestParameters?.navigationController, delegateObject: loadObject.delegateObject, animated: !(loadObject.requestParameters?.shouldNotAnimatePush ?? false))
await navigate(with: operation, loadObject: loadObject)
MVMCoreLoadRequestOperation.loadFinished(loadObject, loadedViewController: nil, errorObject: nil)
} catch {
let error = MVMCoreErrorObject.createErrorObject(for: error, location: MVMCoreLoadHandler.sharedGlobal()?.errorLocation(forRequest: loadObject))!
MVMCoreLoadHandler.sharedGlobal()?.attachLoadInformation(loadObject, toError: error)
MVMCoreLoadRequestOperation.loadAbortedWithError(error, loadObject: loadObject)
}
}
}
@objc
func display(viewController: UIViewController, loadObject: MVMCoreLoadObject?, completionHandler: @escaping () -> Void) {
Task(priority: .high) {
guard let navigationOperation = await NavigationHandler.shared().getNavigationOperation(with: viewController, loadObject: loadObject) else {
completionHandler()
return
}
await navigate(with: navigationOperation, loadObject: loadObject)
completionHandler()
}
}
func navigate(with navigationOperation: NavigationOperation, loadObject: MVMCoreLoadObject?) async {
// stop any loading animation we may have started if we are about to display
cancellable = NavigationHandler.shared().onNavigation
.filter { $0.0 == .willNavigate && navigationOperation == $0.1 }
.sink { [weak self] (event, operation) in
self?.stopLoadingAnimationIfNeeded()
}
await NavigationHandler.shared().navigate(with: navigationOperation)
cancellable = nil
}
}
public extension NavigationHandler {
@MainActor
func getOperationPopToViewController(with pageType: String, navigationController: UINavigationController? = nil, delegateObject: DelegateObject? = nil, animated: Bool = true) throws -> NavigationOperation {
guard let navigationController = navigationController ?? NavigationHandler.shared().navigationController else {
throw PopBackError.noNavigationHandler
}
guard let viewController = navigationController.viewControllers.first(where: { viewController in
(viewController as? MVMCoreViewManagerProtocol)?.containsPage(withPageType: pageType) == true ||
(viewController as? MVMCoreViewControllerProtocol)?.pageType == pageType
}) else {
throw PopBackError.noViewController
}
return NavigationOperation(with: .popTo(viewController: viewController, navigationController: navigationController, animated: animated), delegate: delegateObject?.presentationDelegate)
}
func popToViewController(with pageType: String, navigationController: UINavigationController? = nil, delegateObject: DelegateObject? = nil, animated: Bool = true) async throws {
try await NavigationHandler.shared().navigate(with: getOperationPopToViewController(with: pageType, navigationController: navigationController, delegateObject: delegateObject, animated: animated))
}
}

View File

@ -16,37 +16,24 @@
@class MVMCoreLoadObject;
@class DelegateObject;
@interface MVMCoreLoadRequestOperation : MVMCoreOperation
@interface MVMCoreLoadRequestOperation : MVMCoreOperation <MVMCorePresentationDelegateProtocol>
@property (nullable, strong, nonatomic) MVMCoreRequestParameters *requestParameters;
@property (nullable, strong, nonatomic) MVMCoreLoadObject *loadObject;
@property (nullable, strong, nonatomic) NSDictionary *dataForPage;
@property (nullable, strong, nonatomic) DelegateObject *delegateObject;
@property (nullable, nonatomic, readonly) NSURLSessionTask *sessionTask;
@property (nullable, nonatomic, readonly) NSString *finalLoadSource;
@property (nonatomic) BOOL backgroundLoad;
@property (nonatomic, getter=areDependenciesAdded) BOOL dependenciesAdded;
@property (nonnull, nonatomic, strong) NSString *identifier;
/// If any error happened during the operation.
@property (nullable, strong, nonatomic) MVMCoreErrorObject *error;
/// Legacy flag for if this operation will have an alert to show when finished.
@property (nonatomic, readonly) BOOL alertToShow;
@property (nullable, strong, nonatomic) id cancellable;
// 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;
// Initializes the operation with the load object, data for page, and mvm view controller to handle the loading with. Can be used for loading a screen without going to the cache or server.
- (nullable instancetype)initWithLoadObject:(nullable MVMCoreLoadObject *)loadObject backgroundLoad:(BOOL)backgroundLoad;
/// Begins the loading animation if needed.
- (void)startLoadingAnimationIfNeeded;
/// Ends the loading animation if needed.
- (void)stopLoadingAnimationIfNeeded;
/* Checks the cache for the data and calls the completion handler with any found data.
* @param completionHandler The block that gets called with any fetched data. */
+ (void)checkCacheForDataForRequest:(nonnull MVMCoreRequestParameters *)requestParameters completionHandler:(nonnull void (^)(NSDictionary * _Nullable pageFromCache, NSDictionary * _Nullable modulesFromCache))completionHandler;
@ -95,8 +82,6 @@
*/
+ (void)removeCaches:(nullable NSDictionary *)cacheDictionary;
+ (void)notifyListenersOfNewResponse:(nullable NSDictionary *)pages modules:(nullable NSDictionary *)modules systemParameters:(nullable NSDictionary *)systemParameters loadObject:(nonnull MVMCoreLoadObject *)loadObject;
/** Creates the view controller based on the load object passed in.
* @param loadObject The load data from the cache or server.
* @param completionHandler The completion handler to load once finished. Returns any loaded view controller and the load.*/

View File

@ -6,13 +6,15 @@
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <MVMCore/MVMCoreLoadRequestOperation.h>
#import "MVMCoreLoadHandler.h"
#import "MVMCoreLoadingOverlayHandler.h"
#import "MVMCoreCache.h"
#import "MVMCoreSessionTimeHandler.h"
#import "MVMCoreLoggingHandler.h"
#import "MVMCoreSessionObject.h"
#import "MVMCoreViewControllerMappingObject.h"
#import "MVMCoreNavigationHandler.h"
#import <MVMCore/MVMCoreDispatchUtility.h>
#import "MVMCoreLoadObject.h"
#import "MVMCoreRequestParameters.h"
@ -22,20 +24,19 @@
#import "MVMCoreHardcodedStringsConstants.h"
#import "MVMCoreErrorConstants.h"
#import "MVMCoreActionUtility.h"
#import <MVMCore/MVMCoreActionHandler.h>
#import "MVMCoreObject.h"
#import "MVMCoreConstants.h"
#import "MVMCoreLoggingHandlerHelper.h"
#import <MVMCore/MVMCore-Swift.h>
@interface MVMCoreLoadRequestOperation ()
@property (strong, nonatomic) NSURLSessionTask *sessionTask;
@property (weak, nonatomic) NSURLSessionTask *sessionTask;
// For temporarily storing any alert to show until we determine it's delegate.
@property (nonatomic, readwrite) BOOL alertToShow;
@property (strong, nonatomic, nullable) MVMCoreErrorObject *errorForAlertToShow;
@property (nonatomic, readwrite) BOOL loadingAnimationRunning;
@end
@implementation MVMCoreLoadRequestOperation
@ -55,7 +56,6 @@
- (nullable instancetype)initWithLoadObject:(nullable MVMCoreLoadObject *)loadObject backgroundLoad:(BOOL)backgroundLoad {
if (self = [self initWithRequestParameters:loadObject.requestParameters dataForPage:loadObject.dataForPage delegateObject:loadObject.delegateObject backgroundLoad:backgroundLoad]) {
self.identifier = loadObject.identifier;
self.loadObject = loadObject;
}
return self;
@ -66,27 +66,26 @@
- (void)cancel {
[super cancel];
[self.sessionTask cancel];
[self stopLoadingAnimationIfNeeded];
}
- (void)start {
// Adds a loading overlay if necessary.
[self startLoadingAnimationIfNeeded];
if (!self.backgroundLoad && !self.requestParameters.noloadingOverlay) {
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] startLoading];
}
[super start];
}
- (NSString *)finalLoadSource {
return _sessionTask.currentRequest.URL.absoluteString;
}
- (void)markAsFinished {
// stop any loading animation we may have started
[self stopLoadingAnimationIfNeeded];
if (!self.backgroundLoad && !self.requestParameters.noloadingOverlay) {
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:NO];
}
MVMCoreNetworkLog(@"Load Operation finished for page type %@, background load %@", self.requestParameters.pageType, @(self.backgroundLoad));
MVMCoreLog(@"Load Operation finished for page type %@, background load %@", self.requestParameters.pageType, @(self.backgroundLoad));
[super markAsFinished];
}
@ -107,9 +106,7 @@
}
- (void)main {
MVMCoreNetworkLog(@"Load Operation begun for page type %@, background load %@, delegate %@", self.requestParameters.pageType, @(self.backgroundLoad),self.delegateObject.loadDelegate);
[self.requestParameters resolveURL:[MVMCoreSessionObject sharedGlobal]];
MVMCoreLog(@"Load Operation begun for page type %@, background load %@, delegate %@", self.requestParameters.pageType, @(self.backgroundLoad),self.delegateObject.loadDelegate);
// Always check for cancellation before launching the task.
if ([self checkAndHandleForCancellation]) {
@ -120,7 +117,6 @@
// No load requested, finish.
MVMCoreLoadObject *loadObject = [[MVMCoreLoadObject alloc] initWithRequestParameters:nil dataForPage:self.dataForPage delegateObject:self.delegateObject];
loadObject.identifier = self.identifier;
loadObject.operation = self;
[MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:nil errorObject:nil];
} else if (self.loadObject) {
@ -139,45 +135,26 @@
// Log if loaded from cache.
if (pageFromCache) {
MVMCoreNetworkLog(@"loaded from cache page %@",[MVMCoreActionUtility formatDictionaryAsJSONString:pageFromCache]);
MVMCoreLog(@"loaded from cache page %@",[MVMCoreActionUtility formatDictionaryAsJSONString:pageFromCache]);
}
if (modulesFromCache) {
MVMCoreNetworkLog(@"loaded from cache modules %@",[MVMCoreActionUtility formatDictionaryAsJSONString:modulesFromCache]);
MVMCoreLog(@"loaded from cache modules %@",[MVMCoreActionUtility formatDictionaryAsJSONString:modulesFromCache]);
}
// Create a load object from any data we fetched.
MVMCoreLoadObject *loadObject = [self createLoadObjectWithPageFromCache:pageFromCache modulesFromCache:modulesFromCache];
self.loadObject = loadObject;
// Check if we need to go to server for missing data.
MVMCoreRequestParameters *requestForMissingData = [MVMCoreLoadRequestOperation createRequestForDataWithLoadObject:loadObject];
if (!requestForMissingData) {
[[MVMCoreLoggingHandler sharedLoggingHandler] logPageLoadCompleteFor:loadObject.requestParameters.pageType serverProcessingTime:@"0" requestURL:loadObject.requestParameters.URL.absoluteString requestUUID:loadObject.identifier isFromCache:loadObject.pageDataFromCache];
// We have all the needed data, continue with the load.
[MVMCoreLoadRequestOperation handleLoadObject:loadObject error:nil];
} else {
if(!self.backgroundLoad && loadObject.requestParameters.pageType) {
[[MVMCoreLoggingHandler sharedLoggingHandler] logPageLoadStartedFor:loadObject.requestParameters.pageType requestUUID:loadObject.identifier requestURL:loadObject.requestParameters.URL.absoluteString];
}
// Send a new request to the server.
[MVMCoreLoadRequestOperation sendRequest:requestForMissingData loadObject:loadObject completionHandler:^(NSDictionary * _Nullable json) {
#if ENABLE_HARD_CODED_RESPONSE
if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(modifyJSON:)]) {
json = [[MVMCoreObject sharedInstance].globalLoadDelegate modifyJSON:json];
}
#endif
NSString *serverProcessTime = [(NSDictionary *)json objectChainOfKeysOrIndexes:@[@"ResponseInfo", @"timeStamp"]] ?: @"0";
if(!self.backgroundLoad && loadObject.requestParameters.pageType && serverProcessTime) {
[[MVMCoreLoggingHandler sharedLoggingHandler] logPageLoadCompleteFor:loadObject.requestParameters.pageType serverProcessingTime:serverProcessTime requestURL:loadObject.requestParameters.URL.absoluteString requestUUID:loadObject.identifier isFromCache:loadObject.pageDataFromCache];
}
// Process the data retrieved from the server.
[MVMCoreLoadRequestOperation processJSONFromServer:json loadObject:loadObject completionHandler:^(MVMCoreLoadObject * _Nonnull loadObject, MVMCoreErrorObject * _Nullable error) {
@ -202,20 +179,6 @@
#pragma mark - Load Functions
- (void)startLoadingAnimationIfNeeded {
if (self.loadingAnimationRunning) { return; }
if (self.backgroundLoad) { return; }
if (self.requestParameters.noloadingOverlay) { return; }
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] startLoading];
self.loadingAnimationRunning = YES;
}
- (void)stopLoadingAnimationIfNeeded {
if (!self.loadingAnimationRunning) { return; }
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:YES];
self.loadingAnimationRunning = NO;
}
+ (void)checkCacheForDataForRequest:(nonnull MVMCoreRequestParameters *)requestParameters completionHandler:(nonnull void (^)(NSDictionary * _Nullable pageFromCache, NSDictionary * _Nullable modulesFromCache))completionHandler {
if (requestParameters.neverLoadFromCache) {
@ -260,7 +223,7 @@
if (pageFromCache) {
loadObject.pageType = self.requestParameters.pageType;
}
loadObject.identifier = self.identifier;
// Store if we loaded from the cache or not.
loadObject.pageDataFromCache = (pageFromCache != nil);
loadObject.moduleDataFromCache = (modulesFromCache != nil);
@ -310,44 +273,29 @@
return;
}
#if ENABLE_HARD_CODED_RESPONSE
if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(getJSONForRequestParameters:)]) {
NSDictionary *json = [[MVMCoreObject sharedInstance].globalLoadDelegate getJSONForRequestParameters:requestParameters];
if (json) {
#if HARD_CODED_RESPONSE_DELAY > 0
[NSThread sleepForTimeInterval:HARD_CODED_RESPONSE_DELAY];
#endif
completionHandler(json);
loadObject.operation.sessionTask = [[MVMCoreLoadHandler sharedGlobal] sendRequest:requestParameters locationForError:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject] requestFinished:^(id jsonObject, MVMCoreErrorObject *error) {
if ([loadObject.operation checkAndHandleForCancellation]) {
return;
}
}
#endif
[[MVMCoreLoadHandler sharedGlobal] transformToRequestWithParameters:requestParameters completion:^(NSURLRequest * _Nullable request, MVMCoreErrorObject * _Nullable error) {
loadObject.operation.sessionTask = [[MVMCoreLoadHandler sharedGlobal] sendRequest:request requestParameters:requestParameters locationForError:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject] requestFinished:^(id jsonObject, MVMCoreErrorObject *error) {
if (jsonObject) {
if ([loadObject.operation checkAndHandleForCancellation]) {
return;
}
if (jsonObject) {
if ([jsonObject isKindOfClass:[NSDictionary class]]) {
completionHandler(jsonObject);
if ([jsonObject isKindOfClass:[NSDictionary class]]) {
completionHandler(jsonObject);
} else {
// Error json not correct format.
MVMCoreErrorObject *errorObject = [[MVMCoreLoadHandler sharedGlobal] errorForLoadObject:loadObject withTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeJSONNotDictionary domain:ErrorDomainSystem];
[MVMCoreLoadRequestOperation loadAbortedWithError:errorObject loadObject:loadObject];
}
} else {
// Error with the request/response
[[MVMCoreLoadHandler sharedGlobal] attachLoadInformation:loadObject toError:error];
[MVMCoreLoadRequestOperation loadAbortedWithError:error loadObject:loadObject];
// Error json not correct format.
MVMCoreErrorObject *errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeJSONNotDictionary domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
[MVMCoreLoadRequestOperation loadAbortedWithError:errorObject loadObject:loadObject];
}
}];
} else {
// Error with the request/response
[MVMCoreLoadRequestOperation loadAbortedWithError:error loadObject:loadObject];
}
}];
}
@ -425,9 +373,7 @@
// Update the session timer on a good response.
if (loadObject.extendsAppSession) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[[MVMCoreSessionTimeHandler sharedSessionHandler] startSessionTimer];
}];
[[MVMCoreSessionTimeHandler sharedSessionHandler] startSessionTimer];
}
// Adds the modules received from server to any modules we grabbed from the cache.
@ -444,10 +390,6 @@
NSDictionary *systemParameters = [jsonDictionary dict:KeySystemParameters];
loadObject.systemParametersJSON = systemParameters.count > 0 ? systemParameters : nil;
if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(willProcessLoadObject:)]) {
[[MVMCoreObject sharedInstance].globalLoadDelegate willProcessLoadObject:loadObject];
}
// module items are cached.
MVMCoreErrorObject *moduleCachingError = nil;
BOOL shouldContinue = [MVMCoreLoadRequestOperation cacheModules:modules loadObject:loadObject error:&moduleCachingError];
@ -507,7 +449,9 @@
} else if ([ValuePresentationStylePopToPage isEqualToString:[loadObject.pageJSON string:KeyPresentationStyle]] && !loadObject.operation.backgroundLoad && !loadObject.requestParameters.noViewControllerToLoad) {
// We need to pop back to a page instead of loading a new page.
[loadObject.operation popBackToPageFor:loadObject];
[[MVMCoreNavigationHandler sharedNavigationHandler] popToViewControllerWithPageType:[loadObject.pageJSON string:@"popBackPageType"] navigationController:loadObject.requestParameters.navigationController animated:!loadObject.requestParameters.shouldNotAnimatePush delegate:loadObject.operation completionHandler:^{
[MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:nil errorObject:error];
}];
// Notify listeners of module/pages loaded.
[MVMCoreLoadRequestOperation notifyListenersOfNewResponse:pages modules:modules systemParameters:systemParameters loadObject:loadObject];
@ -551,7 +495,6 @@
if (error.errorScreenError && !error.nativeDrivenErrorScreen && loadObject.pageType.length == 0) {
error.logError = YES;
error.errorScreenError = NO;
error.silentError = NO;
error.location = [NSString stringWithFormat:@"%li-%@",(long)ErrorCodeNoErrorPageSent,error.location];
[MVMCoreLoadRequestOperation handleError:error loadObject:loadObject showAlertForErrorIfApplicable:YES];
[MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:nil errorObject:error];
@ -571,16 +514,9 @@
if (viewController) {
// Display or finish
if (error.errorScreenError) {
error.silentError = NO;
if (error.nativeDrivenErrorScreen) {
[[MVMCoreLoggingHandler sharedLoggingHandler]addErrorToLog:error];
}
[MVMCoreLoadRequestOperation displayViewController:viewController loadObject:loadObject error:error];
} else if (!loadObject.requestParameters.dontDisplayViewController) {
if (error.errorScreenError || !loadObject.requestParameters.dontDisplayViewController) {
[MVMCoreLoadRequestOperation displayViewController:viewController loadObject:loadObject error:error];
} else {
[[MVMCoreLoggingHandler sharedLoggingHandler]addErrorToLog:error];
[MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:viewController errorObject:error];
}
} else if (!loadObject.requestParameters.shouldNotGoToServerOnCacheFail && (loadObject.pageDataFromCache || loadObject.moduleDataFromCache)) {
@ -592,25 +528,16 @@
loadObject.operation.errorForAlertToShow = nil;
[loadObject.operation main];
} else {
[[MVMCoreLoggingHandler sharedLoggingHandler]addErrorToLog:error];
// Otherwise the request is finished.
[MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:nil errorObject:error];
}
};
if (!error.nativeDrivenErrorScreen) {
// Server driven screen, create normally
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[loadObject.operation goToViewControllerWithLoadObject:loadObject completionHandler:^(UIViewController * _Nullable viewController) {
[MVMCoreDispatchUtility performBlockInBackground:^{
if (viewController) {
[MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:viewController errorObject:nil];
} else {
[MVMCoreLoadRequestOperation createViewControllerWithLoadObject:loadObject completionHandler:completionHandler];
}
}];
}];
}];
[MVMCoreLoadRequestOperation createViewControllerWithLoadObject:loadObject completionHandler:completionHandler];
} else {
// Get the proper native error screen from the delegate
[MVMCoreDispatchUtility performBlockOnMainThread:^{
@ -641,8 +568,7 @@
} else if ([ValueTypeErrorScreen isEqualToString:type]) {
// Error Screen, abort the load and handle the screen if necessary
MVMCoreErrorObject *error = [[MVMCoreLoadHandler sharedGlobal] attachLoadInformation:loadObject toError:[MVMCoreErrorObject createErrorObjectForErrorInfo:loadObject.responseInfoMap location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]];
MVMCoreErrorObject *error = [MVMCoreErrorObject createErrorObjectForErrorInfo:loadObject.responseInfoMap location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
[MVMCoreLoadRequestOperation loadAbortedWithError:error loadObject:loadObject];
} else {
@ -653,7 +579,7 @@
} else {
// Check for controller specific errors.
BOOL shouldContinue;
MVMCoreErrorObject *error = [[MVMCoreLoadHandler sharedGlobal] attachLoadInformation:loadObject toError:[MVMCoreErrorObject createErrorObjectForErrorInfo:loadObject.responseInfoMap location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]];
MVMCoreErrorObject *error = [MVMCoreErrorObject createErrorObjectForErrorInfo:loadObject.responseInfoMap location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
if ([loadObject.delegateObject.loadDelegate respondsToSelector:@selector(checkForDelegateSpecificErrors:loadObject:completionHandler:)]) {
shouldContinue = [loadObject.delegateObject.loadDelegate checkForDelegateSpecificErrors:error loadObject:loadObject completionHandler:completionHandler];
} else {
@ -685,30 +611,16 @@
}
if (obj && [obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *responseInfo = [obj dict:KeyResponseInfo];
//Response Info is missing but errorObject should be created with generic message + code + domain
if (responseInfo == nil) {
errorObject = [[MVMCoreLoadHandler sharedGlobal]
errorForLoadObject:loadObject
withTitle:nil
message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical]
messageToLog:[NSString stringWithFormat:@"Module %@ is missing a %@ object.", key, KeyResponseInfo]
code:ErrorCodeJSONNotDictionary
domain:ErrorDomainServer];
} else if (![ValueTypeSuccess isEqualToString:[responseInfo string:KeyType]]) {
errorObject = [[MVMCoreLoadHandler sharedGlobal] attachLoadInformation:loadObject toError:
[[MVMCoreErrorObject alloc]
initWithTitle:[responseInfo stringForKey:KeyErrorHeading]
message:[responseInfo stringForKey:KeyUserMessage]
messageToLog:[responseInfo stringForKey:KeyMessage]
code:[[responseInfo string:KeyCode] integerValue]
domain:ErrorDomainServer
location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]];
if (![ValueTypeSuccess isEqualToString:[responseInfo string:KeyType]]) {
errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[responseInfo stringForKey:KeyErrorHeading] message:[responseInfo stringForKey:KeyUserMessage] messageToLog:[responseInfo stringForKey:KeyMessage] code:[[responseInfo string:KeyCode] integerValue] domain:ErrorDomainServer location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
}
// Caches each dictionary from the array.
[[MVMCoreCache sharedCache] addModuleToCache:obj module:key queue:nil waitUntilFinished:YES completionBlock:NULL];
} else {
errorObject = [[MVMCoreLoadHandler sharedGlobal] errorForLoadObject:loadObject withTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeJSONNotDictionary domain:ErrorDomainSystem];
errorObject = [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeJSONNotDictionary domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
}
if (errorObject) {
@ -751,15 +663,15 @@
if (pageType) {
[[MVMCoreCache sharedCache] addPageToCache:obj pageType:pageType queue:nil waitUntilFinished:YES completionBlock:NULL];
} else {
errorObject = [[MVMCoreLoadHandler sharedGlobal] errorForLoadObject:loadObject withTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeNoPageType domain:ErrorDomainNative];
errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeNoPageType domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
}
} else {
errorObject = [[MVMCoreLoadHandler sharedGlobal] errorForLoadObject:loadObject withTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeJSONNotDictionary domain:ErrorDomainNative];
errorObject = [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeJSONNotDictionary domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
}
// Logs the error.
if (errorObject) {
[[MVMCoreLoggingHandler sharedLoggingHandler]addErrorToLog:errorObject];
[MVMCoreLoggingHandler addErrorToLog:errorObject];
}
}];
@ -793,15 +705,12 @@
// Allows the view controller to handle specific errors (such as ensure it has all the required data).
shouldContinue = [viewController shouldFinishProcessingLoad:loadObject error:&error];
if (error) {
error = [[MVMCoreLoadHandler sharedGlobal] attachLoadInformation:loadObject toError:error];
}
if (!shouldContinue) {
viewController = nil;
}
} else {
// Couldn't initialize view controller, serious error.
error = [[MVMCoreLoadHandler sharedGlobal] errorForLoadObject:loadObject withTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeInitViewController domain:ErrorDomainNative];
error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeInitViewController domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
}
[MVMCoreLoadRequestOperation handleShouldContinue:shouldContinue error:error loadObject:loadObject errorBlock:NULL continueBlock:^{
@ -819,7 +728,7 @@
}
// Once displyed, we are finished.
[loadObject.operation displayWithViewController:viewController loadObject:loadObject completionHandler:^{
[MVMCoreActionUtility displayViewController:viewController forLoadObject:loadObject presentationDelegate:loadObject.operation completionHandler:^{
[MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:viewController errorObject:error];
}];
}
@ -830,19 +739,17 @@
return;
}
MVMCoreNetworkLog(@"Error: %@ %@ %@ %@ %@",[error stringErrorCode], error.domain, error.location,error.messageToDisplay, error.messageToLog);
// Logs the error.
if (error.logError) {
[MVMCoreLoggingHandler addErrorToLog:error];
}
MVMCoreLog(@"Error: %@ %@ %@ %@ %@",[error stringErrorCode], error.domain, error.location,error.messageToDisplay, error.messageToLog);
if (showAlertForErrorIfApplicable && (!loadObject.operation.backgroundLoad || loadObject.requestParameters.allowAlertsIfBackgroundRequest) && !loadObject.requestParameters.handleErrorsSilently && !error.silentError && !error.errorScreenError) {
// Show alert for error.
[MVMCoreLoadRequestOperation createAndShowAlertForLoadObject:loadObject error:error delegateObject:loadObject.operation.delegateObject];
} else {
error.silentError = !error.errorScreenError && !error.nativeDrivenErrorScreen;
}
// Logs the error. If its a native driven error screen postpone until the native screen is define with its messaging.
if (error.logError && !error.nativeDrivenErrorScreen) {
[[MVMCoreLoggingHandler sharedLoggingHandler]addErrorToLog:error];
}
}
@ -891,8 +798,7 @@
//make post load calls function
[MVMCoreLoadRequestOperation loadPostCallActions:loadObject];
[MVMCoreLoadRequestOperation loadPostAction:loadObject delegateObject:delegateObject];
loadObject.error = errorObject;
loadObject.operation.error = errorObject;
[loadObject.operation markAsFinished];
}
@ -935,4 +841,65 @@
}
}
#pragma mark - Presentation Delegate
- (void)navigationController:(UINavigationController *)navigationController prepareDisplayForViewController:(UIViewController *)viewController {
if ([self.delegateObject.presentationDelegate respondsToSelector:@selector(navigationController:prepareDisplayForViewController:)]) {
[self.delegateObject.presentationDelegate navigationController:navigationController prepareDisplayForViewController:viewController];
}
}
- (void)navigationController:(UINavigationController *)navigationController willDisplayViewController:(UIViewController *)viewController {
// stop any loading animation we may have started if we are about to display
if (!self.backgroundLoad && !self.requestParameters.noloadingOverlay) {
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:NO];
}
if ([self.delegateObject.presentationDelegate respondsToSelector:@selector(navigationController:willDisplayViewController:)]) {
[self.delegateObject.presentationDelegate navigationController:navigationController willDisplayViewController:viewController];
}
}
- (void)navigationController:(UINavigationController *)navigationController displayedViewController:(UIViewController *)viewController {
if ([self.delegateObject.presentationDelegate respondsToSelector:@selector(navigationController:displayedViewController:)]) {
[self.delegateObject.presentationDelegate navigationController:navigationController displayedViewController:viewController];
}
}
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
if (self.delegateObject.presentationDelegate && [self.delegateObject.presentationDelegate respondsToSelector:@selector(navigationController:animationControllerForOperation:fromViewController:toViewController:)]) {
return [self.delegateObject.presentationDelegate navigationController:navigationController animationControllerForOperation:operation fromViewController:fromVC toViewController:toVC];
} else {
return nil;
}
}
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
if (self.delegateObject.presentationDelegate && [self.delegateObject.presentationDelegate respondsToSelector:@selector(navigationController:interactionControllerForAnimationController:)]) {
return [self.delegateObject.presentationDelegate navigationController:navigationController interactionControllerForAnimationController:animationController];
} else {
return nil;
}
}
- (void)navigationController:(UINavigationController *)navigationController interactiveTransitionWasCanceled:(BOOL)canceled {
if (self.delegateObject.presentationDelegate && [self.delegateObject.presentationDelegate respondsToSelector:@selector(navigationController:interactiveTransitionWasCanceled:)]) {
[self.delegateObject.presentationDelegate navigationController:navigationController interactiveTransitionWasCanceled:canceled];
}
}
- (void)viewController:(UIViewController *)presentingViewController willPresentViewController:(UIViewController *)presentedViewController {
if ([self.delegateObject.presentationDelegate respondsToSelector:@selector(viewController:willPresentViewController:)]) {
[self.delegateObject.presentationDelegate viewController:presentingViewController willPresentViewController:presentedViewController];
}
}
- (void)viewController:(UIViewController *)presentingViewController didPresentViewController:(UIViewController *)presentedViewController {
if ([self.delegateObject.presentationDelegate respondsToSelector:@selector(viewController:didPresentViewController:)]) {
[self.delegateObject.presentationDelegate viewController:presentingViewController didPresentViewController:presentedViewController];
}
}
@end

View File

@ -9,7 +9,6 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <MVMCore/MVMCoreSessionObject.h>
// The loading style.
// MFLoadStyleDefault: This means it has not been explicitely set by the developer. Standard push.
@ -79,13 +78,8 @@ typedef NS_ENUM(NSInteger, MFLoadStyle) {
// Determines how it is loaded.
@property (nonatomic) MFLoadStyle loadStyle;
/// Determines if we should search the stack for a controller with the same pageType or just load a new controller with style. Default true
@property (nonatomic) BOOL replaceViewIfOnStackElseLoadWithStyle;
/// Determines if, when replaceViewIfOnStackElseLoadWithStyle is true, we should create a new controller or only pop back to the existing controller. Default true
@property (nonatomic) BOOL replaceViewControllerIfOnStackGoToOnly;
// A flag for if a tab was pressed to cause this load. This will ensure that we do not load a new tab page.
@property (nonatomic) BOOL tabWasPressed;
@ -106,9 +100,6 @@ typedef NS_ENUM(NSInteger, MFLoadStyle) {
/// A flag for if it should be a background request or not.
@property (assign, nonatomic) BOOL backgroundRequest;
//Unique Identifier for event tracking
@property (nullable, strong, nonatomic) NSString *identifier;
// Creates an object with the given page type and extra parameters. Adds the extra parameters to the standard request parameters. Will add any modules needed by the page type by default.
- (nullable instancetype)initWithPageType:(nonnull NSString *)pageType extraParameters:(nullable NSDictionary *)extraParameters;
@ -133,7 +124,4 @@ typedef NS_ENUM(NSInteger, MFLoadStyle) {
/// Returns optional and required modules
- (nullable NSArray<NSString *> *)allModules;
/// Resolves the URL given the session object and the current request parameters.
- (nonnull NSURL *)resolveURL:(nonnull MVMCoreSessionObject *)sessionObject;
@end

View File

@ -10,12 +10,9 @@
#import "NSDictionary+MFConvenience.h"
#import "MVMCoreJSONConstants.h"
#import "MVMCoreViewControllerMappingObject.h"
#import "MVMCoreConstants.h"
@interface MVMCoreRequestParameters ()
@property (nonatomic, strong, nullable) NSURL *determinedURL;
- (nullable instancetype)initWithExtraParameters:(nullable NSDictionary *)extraParameters;
@end
@ -29,8 +26,6 @@
// Default load style.
self.loadStyle = MFLoadStyleDefault;
self.replaceViewIfOnStackElseLoadWithStyle = YES;
self.replaceViewControllerIfOnStackGoToOnly = YES;
self.identifier = [[NSUUID UUID] UUIDString];
}
return self;
}
@ -91,56 +86,6 @@
return self;
}
- (void)setAlternateBaseURL:(NSURL *)alternateBaseURL {
_alternateBaseURL = alternateBaseURL;
_URL = self.determinedURL; // Reset resolution when changed.
}
- (void)setPageType:(NSString *)pageType {
_pageType = pageType;
_URL = self.determinedURL; // Reset resolution when changed.
}
- (void)setContextRoot:(NSString *)contextRoot {
_contextRoot = contextRoot;
_URL = self.determinedURL; // Reset resolution when changed.
}
- (void)setURL:(NSURL *)URL {
self.determinedURL = URL; // If set directly, this becomes the ultimate URL.
_URL = URL;
}
- (NSURL *)resolveURL:(MVMCoreSessionObject *)sessionObject {
if (self.URL) {
// Previously resovled.
return self.URL;
}
NSURL *url;
if (self.alternateBaseURL) {
url = self.alternateBaseURL;
} else {
url = sessionObject.baseURL ?: [NSURL URLWithString:URLProdPostpayBase];
}
// Appends the context root.
if (self.contextRoot) {
url = [url URLByAppendingPathComponent:self.contextRoot];
} else if (sessionObject.contextRoot) {
url = [url URLByAppendingPathComponent:[MVMCoreSessionObject sharedGlobal].contextRoot];
}
// Appends the page type
if (self.pageType) {
url = [url URLByAppendingPathComponent:self.pageType];
}
_URL = url;
return url;
}
- (void)addRequestParameters:(nonnull NSDictionary *)parameters {
if ([parameters count] > 0) {
@ -181,7 +126,6 @@
- (id)copyWithZone:(nullable NSZone *)zone {
MVMCoreRequestParameters *copyObject = [[MVMCoreRequestParameters alloc] init];
copyObject.identifier = [self.identifier copy];
copyObject.pageType = [self.pageType copy];
copyObject.parentPageType = self.parentPageType;
copyObject.optionalModules = [self.optionalModules copy];
@ -190,7 +134,6 @@
copyObject.contextRoot = [self.contextRoot copy];
copyObject.loadStyle = self.loadStyle;
copyObject.replaceViewIfOnStackElseLoadWithStyle = self.replaceViewIfOnStackElseLoadWithStyle;
copyObject.replaceViewControllerIfOnStackGoToOnly = self.replaceViewControllerIfOnStackGoToOnly;
copyObject.noViewControllerToLoad = self.noViewControllerToLoad;
copyObject.dontDisplayViewController = self.dontDisplayViewController;
copyObject.noloadingOverlay = self.noloadingOverlay;
@ -209,8 +152,6 @@
copyObject.customTimeoutTime = self.customTimeoutTime;
copyObject.backgroundRequest = self.backgroundRequest;
copyObject.URL = self.URL;
copyObject.determinedURL = self.determinedURL;
copyObject.actionMap = self.actionMap;
return copyObject;
}

View File

@ -34,6 +34,7 @@ FOUNDATION_EXPORT const unsigned char MVMCoreVersionString[];
#import <MVMCore/MVMCoreGetterUtility.h>
#import <MVMCore/MVMCoreErrorObject.h>
#import <MVMCore/MVMCoreOperation.h>
#import <MVMCore/MVMCoreObject.h>
#import <MVMCore/MVMCoreBlockOperation.h>
#import <MVMCore/Reachability.h>
@ -55,9 +56,17 @@ FOUNDATION_EXPORT const unsigned char MVMCoreVersionString[];
// Presentation Handling
#import <MVMCore/MVMCorePresentationDelegateProtocol.h>
#import <MVMCore/MVMCoreNavigationHandler.h>
#import <MVMCore/MVMCoreNavigationObject.h>
#import <MVMCore/MVMCoreDismissViewControllerOperation.h>
#import <MVMCore/MVMCorePresentAnimationOperation.h>
#import <MVMCore/MVMCorePresentViewControllerOperation.h>
#import <MVMCore/MVMCoreNavigationOperation.h>
#import <MVMCore/MVMCoreViewControllerAnimatedTransitioning.h>
#import <MVMCore/MVMCoreAlertController.h>
// Action Handling
#import <MVMCore/MVMCoreActionHandler.h>
#import <MVMCore/MVMCoreActionDelegateProtocol.h>
#import <MVMCore/MVMCoreActionUtility.h>
@ -67,8 +76,12 @@ FOUNDATION_EXPORT const unsigned char MVMCoreVersionString[];
#import <MVMCore/MVMCoreViewManagerViewControllerProtocol.h>
#import <MVMCore/MVMCoreViewManagerViewControllerProtocolHelper.h>
#import <MVMCore/MVMCoreViewProtocol.h>
#import <MVMCore/MVMCoreLoggingDelegateProtocol.h>
// Other Handlers and Protocols
#import <MVMCore/MVMCoreLoggingHandler.h>
// Singletons
#import <MVMCore/MVMCoreCache.h>
#import <MVMCore/MVMCoreSessionTimeHandler.h>
#import <MVMCore/MVMCoreSessionObject.h>
#import <MVMCore/MVMCoreLoggingHandlerHelper.h>

View File

@ -41,12 +41,4 @@
/// Checks to see if the operation has content to show.
- (BOOL)hasContentToShow:(nonnull MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error;
/// Notifies the delegate we are about to process the load object.
- (void)willProcessLoadObject:(nonnull MVMCoreLoadObject *)loadObject;
#if ENABLE_HARD_CODED_RESPONSE
- (nullable NSDictionary *)getJSONForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters;
- (nonnull NSDictionary *)modifyJSON:(nonnull NSDictionary *)json;
#endif
@end

View File

@ -0,0 +1,31 @@
//
// MVMCoreLoggingDelegateProtocol.h
// MVMCore
//
// Created by Pfeil, Scott Robert on 11/28/17.
// Copyright © 2017 myverizon. All rights reserved.
//
#import <UIKit/UIKit.h>
@class MVMCoreAlertController;
@protocol MVMCoreLoggingDelegateProtocol <NSObject>
@optional
// Can be used to log different actions performed by the core.
- (void)handleDebugMessage:(nullable NSString *)message;
// Can be used to log different actions performed by the core.
- (void)logWithObject:(nullable id)object withName:(nullable NSString *)name withExtraInfo:(nullable NSDictionary *)extra;
// Can be used to choose how to log error objects.
- (void)addErrorToLog:(nonnull MVMCoreErrorObject *)errorObject;
// Log that the load has finished.
- (void)logLoadFinished:(nullable MVMCoreLoadObject *)loadObject loadedViewController:(nullable UIViewController <MVMCoreViewControllerProtocol> *)loadedViewController error:(nullable MVMCoreErrorObject *)error;
// Log alert
- (void)logAlertForAlertController:(nullable MVMCoreAlertController *)alertController;
@end

View File

@ -1,27 +0,0 @@
//
// MVMCoreLoggingDelegateProtocol.swift
// MVMCore
//
// Created by Nandhini Rajendran on 13/10/23.
// Copyright © 2023 myverizon. All rights reserved.
//
public protocol MVMCoreLoggingDelegateProtocol {
/// Can be used to log different actions performed by the core.
func handleDebugMessage(_ message: String?)
/// Can be used to log a message under a particular cagetory.
func handleDebugMessage(_ message: String, category: String?)
/// Can be used to choose how to log error objects.
func addError(toLog errorObject: MVMCoreErrorObject)
/// Log that the load has finished.
func logLoadFinished(_ loadObject: MVMCoreLoadObject?, loadedViewController: MVMCoreViewControllerProtocol?, error: MVMCoreErrorObject?)
}
public extension MVMCoreLoggingDelegateProtocol {
func addError(toLog errorObject: MVMCoreErrorObject) {}
func logLoadFinished(_ loadObject: MVMCoreLoadObject?, loadedViewController: MVMCoreViewControllerProtocol?, error: MVMCoreErrorObject?) {}
}

View File

@ -17,11 +17,6 @@
// Returns if the manage currently contains the page with the page type.
- (BOOL)containsPageWithPageType:(nullable NSString *)pageType;
- (nullable NSArray*)getAccessibilityElements; //AccessibilityElements that are owned by Manager.
/// Attempt to navigate to the controller. Return the controller that we navigated to if successful.
- (void)navigateToViewControllerOfPageType:(nonnull NSString *)pageType controllerType:(_Nullable Class)controllerType completionHandler:(void (^ __nullable)(UIViewController * _Nullable viewController))completionHandler;
@optional
/// Notifies the manager that the controller received new data.

View File

@ -8,7 +8,7 @@
import Foundation
public struct ActionActionsModel: ActionModelProtocol {
@objcMembers open class ActionActionsModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -46,7 +46,7 @@ public struct ActionActionsModel: ActionModelProtocol {
// MARK: - Codec
//--------------------------------------------------
public init(from decoder: Decoder) throws {
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) {
@ -64,19 +64,4 @@ public struct ActionActionsModel: ActionModelProtocol {
try container.encodeIfPresent(extraParameters, forKey: .extraParameters)
try container.encodeIfPresent(analyticsData, forKey: .analyticsData)
}
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return actions.isEqual(to: model.actions)
&& concurrent == model.concurrent
&& extraParameters == model.extraParameters
&& analyticsData == model.analyticsData
}
}
extension ActionActionsModel: CustomDebugStringConvertible {
public var debugDescription: String {
return "\(Self.self) [\(actions)]"
}
}

View File

@ -7,7 +7,7 @@
//
public struct ActionBackModel: ActionModelProtocol {
@objcMembers public class ActionBackModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -25,12 +25,4 @@ public struct ActionBackModel: ActionModelProtocol {
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
// Default
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return model.actionType == actionType
&& model.extraParameters == extraParameters
&& model.analyticsData == analyticsData
}
}

View File

@ -7,7 +7,7 @@
//
public struct ActionCallModel: ActionModelProtocol {
@objcMembers public class ActionCallModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -28,11 +28,4 @@ public struct ActionCallModel: ActionModelProtocol {
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return extraParameters == model.extraParameters
&& analyticsData == model.analyticsData
&& callNumber == model.callNumber
}
}

View File

@ -7,7 +7,7 @@
//
public struct ActionCancelModel: ActionModelProtocol {
@objcMembers public class ActionCancelModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -25,12 +25,4 @@ public struct ActionCancelModel: ActionModelProtocol {
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
// Default
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return model.actionType == actionType
&& model.extraParameters == extraParameters
&& model.analyticsData == analyticsData
}
}

View File

@ -9,24 +9,18 @@
import ContactsUI
public struct ActionContactModel: ActionModelProtocol {
@objcMembers public class ActionContactModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public enum Approach: String, Codable {
case add
case create
case view
}
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: Approach = .create
public var approach: String = KeyCreate
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
@ -34,7 +28,7 @@ public struct ActionContactModel: ActionModelProtocol {
// MARK: - Initializer
//--------------------------------------------------
public init(phoneNumber: String, firstName: String? = nil, lastName: String? = nil, approach: Approach = .create, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
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
@ -42,14 +36,4 @@ public struct ActionContactModel: ActionModelProtocol {
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return extraParameters == model.extraParameters
&& analyticsData == model.analyticsData
&& phoneNumber == model.phoneNumber
&& firstName == model.firstName
&& lastName == model.lastName
&& approach == model.approach
}
}

View File

@ -7,7 +7,7 @@
//
public protocol ActionModelProtocol: ModelProtocol, CustomDebugStringConvertible {
public protocol ActionModelProtocol: ModelProtocol {
var actionType: String { get }
var extraParameters: JSONValueDictionary? { get set }
@ -33,15 +33,4 @@ public extension ActionModelProtocol {
static var categoryName: String {
return "\(ActionModelProtocol.self)"
}
var debugDescription: String {
return actionType
}
func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return model.actionType == actionType
&& model.extraParameters == extraParameters
&& model.analyticsData == analyticsData
}
}

View File

@ -6,8 +6,7 @@
// Copyright © 2020 myverizon. All rights reserved.
//
public struct ActionNoopModel: ActionModelProtocol {
@objcMembers public class ActionNoopModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -25,12 +24,4 @@ public struct ActionNoopModel: ActionModelProtocol {
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
// Default
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return model.actionType == actionType
&& model.extraParameters == extraParameters
&& model.analyticsData == analyticsData
}
}

View File

@ -0,0 +1,41 @@
//
// ActionModel.swift
// MVMCore
//
// Created by Suresh, Kamlesh on 10/3/19.
// Copyright © 2019 Suresh, Kamlesh. All rights reserved.
//
@objcMembers open class ActionOpenPageModel: ActionModelProtocol, ActionOpenPageProtocol, ClientParameterActionProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "openPage"
public var actionType: String = ActionOpenPageModel.identifier
public var pageType: String
public var modules: [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
//--------------------------------------------------
public init(pageType: String, presentationStyle: String? = nil, extraParameters: JSONValueDictionary? = nil, analyticsData: JSONValueDictionary? = nil, tabBarIndex: Int? = nil, background: Bool? = nil) {
self.pageType = pageType
self.presentationStyle = presentationStyle
self.extraParameters = extraParameters
self.analyticsData = analyticsData
self.tabBarIndex = tabBarIndex
self.background = background
}
}

View File

@ -7,7 +7,7 @@
//
public struct ActionOpenSMSModel: ActionModelProtocol {
@objcMembers public class ActionOpenSMSModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -30,13 +30,4 @@ public struct ActionOpenSMSModel: ActionModelProtocol {
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
// Default
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return model.extraParameters == extraParameters
&& model.analyticsData == analyticsData
&& model.phoneNumber == phoneNumber
&& model.message == message
}
}

View File

@ -7,7 +7,7 @@
//
public struct ActionPreviousSubmitModel: ActionModelProtocol {
@objcMembers public class ActionPreviousSubmitModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -25,12 +25,4 @@ public struct ActionPreviousSubmitModel: ActionModelProtocol {
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
// Default
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return model.actionType == actionType
&& model.extraParameters == extraParameters
&& model.analyticsData == analyticsData
}
}

View File

@ -6,17 +6,14 @@
// Copyright © 2020 myverizon. All rights reserved.
//
import Foundation
public struct ActionRestartModel: ActionModelProtocol {
@objcMembers public class ActionRestartModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "restart"
public var actionType: String = ActionRestartModel.identifier
public var requestURL: URL?
@DecodableDefault.True public var hardReset: Bool
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
@ -27,20 +24,9 @@ public struct ActionRestartModel: ActionModelProtocol {
// MARK: - Initializer
//--------------------------------------------------
public init(_ pageType: String? = nil, _ requestUrl: URL? = nil, hardReset: Bool? = nil, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
public init(_ pageType: String? = nil, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.pageType = pageType
self.requestURL = requestUrl
self.extraParameters = extraParameters
self.analyticsData = analyticsData
self.hardReset = hardReset ?? true
}
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return extraParameters == model.extraParameters
&& analyticsData == model.analyticsData
&& requestURL == model.requestURL
&& pageType == model.pageType
&& hardReset == model.hardReset
}
}

View File

@ -29,11 +29,4 @@ public class ActionRunJavaScriptModel: ActionModelProtocol {
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return jsCallback == model.jsCallback
&& extraParameters == model.extraParameters
&& analyticsData == model.analyticsData
}
}

View File

@ -7,7 +7,7 @@
//
public struct ActionSettingModel: ActionModelProtocol {
@objcMembers public class ActionSettingModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -25,10 +25,4 @@ public struct ActionSettingModel: ActionModelProtocol {
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
public func isEqual(to model: any ModelComparisonProtocol) -> Bool {
guard let model = model as? Self else { return false }
return extraParameters == model.extraParameters
&& analyticsData == model.analyticsData
}
}

View File

@ -0,0 +1,33 @@
//
// ActionShareModel.swift
// MVMCore
//
// Created by Kevin Christiano on 2/18/20.
// Copyright © 2020 myverizon. All rights reserved.
//
@objcMembers public class ActionShareModel: ActionModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "share"
public var actionType: String = ActionShareModel.identifier
public var sharedType: String
public var sharedText: String
public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(sharedText: String, sharedType: String, _ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.sharedType = sharedType
self.sharedText = sharedText
self.extraParameters = extraParameters
self.analyticsData = analyticsData
}
}

View File

@ -6,153 +6,112 @@
// Copyright © 2021 myverizon. All rights reserved.
//
/// 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
@objcMembers open class ClientParameterHandler: NSObject {
@objc open class ClientParameterHandler: NSObject {
var parameterHandlerList: [ClientParameterProtocol] = []
let parametersWorkQueue = DispatchQueue(label: "com.mva.clientparameter")
let group = DispatchGroup()
public static let DefaultTimeout = 30.0
open func createParametersHandler(_ clientParameterModel: ClientParameterModelProtocol) -> (any ClientParameterProtocol)? {
open func createParametersHandler(_ clientParameterModel: ClientParameterModelProtocol) -> ClientParameterProtocol? {
do {
//Ensure the handlerType return will be initable using ClientParameterHandler
guard let handlerType = try ModelRegistry.getHandler(clientParameterModel) as? AnyClientParameterProtocol.Type else { throw ClientParameterError.castingError }
//init the handler
let handler = try handlerType.init(clientParameterModel: clientParameterModel)
//Cast from AnyClientParameterProtocol to AnyClientParameterProtocol, if not throw
guard let castedHandler = handler as? any ClientParameterProtocol else { throw ClientParameterError.castingError }
//Return the any ClientParameterProtocol
return castedHandler
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)
MVMCoreLoggingHandler.addError(toLog: errorObject)
}
return nil
}
}
open func getClientParameterModel(_ clientParameters: [String: Any]) throws -> ClientParameterModel? {
func getClientParameterModel(_ clientParameters: [String: Any]) throws -> ClientParameterModel? {
let data = try JSONSerialization.data(withJSONObject: clientParameters)
return try JSONDecoder.create().decode(ClientParameterModel.self, from: data)
}
@objc
open func getParameters(with clientParameters: [String: Any], requestParameters: [String: Any], actionId: String, completionHandler:@escaping ([String: Any]?) -> ()) throws {
/// 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
}
getParameters(with: clientParameterModel, requestParameters: requestParameters, actionId: actionId, completionHandler: completionHandler)
}
open func getParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String) async -> [String: AnyHashable] {
await withCheckedContinuation { continuation in
getParameters(with: model, requestParameters: requestParameters, actionId: actionId) { parameters in
continuation.resume(returning: parameters)
}
}
}
private func getParameters(with model: ClientParameterModel, requestParameters: [String: Any], actionId: String, completionHandler:@escaping ([String: AnyHashable]) -> ()) {
let parametersWorkQueue = DispatchQueue(label: "com.mva.clientparameter")
let group = DispatchGroup()
let timeout = model.timeout ?? Self.DefaultTimeout
let parameterHandlerList = model.list.compactMap { createParametersHandler($0) }
let requestUUID = (0..<parameterHandlerList.count).map { _ in UUID().uuidString }
var returnedList = [[String: AnyHashable]?](repeating: nil, count: parameterHandlerList.count)
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) {
var mergedParametersList: [String: AnyHashable] {
return returnedList.enumerated().map { (index, element) -> [String: AnyHashable] in
guard let parameter = element else {
let handler = parameterHandlerList[index]
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterTimeout(
name: handler.clientParameterModel.type,
uuid: requestUUID[index],
actionId: actionId))
return handler.valueOnTimeout()
}
return parameter
}.reduce(into: [String: AnyHashable]()) { partialResult, next in
partialResult.merge(next) { first, last in first }
// 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) {
let params = mergedParametersList
completionHandler(params);
completionHandler(mergedParametersList);
complete = true
}
let completionWorkItem = DispatchWorkItem(qos: .userInitiated) {
timeoutWorkItem.cancel()
if !complete { // In the case of firing after timeout.
let params = mergedParametersList
completionHandler(params);
completionHandler(mergedParametersList);
complete = true
}
}
// Setup timeout.
parametersWorkQueue.asyncAfter(deadline: .now() + .seconds(Int(timeout)), execute: timeoutWorkItem)
self.parametersWorkQueue.asyncAfter(deadline: .now() + .seconds(Int(timeout)), execute: timeoutWorkItem)
// Setup the parameter execution.
for (index, parameterHandler) in parameterHandlerList.enumerated() {
for (index, parameterHandler) in self.parameterHandlerList.enumerated() {
let parameterType = parameterHandler.clientParameterModel.type
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterStartFetch(name: parameterType, uuid: requestUUID[index], actionId: actionId))
group.enter()
parameterHandler.fetchClientParametersBridge(requestParameters: requestParameters,
self.group.enter()
parameterHandler.fetchClientParameters(requestParameters: requestParameters,
timingOutIn: timeout) { (receivedParameter) in
// Queue the results for merge.
parametersWorkQueue.async {
guard !complete else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "Client \(parameterType) responded after task completion.")
return
}
guard returnedList[index] == nil else {
self.parametersWorkQueue.async {
if (returnedList[index] != nil) {
MVMCoreLoggingHandler.shared()?.addError(toLog: MVMCoreErrorObject(title: nil, message: "Client parameter \(parameterType) has already executed. The completion handler should only be called once!", code: ErrorCode.default.rawValue, domain: ErrorDomainNative, location: String(describing: ClientParameterHandler.self))!)
return
} else {
returnedList[index] = receivedParameter
self.group.leave() // Leaving is only done after setup (barriered).
}
MVMCoreLoggingHandler.shared()?.logCoreEvent(.clientParameterFetchComplete(name: parameterType, uuid: requestUUID[index], actionId: actionId))
returnedList[index] = receivedParameter
group.leave() // Leaving is only done after setup (barriered).
}
}
}
// Callback when all parameters have been merged.
group.notify(queue: parametersWorkQueue, work: completionWorkItem);
}
}
}
fileprivate extension ClientParameterProtocol {
func fetchClientParametersBridge(requestParameters: [String: Any], timingOutIn timeout: TimeInterval, completion: @escaping ([String: AnyHashable]?) -> ()) {
Task {
let parameters = await fetchClientParameters(requestParameters: requestParameters, timingOutIn: timeout)
completion(parameters)
self.group.notify(queue: self.parametersWorkQueue, work: completionWorkItem);
}
}
}

View File

@ -8,8 +8,7 @@
import Foundation
public class ClientParameterModel: Equatable, Codable {
public class ClientParameterModel: Codable {
var timeout: Double?
var list: [ClientParameterModelProtocol]
@ -34,9 +33,4 @@ public class ClientParameterModel: Equatable, Codable {
try container.encodeIfPresent(timeout, forKey: .timeout)
try container.encodeModels(list, forKey: .list)
}
public static func == (lhs: ClientParameterModel, rhs: ClientParameterModel) -> Bool {
return lhs.list.isEqual(to: rhs.list)
&& lhs.timeout == rhs.timeout
}
}

View File

@ -8,13 +8,8 @@
import Foundation
/// A protocol defining models for client parameter fetching handlers.
public protocol ClientParameterModelProtocol: ModelProtocol {
/// The type of client parameter.
var type: String { get }
/// A unique identifier for this instance.
var id: String { get }
}
public extension ClientParameterModelProtocol {
@ -30,9 +25,4 @@ public extension ClientParameterModelProtocol {
static var categoryName: String {
return "\(ClientParameterModelProtocol.self)"
}
func isEqual(to model: any ModelProtocol) -> Bool {
guard let model = model as? Self else { return false }
return type == model.type
}
}

View File

@ -8,50 +8,34 @@
import Foundation
/// Errors used for ClientParamter
public enum ClientParameterError: Error {
case castingError
case modelMismatch(message: String)
}
public protocol ClientParameterProtocol: ModelHandlerProtocol {
static var name: String { get }
public protocol AnyClientParameterProtocol {
/// Original init using a ClientParameterModelProtocol to get around the self - associatedType issues with ClientParameterProtocol
/// - Parameter clientParameterModel: any Model using the ClientParameterModelProtocol
init(clientParameterModel: ClientParameterModelProtocol) throws
}
/// A protocol defining client parameter fetching handlers.
public protocol ClientParameterProtocol<Model>: ModelHandlerProtocol, AnyClientParameterProtocol {
init(_ clientParameterModel: ClientParameterModelProtocol)
associatedtype Model: ClientParameterModelProtocol
var clientParameterModel: Model { get set }
init(clientParameterModel: Model)
var clientParameterModel: ClientParameterModelProtocol { get set }
func fetchClientParameters(requestParameters: [String: Any], timingOutIn timeout: TimeInterval) async -> [String: AnyHashable]
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 {
/// Helper init to convert protocol to specific model. Default Implementation should never be overridden
init(clientParameterModel: ClientParameterModelProtocol) throws {
// Cast the clientParameterModel to ensure it matches the asscociatedType Model assigned to this Handler.
guard let castedModel = clientParameterModel as? Model else {
let message = "Expected \(Model.self) but received \(clientParameterModel.type)"
throw ClientParameterError.modelMismatch(message: message)
}
// Call init for the Handler Class and pass in the casted Model.
self.init(clientParameterModel: castedModel)
func valueOnTimeout() -> [String: AnyHashable] {
return [Self.name: "failed_to_collect"]
}
func valueOnTimeout() -> [String: AnyHashable] {
return [clientParameterModel.type: "failed_to_collect"]
}
/// Convenience function to format the return parameters consistently.
func formatReturnParameters(_ parameter: AnyHashable) -> [String: AnyHashable] {
return [clientParameterModel.type: parameter]
/// 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

@ -43,9 +43,9 @@ extension Encodable {
return jsonArray
}
public func toJSONString(options: JSONSerialization.WritingOptions = .prettyPrinted) -> String? {
public func toJSONString() -> String? {
guard let json = self.toJSON(),
let data = try? JSONSerialization.data(withJSONObject: json, options: options),
let data = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted),
let string = String(data: data, encoding: .utf8) else {
return nil
}

View File

@ -18,31 +18,6 @@ public enum JSONError: Error {
case error(message: String)
}
extension JSONError: LocalizedError, CustomStringConvertible {
public var description: String {
switch self {
case .pathNotFound:
return "JSON path not found"
case .data(path: let path):
return "JSON data in \(path) is corrupt"
case .other(error: let error):
if let decodingError = error as? DecodingError {
// the only way to get the decoding error description and details...
return (decodingError as NSError).description
}
return error.localizedDescription
case .error(message: let message):
return message
}
}
// Shown to customers.
public var errorDescription: String? {
return MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess)
}
}
extension JSONDictionary {
public func toJSONString(options: JSONSerialization.WritingOptions = []) throws -> String {
@ -53,7 +28,7 @@ extension JSONDictionary {
return string
}
public func toUrlQueryItems(shouldRemoveDefaultEncoding: Bool = false) throws -> [URLQueryItem] {
public func toUrlQueryItems() throws -> [URLQueryItem] {
var queryItems: [URLQueryItem] = []
for (key, value) in self {
var valueString: String
@ -62,11 +37,10 @@ extension JSONDictionary {
} else if let value = value as? JSONArray {
valueString = try value.toJSONString()
} else {
valueString = String(describing: value.base)
if shouldRemoveDefaultEncoding {
guard let encodedValue = valueString.removingPercentEncoding else { throw JSONError.error(message:"query item failed: \(key) value \(value.base)") }
valueString = encodedValue
guard let baseValueString = String(describing: value.base).removingPercentEncoding else {
throw JSONError.error(message: "query item failed: \(key) value \(value.base)")
}
valueString = baseValueString
}
let queryItem = URLQueryItem(name: key, value: valueString)
queryItems.append(queryItem)

View File

@ -8,7 +8,7 @@
import Foundation
public protocol ModelProtocol: ModelComparisonProtocol, Codable {
public protocol ModelProtocol: Codable {
/// The key name of the molecule
static var identifier: String { get }
@ -50,102 +50,3 @@ extension ModelProtocol {
try unkeyedContainer.encode(self)
}
}
public protocol ModelComparisonProtocol {
/// Shallow checks if the current model is equal to another model. Defaults to false unless implemented otherwise.
func isEqual(to model: ModelComparisonProtocol) -> Bool
}
extension ModelComparisonProtocol {
public func isEqual(to model: ModelComparisonProtocol) -> Bool {
return false
}
}
public extension Optional {
/// Checks if the current model is equal to another model.
func isEqual(to model: ModelComparisonProtocol?) -> Bool {
guard let self = self as? ModelComparisonProtocol else {
return model == nil
}
guard let model = model else {
return false
}
return self.isEqual(to: model)
}
}
public extension Collection {
/// Checks if all the models in the given collection match another given collection.
func isEqual(to models: [ModelComparisonProtocol]) -> Bool {
guard count == models.count, let self = self as? [ModelComparisonProtocol] else { return false }
return models.indices.allSatisfy { index in
self[index].isEqual(to: models[index])
}
}
}
public extension Optional where Wrapped: Collection {
/// Checks if the current model is equal to another model.
func isEqual(to models: [ModelComparisonProtocol]?) -> Bool {
guard let self = self as? [ModelComparisonProtocol] else {
return models == nil
}
guard let models = models else {
return false
}
return self.isEqual(to: models)
}
}
public extension Optional {
/// Checks if
func matchExistence(with anotherOptional: Optional) -> Bool {
switch(self) {
case .none:
return anotherOptional == nil
case .some(_):
return anotherOptional != nil
}
}
}
func == (lhs: (any ModelComparisonProtocol)?, rhs: (any ModelComparisonProtocol)?) -> Bool {
switch (lhs, rhs) {
case (.some(let lhs), .some(let rhs)):
return lhs.isEqual(to: rhs)
case (.none, .none):
return true
default:
return false
}
}
func != (lhs: (any ModelComparisonProtocol)?, rhs: (any ModelComparisonProtocol)?) -> Bool {
return !(lhs == rhs)
}
func == (lhs: [any ModelComparisonProtocol]?, rhs: [any ModelComparisonProtocol]?) -> Bool {
switch (lhs, rhs) {
case (.some(let lhs), .some(let rhs)):
return lhs == rhs
case (.none, .none):
return true
default:
return false
}
}
func != (lhs: [any ModelComparisonProtocol]?, rhs: [any ModelComparisonProtocol]?) -> Bool {
return !(lhs == rhs)
}
func == (lhs: [any ModelComparisonProtocol], rhs: [any ModelComparisonProtocol]) -> Bool {
return lhs.elementsEqual(rhs, by: { (lhsElement, rhsElement) -> Bool in
return lhsElement == rhsElement
})
}
func != (lhs: [any ModelComparisonProtocol], rhs: [any ModelComparisonProtocol]) -> Bool {
return !(lhs == rhs)
}

View File

@ -50,9 +50,9 @@ public struct ModelRegistry {
/// 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, allowsReplace: Bool = false) {
public static func register<M: ModelProtocol>(_ type: M.Type) {
do {
try throwable_register(type: type, allowsReplace: allowsReplace)
try throwable_register(type: type)
} catch {
handleError(error)
}
@ -103,7 +103,7 @@ public struct ModelRegistry {
errorObject.title = "Unknown Model Registry Error"
}
MVMCoreLoggingHandler.shared()?.addError(toLog: errorObject)
MVMCoreLoggingHandler.addError(toLog: errorObject)
#if DEBUG
triggerCrashInDebug()
#endif
@ -211,7 +211,7 @@ public struct ModelRegistry {
public static func getCodingKey<T>(for type: T.Type) throws -> AnyCodingKey {
guard let category = getCategory(for: type) else {
throw ModelRegistry.Error.decoderOther(message: "Category hasnt been registered for the CodingKey for this type: \(String(describing: type.self))")
throw ModelRegistry.Error.decoderOther(message: "decodeModelsIfPresent only works for objects implementing the ModelProtocol protocol")
}
return AnyCodingKey(category.codingKey)

View File

@ -12,18 +12,18 @@
open class func registerActions() {
ModelRegistry.register(ActionRunJavaScriptModel.self)
ModelRegistry.register(handler: ActionOpenPageHandler.self, for: ActionOpenPageModel.self)
ModelRegistry.register(ActionOpenPageModel.self)
ModelRegistry.register(handler: ActionOpenUrlHandler.self, for: ActionOpenUrlModel.self)
ModelRegistry.register(handler: ActionCallHandler.self, for: ActionCallModel.self)
ModelRegistry.register(handler: ActionBackHandler.self, for: ActionBackModel.self)
ModelRegistry.register(handler: ActionShareHandler.self, for: ActionShareModel.self)
ModelRegistry.register(handler: ActionRestartHandler.self, for: ActionRestartModel.self)
ModelRegistry.register(handler: ActionPreviousSubmitHandler.self, for: ActionPreviousSubmitModel.self)
ModelRegistry.register(handler: ActionCancelHandler.self, for: ActionCancelModel.self)
ModelRegistry.register(handler: ActionSettingHandler.self, for: ActionSettingModel.self)
ModelRegistry.register(handler: ActionNoopHandler.self, for: ActionNoopModel.self)
ModelRegistry.register(handler: ActionActionsHandler.self, for: ActionActionsModel.self)
ModelRegistry.register(handler: ActionOpenSMSHandler.self, for: ActionOpenSMSModel.self)
ModelRegistry.register(handler: ActionContactHandler.self, for: ActionContactModel.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

@ -1,106 +0,0 @@
//
// Cache.swift
// JSONCreator
//
// Created by Matt Bruce on 3/20/24.
// Copyright © 2024 Verizon Wireless. All rights reserved.
//
import Foundation
public enum CacheError: Error {
case serializationFailed
case deserializationFailed
case dataNotFound
case dataExpired
case saveFailed(Error)
case loadFailed(Error)
}
public class CachedData: Codable {
public var data: [String: AnyHashable]
public var expirationDate: Date
enum CodingKeys: CodingKey {
case data, expirationDate
}
public init(data: [String: AnyHashable], expirationDate: Date) {
self.data = data
self.expirationDate = expirationDate
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(expirationDate, forKey: .expirationDate)
let dataAsData = try JSONSerialization.data(withJSONObject: data, options: [])
try container.encode(dataAsData, forKey: .data)
}
required public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
expirationDate = try values.decode(Date.self, forKey: .expirationDate)
let dataAsData = try values.decode(Data.self, forKey: .data)
guard let jsonData = try JSONSerialization.jsonObject(with: dataAsData, options: []) as? [String: AnyHashable] else {
throw CacheError.deserializationFailed
}
data = jsonData
}
}
@objc public class PersistentCacheManager: NSObject {
@objc public static let shared = PersistentCacheManager()
private let fileManager = FileManager.default
@objc public lazy var cacheDirectory = { fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("Atomic")}()
private override init() {}
@objc public func save(data: [String: AnyHashable], forKey key: String, path: URL, expirationDate: Date) throws {
let cachedData = CachedData(data: data, expirationDate: expirationDate)
do {
try FileManager.default.createDirectory(atPath: path.deletingLastPathComponent().relativePath, withIntermediateDirectories: true, attributes: [.protectionKey: FileProtectionType.complete])
let dataToSave = try JSONEncoder().encode(cachedData)
try dataToSave.write(to: path, options: [.atomic, .completeFileProtection])
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "CACHEDFEED: SAVED TO PERSISTENT CACHE, key:\(key), path:\(path)")
} catch is EncodingError {
throw CacheError.serializationFailed
} catch {
throw CacheError.saveFailed(error)
}
}
@objc public func load(forKey key: String, path: URL) throws -> [String: AnyHashable] {
do {
let data = try Data(contentsOf: path)
let decodedCachedData = try JSONDecoder().decode(CachedData.self, from: data)
if Date() < decodedCachedData.expirationDate {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "CACHEDFEED: LOADED FROM PERSISTENT CACHE, key:\(key), path:\(path)")
return decodedCachedData.data
} else {
MVMCoreLoggingHandler.logDebugMessage(withDelegate: "CACHEDFEED: EXPIRED, key:\(key), path:\(path)")
throw CacheError.dataExpired
}
} catch {
// Remove item from the cache on any failure.
try fileManager.removeItem(at: path)
switch error {
case is DecodingError:
throw CacheError.deserializationFailed
default:
throw CacheError.loadFailed(error)
}
}
}
@objc public func remove(forKey key: String) throws {
let filePath = self.filePath(forKey: key)
try fileManager.removeItem(at: filePath)
}
@objc public func removeAll() throws {
try FileManager.default.removeItem(at: cacheDirectory)
}
private func filePath(forKey key: String) -> URL {
return cacheDirectory.appendingPathComponent("\(key).json")
}
}

View File

@ -12,9 +12,6 @@
#import <AVKit/AVKit.h>
@class MVMCoreErrorObject;
extern NSString * _Nonnull const KeyCachePolicy;
extern NSString * _Nonnull const KeyCacheExpiry;
//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.
typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOOL);
@ -34,28 +31,10 @@ typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOO
#pragma mark - Page and Module Handling
// Checks the set of pageTypes to be cached for the given pageType.
- (BOOL)shouldCachePageJSON:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType;
- (BOOL)shouldCacheJSONWithPageType:(nonnull NSString *)pageType;
// Checks the set of modules to be cached for the given module
- (BOOL)shouldCacheModuleJSON:(nonnull NSDictionary *)jsonDictionary moduleName:(nonnull NSString *)moduleName;
/// Returns if the json is expired or not.
- (BOOL)isJSONExpired:(nonnull NSDictionary *)jsonDictionary;
/// Returns the expiry time for the object.
- (nonnull NSDate *)getExpirationDateForJSON:(nonnull NSDictionary *)jsonDictionary;
/// Checks if the page is to be persistently cached.
- (BOOL)shouldPersistentlyCachePage:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType;
/// Checks if the module is to be persistently cached.
- (BOOL)shouldPersistentlyCacheModule:(nonnull NSDictionary *)jsonDictionary module:(nonnull NSString *)module;
/// Can override the path for the page to be cached. Currently Cache/Atomic/Pages/pageType
- (nullable NSURL *)getPathForPersistentCachePage:(nonnull NSString *)pageType;
/// Can override the path for the page to be cached. Currently Cache/Atomic/Modules/moduleName
- (nullable NSURL *)getPathForPersistentCacheModule:(nonnull NSString *)moduleName;
- (BOOL)shouldCacheJSONWithModule:(nonnull NSString *)module;
// For pages external to the mobile first framework to be added to the list to not cache
- (void)addPageTypesToNotCache:(nullable NSArray <NSString *>*)array;
@ -74,12 +53,6 @@ typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOO
// Gets a json dictionary from the cache with all the requested modules. Pass in the block that you want to run once the dictionary is received. This will be run on a background thread.
- (void)fetchJSONForModules:(nullable NSArray *)modules completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler;
/// Returns a page JSON from the persistent cache.
- (NSDictionary * _Nullable)fetchPageFromPersistentCache:(nonnull NSString *)pageType;
/// Returns a module JSON from the persistent cache.
- (NSDictionary * _Nullable)fetchModuleFromPersistentCache:(nonnull NSString *)moduleName;
#pragma mark - Advanced Fetch
// Pass in the block that you want to run once the dictionary is received and which queue to run it on. Pass in if you'd like the current thread to wait.
@ -102,12 +75,6 @@ typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOO
// Adds a json dictionary to the cache by modules.
- (void)addModulesToCache:(nonnull NSDictionary *)jsonDictionary;
/// Adds the json to the persistent cache by pageType.
- (void)addPageToPersistentCache:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType expirationDate:(nonnull NSDate *)expirationDate;
/// Adds the json to the persistent cache by module.
- (void)addModuleToPersistentCache:(nonnull NSDictionary *)jsonDictionary moduleName:(nonnull NSString *)moduleName expirationDate:(nonnull NSDate *)expirationDate;
#pragma mark - Advanced Insertion
// Adds a json dictionary to the cache by pageType. Pass in the block that you want to run once the dictionary is received and which queue to run it on. Pass in if you'd like the current thread to wait.
@ -144,15 +111,6 @@ typedef void(^MVMCoreGetImageBlock)(UIImage * _Nullable, NSData * _Nullable, BOO
// Removes the json for modules. Pass in the block that you want to run once the dictionary is received and which queue to run it on.
- (void)removeJSONForModules:(nonnull NSArray *)modules queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock;
/// Clears the persistent JSON cache
- (void)clearPersistentJSONCache;
// Removes a json dictionary from the cache by pageType.
- (void)removePersistentJSONCacheForPageType:(nonnull NSString *)pageType pageJSON:(nonnull NSDictionary *)jsonDictionary;
// Removes a json dictionary from the cache by moduleType.
- (void)removePersistentModuleCacheForModule:(nonnull NSString *)moduleType moduleJSON:(nonnull NSDictionary *)jsonDictionary;
#pragma mark Image Functions
/// Register a bundle as one to search for images in.

View File

@ -9,16 +9,14 @@
#import "MVMCoreCache.h"
#import <MVMCore/NSDictionary+MFConvenience.h>
#import "MVMCoreErrorObject.h"
#import <MVMCore/MVMCore-Swift.h>
#import "MVMCoreLoggingHandler.h"
#import "MVMCoreGetterUtility.h"
#import "MVMCoreObject.h"
#import "MVMCoreConstants.h"
#import "MVMCoreActionUtility.h"
#import "MVMCoreLoggingHandler.h"
#import "MVMCoreDispatchUtility.h"
#import "MVMCoreErrorConstants.h"
#import "MVMCoreLoggingHandlerHelper.h"
NSString * _Nonnull const KeyCachePolicy = @"cachePolicy";
NSString * _Nonnull const KeyCacheExpiry = @"expiry";
@interface MVMCoreCache ()
@ -94,55 +92,12 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
return _modulesToNotCache;
}
- (BOOL)shouldCachePageJSON:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType {
if ([self.pageTypesToNotCache containsObject:pageType]) { return NO; }
NSNumber *shouldCache = [jsonDictionary optionalNumberForKey:@"cache"];
return shouldCache == nil || shouldCache.boolValue;
- (BOOL)shouldCacheJSONWithPageType:(nonnull NSString *)pageType {
return ![self.pageTypesToNotCache containsObject:pageType];
}
- (BOOL)shouldCacheModuleJSON:(nonnull NSDictionary *)jsonDictionary moduleName:(nonnull NSString *)moduleName {
if ([self.modulesToNotCache containsObject:moduleName]) { return NO; }
NSNumber *shouldCache = [jsonDictionary optionalNumberForKey:@"cache"];
return shouldCache == nil || shouldCache.boolValue;
}
- (BOOL)isJSONExpired:(nonnull NSDictionary *)jsonDictionary {
NSDate *expirationDate = [self getExpirationDateForJSON:jsonDictionary];
NSDate *now = [NSDate date];
if ([now compare:expirationDate] == NSOrderedDescending) {
[MVMCoreLoggingHandler logDebugMessageWithDelegate:[NSString stringWithFormat:@"CACHEDFEED: NEW DATA ALREADY EXPIRED %@ now:%@ expirationDate:%@",jsonDictionary,now,expirationDate]];
}
return [now compare:expirationDate] == NSOrderedDescending;
}
- (nonnull NSDate *)getExpirationDateForJSON:(nonnull NSDictionary *)jsonDictionary {
NSDictionary *cachePolicy = [jsonDictionary dict:KeyCachePolicy];
NSTimeInterval interval = [[cachePolicy string:KeyCacheExpiry] doubleValue] / 1000;
return [NSDate dateWithTimeIntervalSince1970:interval];
}
- (BOOL)shouldPersistentlyCacheJSON:(nonnull NSDictionary *)jsonDictionary {
NSDictionary *cachePolicy = [jsonDictionary dict:KeyCachePolicy];
if (!cachePolicy || ![cachePolicy boolForKey:@"persist"]) {
return NO;
}
return ![self isJSONExpired:jsonDictionary];
}
- (BOOL)shouldPersistentlyCachePage:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType {
return [self shouldPersistentlyCacheJSON:jsonDictionary];
}
- (BOOL)shouldPersistentlyCacheModule:(nonnull NSDictionary *)jsonDictionary module:(nonnull NSString *)module {
return [self shouldPersistentlyCacheJSON:jsonDictionary];
}
- (nullable NSURL *)getPathForPersistentCachePage:(nonnull NSString *)pageType {
return [[[[PersistentCacheManager shared].cacheDirectory URLByAppendingPathComponent:@"Pages"] URLByAppendingPathComponent:pageType] URLByAppendingPathExtension:@"json"];
}
- (nullable NSURL *)getPathForPersistentCacheModule:(nonnull NSString *)moduleName {
return [[[[PersistentCacheManager shared].cacheDirectory URLByAppendingPathComponent:@"Modules"] URLByAppendingPathComponent:moduleName]URLByAppendingPathExtension:@"json"];
- (BOOL)shouldCacheJSONWithModule:(nonnull NSString *)module {
return ![self.modulesToNotCache containsObject:module];
}
- (void)addPageTypesToNotCache:(nullable NSArray <NSString *>*)array {
@ -171,24 +126,6 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
[self fetchJSONForModules:modules queue:self.completionQueue waitUntilFinished:NO completionHandler:completionHandler];
}
- (NSDictionary * _Nullable)fetchPageFromPersistentCache:(nonnull NSString *)pageType {
NSError *error = nil;
return [[PersistentCacheManager shared] loadForKey:pageType path:[self getPathForPersistentCachePage:pageType] error:&error];
}
- (NSDictionary * _Nullable)fetchModuleFromPersistentCache:(nonnull NSString *)moduleName {
NSError *error = nil;
return [[PersistentCacheManager shared] loadForKey:moduleName path:[self getPathForPersistentCacheModule:moduleName] error:&error];
}
- (void)removePersistentJSONCacheForPageType:(nonnull NSString *)pageType pageJSON:(nonnull NSDictionary *)jsonDictionary {
[[PersistentCacheManager shared] removeForKey:pageType error:nil];
}
- (void)removePersistentModuleCacheForModule:(nonnull NSString *)moduleType moduleJSON:(nonnull NSDictionary *)jsonDictionary {
[[PersistentCacheManager shared] removeForKey:moduleType error:nil];
}
#pragma mark - Advanced Fetch
- (void)fetchJSONForPageType:(nullable NSString *)pageType queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler {
@ -204,9 +141,6 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
// First checks the cache by page type.
dictionary = [weakSelf.pageTypeCache objectForKey:pageType];
if (!dictionary) {
dictionary = [self fetchPageFromPersistentCache:pageType];
}
} else {
// If no pagetype, return whole cache
@ -243,11 +177,6 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
NSDictionary *moduleDictionary = [weakSelf.moduleCache objectForKey:module];
if (moduleDictionary) {
[modulesDictionary setObject:moduleDictionary forKey:module];
} else {
moduleDictionary = [self fetchModuleFromPersistentCache:module];
if (moduleDictionary) {
[modulesDictionary setObject:moduleDictionary forKey:module];
}
}
}
@ -278,37 +207,16 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
[self addModulesToCache:jsonDictionary queue:nil waitUntilFinished:NO completionBlock:NULL];
}
- (void)addPageToPersistentCache:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType expirationDate:(nonnull NSDate *)expirationDate {
NSError *error = nil;
[[PersistentCacheManager shared] saveWithData:jsonDictionary forKey:pageType path:[self getPathForPersistentCachePage:pageType] expirationDate:expirationDate error:&error];
if (error) {
[[MVMCoreLoggingHandler sharedLoggingHandler] addErrorToLog:[MVMCoreErrorObject createErrorObjectForNSError:error location:[NSString stringWithFormat:@"%s_%@",__PRETTY_FUNCTION__,pageType]]];
}
}
- (void)addModuleToPersistentCache:(nonnull NSDictionary *)jsonDictionary moduleName:(nonnull NSString *)moduleName expirationDate:(nonnull NSDate *)expirationDate {
NSError *error = nil;
[[PersistentCacheManager shared] saveWithData:jsonDictionary forKey:moduleName path:[self getPathForPersistentCacheModule:moduleName] expirationDate:expirationDate error:&error];
if (error) {
[[MVMCoreLoggingHandler sharedLoggingHandler] addErrorToLog:[MVMCoreErrorObject createErrorObjectForNSError:error location:[NSString stringWithFormat:@"%s_%@",__PRETTY_FUNCTION__,moduleName]]];
}
}
#pragma mark - Advanced Insertion
- (void)addPageToCache:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock {
if (![self shouldCachePageJSON:jsonDictionary pageType:pageType]) {
if (completionBlock) {
completionBlock();
}
return;
}
NSBlockOperation *addOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = addOperation;
__weak typeof(self) weakSelf = self;
[addOperation addExecutionBlock:^{
if (!weakOperation.isCancelled) {
if (!weakOperation.isCancelled && [[MVMCoreCache sharedCache] shouldCacheJSONWithPageType:pageType]) {
// There must be a dictionary and page type to cache.
if (jsonDictionary && pageType) {
@ -320,13 +228,10 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
}
// Adds json to cache with page type key.
[weakSelf.pageTypeCache setObject:jsonDictionary forKey:pageType];
if (![self shouldPersistentlyCachePage:jsonDictionary pageType:pageType]) {
[self removePersistentJSONCacheForPageType:pageType pageJSON:jsonDictionary];
return;
NSNumber *shouldCache = [jsonDictionary optionalNumberForKey:@"cache"];
if (shouldCache == nil || shouldCache.boolValue) {
[weakSelf.pageTypeCache setObject:jsonDictionary forKey:pageType];
}
[self addPageToPersistentCache:jsonDictionary pageType:pageType expirationDate:[self getExpirationDateForJSON:jsonDictionary]];
}
}
if (completionBlock) {
@ -337,12 +242,11 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
}
- (void)addModuleToCache:(nonnull NSDictionary *)jsonDictionary module:(nonnull NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock {
if (![self shouldCacheModuleJSON:jsonDictionary moduleName:module]) {
if (completionBlock) {
completionBlock();
}
return;
}
[self addModulesToCache:@{module:jsonDictionary} queue:queue waitUntilFinished:waitUntilFinished completionBlock:completionBlock];
}
- (void)addModulesToCache:(nonnull NSDictionary *)jsonDictionary queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock {
NSBlockOperation *addOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = addOperation;
__weak typeof(self) weakSelf = self;
@ -350,19 +254,23 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
if (!weakOperation.isCancelled) {
if (!weakSelf.moduleCache) {
[jsonDictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
// Create the cache if necessary.
weakSelf.moduleCache = [NSMutableDictionary dictionary];
}
[weakSelf.moduleCache setObject:jsonDictionary forKey:module];
if (![self shouldPersistentlyCacheModule:jsonDictionary module:module]) {
[self removePersistentModuleCacheForModule:module moduleJSON:jsonDictionary];
return;
}
[self addModuleToPersistentCache:jsonDictionary moduleName:module expirationDate:[self getExpirationDateForJSON:jsonDictionary]];
if ([[MVMCoreCache sharedCache] shouldCacheJSONWithModule:key]) {
if (!weakSelf.moduleCache) {
// Create the cache if necessary.
weakSelf.moduleCache = [NSMutableDictionary dictionary];
}
// Adds json to cache with page type key.
NSNumber *shouldCache = [jsonDictionary optionalNumberForKey:@"cache"];
if (shouldCache == nil || shouldCache.boolValue) {
[weakSelf.moduleCache setObject:obj forKey:key];
}
}
}];
}
if (completionBlock) {
[(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:completionBlock]] waitUntilFinished:waitUntilFinished];
@ -371,28 +279,6 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
[self.moduleQueue addOperations:@[addOperation] waitUntilFinished:waitUntilFinished];
}
- (void)addModulesToCache:(nonnull NSDictionary *)jsonDictionary queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock {
[jsonDictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if (![obj isKindOfClass:[NSDictionary class]]) {
MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc]
initWithTitle:nil
messageToLog:[NSString stringWithFormat:@"Invalid module format. Cannot cache %@ as it's not an object", key]
code:ErrorCodeJSONNotDictionary
domain:ErrorDomainSystem
location:NSStringFromClass([self class])];
[MVMCoreLoggingHandler.sharedLoggingHandler addErrorToLog:error];
return;
}
[self addModuleToCache:obj module:key];
}];
__weak typeof(self) weakSelf = self;
[self.moduleQueue addOperations:@[[NSBlockOperation blockOperationWithBlock:^{
if (completionBlock) {
[(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:completionBlock]] waitUntilFinished:waitUntilFinished];
}
}]] waitUntilFinished:waitUntilFinished];
}
#pragma mark - Simple Deletion
- (void)removeJSONForPageType:(nonnull NSString *)pageType {
@ -407,10 +293,6 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
[self removeJSONForModules:modules queue:nil waitUntilFinished:NO completionBlock:NULL];
}
- (void)clearPersistentJSONCache {
[[PersistentCacheManager shared] removeAllAndReturnError:nil];
}
- (void)clearMFCache {
[self.pageTypeQueue cancelAllOperations];
[self.moduleQueue cancelAllOperations];
@ -448,7 +330,6 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
}
}];
[self.pageTypeQueue addOperations:@[removeOperation] waitUntilFinished:waitUntilFinished];
[[PersistentCacheManager shared] removeForKey:pageType error:nil];
}
- (void)removeJSONForModule:(nonnull NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock {
@ -472,7 +353,6 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
}
}];
[self.moduleQueue addOperations:@[removeOperation] waitUntilFinished:waitUntilFinished];
[[PersistentCacheManager shared] removeForKey:module error:nil];
}
- (void)removeJSONForModules:(nonnull NSArray *)modules queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock {
@ -492,7 +372,6 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
// Removes json from cache with module key.
[weakSelf.moduleCache removeObjectForKey:obj];
}
[[PersistentCacheManager shared] removeForKey:obj error:nil];
}
}];
if (completionBlock) {
@ -713,10 +592,6 @@ static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt";
//system stores cache based datatask and check cache based on response,
//need to manually store cache for response
[sharedCache storeCachedResponse:cachedReponse forRequest:request];
} else if (error) {
[[MVMCoreLoggingHandler sharedLoggingHandler]addErrorToLog:[MVMCoreErrorObject createErrorObjectForNSError:error location:@"MVMCoreCache->downloadImage"]];
} else {
[[MVMCoreLoggingHandler sharedLoggingHandler]addErrorToLog:[[MVMCoreErrorObject alloc] initWithTitle:nil messageToLog:[NSString stringWithFormat:@"Failed to download image %@", request.URL.absoluteString] code:0 domain:ErrorDomainSystem location:@"MVMCoreCache->downloadImage"]];
}
if (isGif) {
[self checkImage:nil imageData:data fallbackImage:fallbackImageName completionHandler:completionHandler];

View File

@ -1,187 +0,0 @@
//
// MVMCoreEvent.swift
// MVMCore
//
// https://oneconfluence.verizon.com/display/MFD/NewRelic+Client+Event+Logging
//
// Created by Kyle on 8/29/22.
// Copyright © 2022 myverizon. All rights reserved.
//
import Foundation
/// A list of possible events from the app.
public enum MVMCoreEvent {
// ----------------------------
// MARK: Action Events
// ----------------------------
/// Failed to decode the action payload.
case actionFailedToDecode(
pageType: String,
error: Error?
)
/// Could not find the action specified.
case actionNotFound(
name: String,
pageType: String
)
/// The core action handler was invoked and is in progress.
case actionInvoked(
name: String,
pageType: String,
uuid: String,
model: ActionModelProtocol
)
/// The core action handler failed to complete the action.
case actionFailed(
name: String,
pageType: String,
uuid: String,
model: ActionModelProtocol,
error: Error?
)
/// The core action handler completed an action.
case actionComplete(
name: String,
pageType: String,
uuid: String,
model: ActionModelProtocol
)
// ----------------------------
// MARK: ClientParameter Events
// ----------------------------
/// Could not find the client parameter specified.
case clientParameterNotFound(
name: String,
actionId: String
)
/// The client parameter handler was invoked and is in progress.
case clientParameterStartFetch(
name: String,
uuid: String,
actionId: String
)
/// The client perameter handler timed out and is returning a default value.
case clientParameterTimeout(
name: String,
uuid: String,
actionId: String
)
/// The client paramter fetch completed.
case clientParameterFetchComplete(
name: String,
uuid: String,
actionId: String
)
case pageStarted(
pageType: String,
requestUUID: String
)
case pageLoadStarted(
pageType: String,
requestUUID: String,
requestURL: String
)
case pageLoadComplete(
pageType: String,
requestUUID: String,
serverProcessingTime: String,
requestURL: String,
isFromCache: Bool
)
case pageProcessingComplete(
pageType: String,
requestUUID: String,
webUrl: String?
)
case pageRenderComplete(
pageType: String,
requestUUID: String,
templateName: String?,
controllerName: String,
error: String?
)
public enum EventType: String {
case action
case clientParameter
case pageLoad
public var notification: Notification.Name {
return Notification.Name(rawValue: rawValue)
}
}
public var type: EventType {
switch self {
case .actionFailedToDecode: return .action
case .actionNotFound: return .action
case .actionInvoked: return .action
case .actionFailed: return .action
case .actionComplete: return .action
case .clientParameterNotFound: return .clientParameter
case .clientParameterStartFetch: return .clientParameter
case .clientParameterTimeout: return .clientParameter
case .clientParameterFetchComplete: return .clientParameter
case .pageStarted, .pageLoadStarted, .pageLoadComplete, .pageProcessingComplete, .pageRenderComplete: return .pageLoad
}
}
public var name: String {
let name = String(describing: self)
if let attribIndex = name.firstIndex(of: "(") {
return String(name[..<attribIndex])
}
return name
}
public var notification: Notification.Name {
return Notification.Name(rawValue: type.rawValue)
}
}
public extension MVMCoreLoggingHandler {
func logCoreEvent(_ event: MVMCoreEvent, at timestamp: Int64 = Date.unixMillisecondsNow()) {
recordEvent(event.type.rawValue, attributes: ["timestamp": timestamp, "event": event])
}
}
@objc public extension MVMCoreLoggingHandler {
@objc func pageStarted(for pageType: String, requestUUID: String) {
logCoreEvent(.pageStarted(pageType: pageType, requestUUID: requestUUID))
}
@objc func logPageLoadStarted(for pageType: String, requestUUID: String, requestURL: String) {
logCoreEvent(.pageLoadStarted(pageType: pageType, requestUUID: requestUUID, requestURL: requestURL))
}
@objc func logPageLoadComplete(for pageType: String, serverProcessingTime: String, requestURL: String, requestUUID: String, isFromCache: Bool) {
logCoreEvent(.pageLoadComplete(pageType: pageType, requestUUID: requestUUID, serverProcessingTime: serverProcessingTime, requestURL: requestURL, isFromCache: isFromCache))
}
@objc func logPageProcessingComplete(for pageType: String, requestUUID: String, webUrl: String?) {
logCoreEvent(.pageProcessingComplete(pageType: pageType, requestUUID: requestUUID, webUrl: webUrl))
}
@objc func logPageRenderComplete(for pageType: String, requestUUID: String, templateName: String?, controllerName: String, error: String?) {
logCoreEvent(.pageRenderComplete(pageType: pageType, requestUUID: requestUUID, templateName: templateName, controllerName: controllerName, error: error))
}
}

View File

@ -7,31 +7,9 @@
//
import Foundation
import os
public protocol CoreLogging {
static var loggingCategory: String? { get }
var loggingPrefix: String { get }
}
public extension CoreLogging {
static var loggingCategory: String? { return nil }
var loggingPrefix: String {
return ""
}
static func debugLog(_ string: String) {
#if LOGGING
MVMCoreLoggingHandler.shared()?.handleDebugMessage(string, category: loggingCategory)
#endif
}
func debugLog(_ string: String) {
#if LOGGING
MVMCoreLoggingHandler.shared()?.handleDebugMessage("\(loggingPrefix)\(string)", category: Self.loggingCategory)
#endif
@objc public extension MVMCoreLoggingHandler {
@objc func print(with message: String) {
Swift.print(message)
}
}

View File

@ -0,0 +1,32 @@
//
// MVMCoreLoggingHandler.h
// MVMCore
//
// Created by Pfeil, Scott Robert on 11/28/17.
// Copyright © 2017 myverizon. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <MVMCore/MVMCoreErrorObject.h>
#import <MVMCore/MVMCoreViewControllerProtocol.h>
#import <MVMCore/MVMCoreLoadObject.h>
#import <MVMCore/MVMCoreLoggingDelegateProtocol.h>
@class MVMCoreAlertController;
#define MVMCoreLog(fmt, ...) \
[MVMCoreLoggingHandler logDebugMessageWithDelegate:[NSString stringWithFormat:(@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__]];
@interface MVMCoreLoggingHandler : NSObject <MVMCoreLoggingDelegateProtocol>
+ (nullable instancetype)sharedLoggingHandler;
+ (void)addErrorToLog:(nonnull MVMCoreErrorObject *)errorObject;
+ (void)logDebugMessageWithDelegate:(nullable NSString *)message;
+ (void)logWithDelegateWithObject:(nullable id)object withName:(nullable NSString *)name withExtraInfo:(nullable NSDictionary *)extra;
+ (void)logWithDelegateLoadFinished:(nullable MVMCoreLoadObject *)loadObject loadedViewController:(nullable UIViewController <MVMCoreViewControllerProtocol> *)loadedViewController error:(nullable MVMCoreErrorObject *)error;
+ (void)logAlertForAlertController:(nullable MVMCoreAlertController *)alertController;
#pragma mark MVMCoreLoggingDelegateProtocol
- (void)handleDebugMessage:(nullable NSString *)message;
@end

View File

@ -0,0 +1,62 @@
//
// MVMCoreLoggingHandler.m
// MVMCore
//
// Created by Pfeil, Scott Robert on 11/28/17.
// Copyright © 2017 myverizon. All rights reserved.
//
#import "MVMCoreLoggingHandler.h"
#import "MVMCoreObject.h"
#import "MVMCoreActionUtility.h"
#import <MVMCore/MVMCore-Swift.h>
@implementation MVMCoreLoggingHandler
+ (nullable instancetype)sharedLoggingHandler {
if ([MVMCoreObject sharedInstance].loggingDelegate && [MVMCoreActionUtility classIsInstanceTypeOfClass:[MVMCoreObject sharedInstance].loggingDelegate.class otherClass:self throwException:NO]) {
return (MVMCoreLoggingHandler *)[MVMCoreObject sharedInstance].loggingDelegate;
} else {
return nil;
}
}
+ (void)addErrorToLog:(nonnull MVMCoreErrorObject *)errorObject {
if (errorObject && [[MVMCoreObject sharedInstance].loggingDelegate respondsToSelector:@selector(addErrorToLog:)]) {
[[MVMCoreObject sharedInstance].loggingDelegate addErrorToLog:errorObject];
}
}
+ (void)logDebugMessageWithDelegate:(nullable NSString *)message {
if (message && [[MVMCoreObject sharedInstance].loggingDelegate respondsToSelector:@selector(handleDebugMessage:)]) {
[[MVMCoreObject sharedInstance].loggingDelegate handleDebugMessage:message];
}
}
+ (void)logWithDelegateWithObject:(id)object withName:(nullable NSString *)name withExtraInfo:(nullable NSDictionary *)extra {
if ([[MVMCoreObject sharedInstance].loggingDelegate respondsToSelector:@selector(logWithObject:withName:withExtraInfo:)]) {
[[MVMCoreObject sharedInstance].loggingDelegate logWithObject:object withName:name withExtraInfo:extra];
}
}
+ (void)logWithDelegateLoadFinished:(nullable MVMCoreLoadObject *)loadObject loadedViewController:(nullable UIViewController <MVMCoreViewControllerProtocol> *)loadedViewController error:(nullable MVMCoreErrorObject *)error {
if ([[MVMCoreObject sharedInstance].loggingDelegate respondsToSelector:@selector(logLoadFinished:loadedViewController:error:)]) {
[[MVMCoreObject sharedInstance].loggingDelegate logLoadFinished:loadObject loadedViewController:loadedViewController error:error];
}
}
+ (void)logAlertForAlertController:(nullable MVMCoreAlertController *)alertController {
if ([[MVMCoreObject sharedInstance].loggingDelegate respondsToSelector:@selector(logAlertForAlertController:)]) {
[[MVMCoreObject sharedInstance].loggingDelegate logAlertForAlertController:alertController];
}
}
#pragma mark - logging delegate
- (void)handleDebugMessage:(nullable NSString *)message {
#ifdef DEBUG
[self printWith:message];
#endif
}
@end

View File

@ -1,105 +0,0 @@
//
// MVMCoreLoggingHandler.swift
// MVMCore
//
// Created by Nandhini Rajendran on 22/09/23.
// Copyright © 2023 myverizon. All rights reserved.
//
import Foundation
import os
@objc open class MVMCoreLoggingHandler: NSObject, MVMCoreLoggingDelegateProtocol {
public static let standardCategory = "General"
private let logger = Logger(subsystem: "MVMCoreLogging", category: standardCategory)
private var loggerCache = [String: Logger]()
open func getLogger(category: String?) -> Logger {
if let category {
if let logger = loggerCache[category] {
return logger
} else {
let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: category)
loggerCache[category] = logger
return logger
}
}
return logger
}
@objc(sharedLoggingHandler)
public static func shared() -> Self? {
return MVMCoreActionUtility.initializerClassCheck(MVMCoreObject.sharedInstance()?.loggingDelegate as? NSObject, classToVerify: self) as? Self
}
@objc public class func logDebugMessage(withDelegate message: String) {
MVMCoreObject.sharedInstance()?.loggingDelegate?.handleDebugMessage(message)
}
@objc(logWithDelegateLoadFinished:loadedViewController:error:)
public class func log(withDelegateLoadFinished loadObject: MVMCoreLoadObject?, loadedViewController: UIViewController?, error: MVMCoreErrorObject?) {
MVMCoreObject.sharedInstance()?.loggingDelegate?.logLoadFinished(loadObject, loadedViewController: loadedViewController as? MVMCoreViewControllerProtocol, error: error)
}
open func recordEvent(_ name: String, attributes: [String : Any]?) {}
// MARK: - logging delegate
@objc open func handleDebugMessage(_ message: String?) {
#if LOGGING
guard let message = message else { return }
guard message.count < 1024 else {
logger.debug("\(message.prefix(300), privacy: .public)... <stdio>") // Send initial log to console.
print(message) // Print the whole on stdout.
return
}
logger.debug("\(message, privacy: .public)") // Assume that becaues this is a LOGGING build we want these messages to be unmasked.
// TODO: How do we split the messaging by Library and Subsystem?
#endif
}
@objc open func handleDebugMessage(_ message: String, category: String?) {
#if LOGGING
guard message.count < 1024 else {
getLogger(category: category).debug("\(message.prefix(300), privacy: .public)... <stdio>") // Send initial log to console.
print(message) // Print the whole on stdout.
return
}
getLogger(category: category).debug("\(message, privacy: .public)") // Assume that becaues this is a LOGGING build we want these messages to be unmasked.
#endif
}
open func handleWarningMessage(_ message: String, category: String?) {
#if LOGGING
guard message.count < 1024 else {
getLogger(category: category).warning("\(message.prefix(300), privacy: .public)... <stdio>") // Send initial log to console.
print(message) // Print the whole on stdout.
return
}
getLogger(category: category).warning("\(message, privacy: .public)") // Assume that becaues this is a LOGGING build we want these messages to be unmasked.
#endif
}
open func handleErrorMessage(_ message: String, category: String?) {
#if LOGGING
guard message.count < 1024 else {
getLogger(category: category).error("\(message.prefix(300), privacy: .public)... <stdio>") // Send initial log to console.
print(message) // Print the whole on stdout.
return
}
getLogger(category: category).error("\(message, privacy: .public)") // Assume that becaues this is a LOGGING build we want these messages to be unmasked.
#endif
}
@objc(addErrorToLog:)
open func addError(toLog errorObject: MVMCoreErrorObject) {
if errorObject.silentError {
handleWarningMessage(errorObject.messageToLog ?? errorObject.messageToDisplay ?? "Some error occurred.", category: "Handled Exception")
} else {
handleErrorMessage(errorObject.messageToLog ?? errorObject.messageToDisplay ?? "Some error occurred.", category: "Handled Exception")
}
}
open func logLoadFinished(_ loadObject: MVMCoreLoadObject?, loadedViewController: MVMCoreViewControllerProtocol?, error: MVMCoreErrorObject?) {}
}

View File

@ -1,18 +0,0 @@
//
// MVMCoreLoggingHandlerHelper.h
// MVMCore
//
// Created by Nandhini Rajendran on 18/12/23.
// Copyright © 2023 myverizon. All rights reserved.
//
#ifndef MVMCoreLoggingHandlerHelper_h
#define MVMCoreLoggingHandlerHelper_h
#define MVMCoreLog(fmt, ...) \
[MVMCoreLoggingHandler logDebugMessageWithDelegate:[NSString stringWithFormat:(@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__]];
#define MVMCoreNetworkLog(fmt, ...) \
[MVMCoreLoggingHandler.sharedLoggingHandler handleDebugMessage:[NSString stringWithFormat:(@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__] category: @"Network"];
#endif

View File

@ -0,0 +1,21 @@
//
// MVMCoreDismissViewControllerOperation.h
// myverizon
//
// Created by Scott Pfeil on 9/28/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
// Operation for dismissing an alert view controller. Needs to be added to the main queue
#import <MVMCore/MVMCoreOperation.h>
#import <UIKit/UIKit.h>
@interface MVMCoreDismissViewControllerOperation : MVMCoreOperation
// Goes up the presentation chain to only dismiss the top presented.
- (nullable instancetype)initAndDismissTopViewController:(nullable UIViewController *)viewController animated:(BOOL)animated;
// Dismiss the passed in view controller.
- (nullable instancetype)initAndDismissViewController:(nullable UIViewController *)viewController animated:(BOOL)animated;
@end

View File

@ -0,0 +1,91 @@
//
// MVMCoreDismissViewControllerOperation.m
// myverizon
//
// Created by Scott Pfeil on 9/28/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
#import "MVMCoreDismissViewControllerOperation.h"
#import "MVMCoreConstants.h"
typedef NS_ENUM(NSInteger, DismissType) {
DismissTypeTop = 0,
DismissTypeSpecific
};
@interface MVMCoreDismissViewControllerOperation ()
@property (nullable, strong, nonatomic) UIViewController *viewController;
@property (nonatomic) BOOL animate;
@property (nonatomic) DismissType type;
@end
@implementation MVMCoreDismissViewControllerOperation
- (nullable instancetype)initAndDismissTopViewController:(nullable UIViewController *)viewController animated:(BOOL)animated {
if (self = [super init]) {
self.viewController = viewController;
self.animate = animated;
self.type = DismissTypeTop;
}
return self;
}
- (nullable instancetype)initAndDismissViewController:(nullable UIViewController *)viewController animated:(BOOL)animated {
if (self = [super init]) {
self.viewController = viewController;
self.animate = animated;
self.type = DismissTypeSpecific;
}
return self;
}
- (void)main {
// Always check for cancellation before launching the task.
if ([self checkAndHandleForCancellation]) {
return;
}
switch (self.type) {
case DismissTypeTop:
{
// Gets the top most presented.
UIViewController *controllerToDismiss = self.viewController;
while (controllerToDismiss.presentedViewController) {
controllerToDismiss = controllerToDismiss.presentedViewController;
}
[self dismissViewController:controllerToDismiss];
}
break;
case DismissTypeSpecific:
{
if (self.viewController.presentingViewController) {
[self dismissViewController:self.viewController.presentingViewController];
} else {
[self dismissViewController:self.viewController];
}
}
break;
default:
break;
}
}
- (void)dismissViewController:(nonnull UIViewController *)viewController {
if (viewController.presentedViewController || viewController.presentingViewController) {
[viewController dismissViewControllerAnimated:self.animate completion:^{
[self markAsFinished];
// Notify that page has changed
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:MVMCoreNotificationViewControllerChanged object:nil];
});
}];
} else {
[self markAsFinished];
}
}
@end

View File

@ -0,0 +1,153 @@
//
// MVMCoreNavigationHandler.h
// myverizon
//
// Created by Scott Pfeil on 11/18/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
// This class should be used to handle all navigation and presentation for mvm.
#import <Foundation/Foundation.h>
#import <MVMCore/MVMCorePresentationDelegateProtocol.h>
@class MVMCoreOperation;
@class MVMCoreNavigationObject;
@class MVMCoreLoadObject;
@interface MVMCoreNavigationHandler : NSObject <UINavigationControllerDelegate>
// Returns the shared instance of this singleton
+ (nullable instancetype)sharedNavigationHandler;
// Can set the view controller to present on... will use MF if nil.
@property (nullable, nonatomic, weak) UIViewController *viewControllerToPresentOn;
// reference to main navigation controller
@property (nullable, weak, nonatomic) UINavigationController *navigationController;
/// A list of possible delegates looking for information.
@property (nonnull, strong, nonatomic) NSHashTable<MVMCorePresentationDelegateProtocol> *delegates;
// Will navigate appropriately based on the load style
- (void)navigateWithLoadObject:(nullable MVMCoreLoadObject *)loadObject viewController:(nonnull UIViewController *)viewController delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
// Perform navigation with object.
- (void)startNavigationWithNavigationObject:(nonnull MVMCoreNavigationObject *)navigationObject delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
// pops or dimisses as needed
- (void)removeCurrentViewController;
#pragma mark - Delegate Handling
/// Adds a listener for navigation delegate functions
- (void)addDelegate:(nonnull id <MVMCorePresentationDelegateProtocol>)delegate;
/// Removes a listener for navigation delegate functions
- (void)removeDelegate:(nullable id <MVMCorePresentationDelegateProtocol>)delegate;
#pragma mark - Navigation Simple
// Uses the default navigation controller in app delegate
// Use this to push.
- (void)pushViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated;
// Use this to replace the top view controller.
- (void)replaceTopViewControllerWithViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated;
// Use this to replace all the view controllers after the root with the passed in view controller.
- (void)replaceAfterRoot:(nonnull UIViewController *)viewController animated:(BOOL)animated;
// Use this to set the navigation stack.
- (void)setViewControllers:(nonnull NSArray *)viewControllers animated:(BOOL)animated;
// Use this to pop.
- (void)popTopViewControllerAnimated:(BOOL)animated;
// Use this to pop a specific view controller.
- (void)popViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated;
// Use this to pop to a specific view controller in the stack.
- (void)popToViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated;
// Use this to pop to a view controller of a specific class in the stack.
- (void)popToInstanceOfClass:(nonnull Class)viewControllerClass animated:(BOOL)animated;
// Use this to pop to a view controller of a specific page type in the stack.
- (void)popToViewControllerWithPageType:(nonnull NSString *)pageType animated:(BOOL)animated;
// Use this to pop to the root of the stack.
- (void)popToRootAnimated:(BOOL)animated;
// Adds the navigation operation to the queue.
- (void)addNavigationOperation:(nonnull MVMCoreOperation *)operation;
// Removes all queued up items.
- (void)cancelNavigation;
#pragma mark - Navigation Extra
// Use this to push.
- (void)pushViewController:(nonnull UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate replaceInStack:(BOOL)replaceInStack completionHandler:(nullable void (^)(void))completionBlock;
// Use this to replace the top view controller.
- (void)replaceTopViewControllerWithViewController:(nonnull UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate replaceInStack:(BOOL)replaceInStack completionHandler:(nullable void (^)(void))completionBlock;
// Use this to replace all the view controllers after the root with the passed in view controller. If no navigation controller is provided, will use the main one in app delegate.
- (void)replaceAfterRoot:(nonnull UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate replaceInStack:(BOOL)replaceInStack completionHandler:(nullable void (^)(void))completionBlock;
// Use this to set the navigation stack. If no navigation controller is provided, will use the main one in app delegate.
- (void)setViewControllers:(nonnull NSArray *)viewControllers navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate replaceInStack:(BOOL)replaceInStack completionHandler:(nullable void (^)(void))completionBlock;
// Use this to pop. If no navigation controller is provided, will use the main one in app delegate.
- (void)popTopViewControllerAnimated:(BOOL)animated navigationController:(nullable UINavigationController *)navigationController delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
// Use this to pop a specific view controller. If no navigation controller is provided, will use the main one in app delegate.
- (void)popViewController:(nonnull UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
// Use this to pop to a specific view controller in the stack. If no navigation controller is provided, will use the main one in app delegate.
- (void)popToViewController:(nonnull UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
// Use this to pop to a view controller of a specific class in the stack. If no navigation controller is provided, will use the main one in app delegate.
- (void)popToInstanceOfClass:(nonnull Class)viewControllerClass navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
// Use this to pop to a view controller of a specific pageType in the stack. If no navigation controller is provided, will use the main one in app delegate.
- (void)popToViewControllerWithPageType:(nonnull NSString *)pageType navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
// Use this to pop to the root of the stack. If no navigation controller is provided, will use the main one in app delegate.
- (void)popToRootAnimated:(BOOL)animated navigationController:(nullable UINavigationController *)navigationController delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
/// Returns the view controllers of the navigation controller. Takes into account navigation controller animation states.
- (nullable NSArray<UIViewController *> *)getViewControllersForNavigationController:(nullable UINavigationController *)navigationController;
#pragma mark - Presentation Simple
// Use this to present.
- (void)presentViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated;
// Use this to dismiss.
- (void)dismissTopViewControllerAnimated:(BOOL)animated;
// Use this to dismiss a specific view controller and any it may be presenting.
- (void)dismissViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated;
// Use this to dismiss all presented view controllers stacked on the bottom one.
- (void)dismissToBottom:(BOOL)animated;
// Removes all queued up items.
- (void)cancelPresentation;
#pragma mark - Presentation Extra
// Use this to present.
- (void)presentViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
// Use this to dismiss.
- (void)dismissTopViewControllerAnimated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
// Use this to dismiss a specific view controller and any it may be presenting.
- (void)dismissViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
// Use this to dismiss all presented view controllers stacked on the bottom one.
- (void)dismissToBottom:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock;
@end

View File

@ -0,0 +1,356 @@
//
// MVMCoreNavigationHandler.m
// myverizon
//
// Created by Scott Pfeil on 11/18/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
#import "MVMCoreNavigationHandler.h"
#import "MVMCoreNavigationOperation.h"
#import "MVMCorePresentViewControllerOperation.h"
#import "MVMCoreDismissViewControllerOperation.h"
#import "MVMCoreNavigationObject.h"
#import "MVMCoreLoadObject.h"
#import "MVMCoreObject.h"
#import "MVMCoreRequestParameters.h"
#import "MVMCoreErrorConstants.h"
#import "MVMCoreLoggingHandler.h"
#import "MVMCoreLoadingOverlayHandler.h"
#import "MVMCoreDispatchUtility.h"
#import <MVMCore/MVMCore-Swift.h>
@interface MVMCoreNavigationHandler ()
@property (strong, nonatomic) NSOperationQueue *navigationQueue;
@property (strong, nonatomic) NSOperationQueue *presentationQueue;
@end
@implementation MVMCoreNavigationHandler
+ (nullable instancetype)sharedNavigationHandler {
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
if (self = [super init]) {
self.navigationQueue = [[NSOperationQueue alloc] init];
self.navigationQueue.maxConcurrentOperationCount = 1;
self.presentationQueue = [[NSOperationQueue alloc] init];
self.presentationQueue.maxConcurrentOperationCount = 1;
self.delegates = (NSHashTable <MVMCorePresentationDelegateProtocol>*)[NSHashTable weakObjectsHashTable];
}
return self;
}
#pragma mark - Delegate Handling
- (void)addDelegate:(nonnull id <MVMCorePresentationDelegateProtocol>)delegate {
[self.delegates addObject:delegate];
}
- (void)removeDelegate:(nullable id <MVMCorePresentationDelegateProtocol>)delegate {
[self.delegates removeObject:delegate];
}
#pragma mark - Navigation Helpers
- (void)navigateWithLoadObject:(nullable MVMCoreLoadObject *)loadObject viewController:(nonnull UIViewController *)viewController delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
BOOL shouldAnimate = !loadObject.requestParameters.shouldNotAnimatePush;
switch (loadObject.requestParameters.loadStyle) {
case MFLoadStyleReplaceCurrent:
{
// Replace current screen.
[[MVMCoreNavigationHandler sharedNavigationHandler] replaceTopViewControllerWithViewController:viewController navigationController:loadObject.requestParameters.navigationController animated:shouldAnimate delegate:delegate replaceInStack:loadObject.requestParameters.replaceViewIfOnStackElseLoadWithStyle completionHandler:completionBlock];
}
break;
case MFLoadStyleOnTopOfRoot:
{
// Pushes ater the root.
[[MVMCoreNavigationHandler sharedNavigationHandler] replaceAfterRoot:viewController navigationController:loadObject.requestParameters.navigationController animated:shouldAnimate delegate:delegate replaceInStack:loadObject.requestParameters.replaceViewIfOnStackElseLoadWithStyle completionHandler:completionBlock];
}
break;
case MFLoadStyleBecomeRoot:
{
// Loads as root the root.
NSArray *replaceViewControllers = @[viewController];
[[MVMCoreNavigationHandler sharedNavigationHandler] setViewControllers:replaceViewControllers navigationController:loadObject.requestParameters.navigationController animated:shouldAnimate delegate:delegate replaceInStack:loadObject.requestParameters.replaceViewIfOnStackElseLoadWithStyle completionHandler:completionBlock];
}
break;
case MFLoadStylePresent:
{
// Present as modal.
[[MVMCoreNavigationHandler sharedNavigationHandler] presentViewController:viewController animated:shouldAnimate delegate:delegate completionHandler:completionBlock];
}
break;
default:
{
// Just pushes.
[[MVMCoreNavigationHandler sharedNavigationHandler] pushViewController:viewController navigationController:loadObject.requestParameters.navigationController animated:shouldAnimate delegate:delegate replaceInStack:loadObject.requestParameters.replaceViewIfOnStackElseLoadWithStyle completionHandler:completionBlock];
}
break;
}
}
- (void)startNavigationWithNavigationObject:(MVMCoreNavigationObject *)navigationObject delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
// In case it takes some time to happen.
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] startLoading];
navigationObject.stopLoadingOverlay = YES;
MVMCoreNavigationOperation *navigationOperation = [[MVMCoreNavigationOperation alloc] initWithNavigationObject:navigationObject];
navigationOperation.delegate = delegate;
navigationOperation.completionBlock = completionBlock;
[self.navigationQueue addOperation:navigationOperation];
}
- (void)removeCurrentViewController {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
// presentedViewController must be used on main thread
if (self.viewControllerToPresentOn.presentedViewController) {
[[MVMCoreNavigationHandler sharedNavigationHandler] dismissTopViewControllerAnimated:YES];
} else {
[[MVMCoreNavigationHandler sharedNavigationHandler] popTopViewControllerAnimated:YES];
}
}];
}
#pragma mark - Navigation Simple
- (void)pushViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:viewController navigationController:nil viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePush];
[self startNavigationWithNavigationObject:navigationObject delegate:nil completionHandler:NULL];
}
- (void)replaceTopViewControllerWithViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:viewController navigationController:nil viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypeReplaceTop];
[self startNavigationWithNavigationObject:navigationObject delegate:nil completionHandler:NULL];
}
- (void)replaceAfterRoot:(nonnull UIViewController *)viewController animated:(BOOL)animated {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:viewController navigationController:nil viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypeReplaceAfterRoot];
[self startNavigationWithNavigationObject:navigationObject delegate:nil completionHandler:NULL];
}
- (void)setViewControllers:(nonnull NSArray *)viewControllers animated:(BOOL)animated {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:nil navigationController:nil viewControllers:viewControllers animated:animated tryToReplaceFirst:NO navigationType:NavigationTypeSet];
[self startNavigationWithNavigationObject:navigationObject delegate:nil completionHandler:NULL];
}
- (void)popTopViewControllerAnimated:(BOOL)animated {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:nil navigationController:nil viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePop];
[self startNavigationWithNavigationObject:navigationObject delegate:nil completionHandler:NULL];
}
- (void)popViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:viewController navigationController:nil viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePopSpecific];
[self startNavigationWithNavigationObject:navigationObject delegate:nil completionHandler:NULL];
}
- (void)popToViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:viewController navigationController:nil viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePopTo];
[self startNavigationWithNavigationObject:navigationObject delegate:nil completionHandler:NULL];
}
- (void)popToInstanceOfClass:(nonnull Class)viewControllerClass animated:(BOOL)animated {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:nil navigationController:nil viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePopTo];
navigationObject.classToPopTo = viewControllerClass;
[self startNavigationWithNavigationObject:navigationObject delegate:nil completionHandler:NULL];
}
- (void)popToViewControllerWithPageType:(nonnull NSString *)pageType animated:(BOOL)animated {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:nil navigationController:nil viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePopTo];
navigationObject.pageTypeToPopTo = pageType;
[self startNavigationWithNavigationObject:navigationObject delegate:nil completionHandler:NULL];
}
- (void)popToRootAnimated:(BOOL)animated {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:nil navigationController:nil viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePopToRoot];
[self startNavigationWithNavigationObject:navigationObject delegate:nil completionHandler:NULL];
}
- (void)addNavigationOperation:(nonnull MVMCoreOperation *)operation {
[self.navigationQueue addOperation:operation];
}
- (void)cancelNavigation {
[self.navigationQueue cancelAllOperations];
}
#pragma mark - Navigation Extra
- (void)pushViewController:(nonnull UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate replaceInStack:(BOOL)replaceInStack completionHandler:(nullable void (^)(void))completionBlock {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:viewController navigationController:navigationController viewControllers:nil animated:animated tryToReplaceFirst:replaceInStack navigationType:NavigationTypePush];
[self startNavigationWithNavigationObject:navigationObject delegate:delegate completionHandler:completionBlock];
}
- (void)replaceTopViewControllerWithViewController:(nonnull UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate replaceInStack:(BOOL)replaceInStack completionHandler:(nullable void (^)(void))completionBlock {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:viewController navigationController:navigationController viewControllers:nil animated:animated tryToReplaceFirst:replaceInStack navigationType:NavigationTypeReplaceTop];
[self startNavigationWithNavigationObject:navigationObject delegate:delegate completionHandler:completionBlock];
}
- (void)replaceAfterRoot:(nonnull UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate replaceInStack:(BOOL)replaceInStack completionHandler:(nullable void (^)(void))completionBlock {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:viewController navigationController:navigationController viewControllers:nil animated:animated tryToReplaceFirst:replaceInStack navigationType:NavigationTypeReplaceAfterRoot];
[self startNavigationWithNavigationObject:navigationObject delegate:delegate completionHandler:completionBlock];
}
- (void)setViewControllers:(nonnull NSArray *)viewControllers navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate replaceInStack:(BOOL)replaceInStack completionHandler:(nullable void (^)(void))completionBlock {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:nil navigationController:navigationController viewControllers:viewControllers animated:animated tryToReplaceFirst:replaceInStack navigationType:NavigationTypeSet];
[self startNavigationWithNavigationObject:navigationObject delegate:delegate completionHandler:completionBlock];
}
- (void)popTopViewControllerAnimated:(BOOL)animated navigationController:(nullable UINavigationController *)navigationController delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:nil navigationController:navigationController viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePop];
[self startNavigationWithNavigationObject:navigationObject delegate:delegate completionHandler:completionBlock];
}
- (void)popViewController:(nonnull UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:viewController navigationController:navigationController viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePopSpecific];
[self startNavigationWithNavigationObject:navigationObject delegate:delegate completionHandler:completionBlock];
}
- (void)popToViewController:(nonnull UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:viewController navigationController:navigationController viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePopTo];
[self startNavigationWithNavigationObject:navigationObject delegate:delegate completionHandler:completionBlock];
}
- (void)popToInstanceOfClass:(nonnull Class)viewControllerClass navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:nil navigationController:navigationController viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePopTo];
navigationObject.classToPopTo = viewControllerClass;
[self startNavigationWithNavigationObject:navigationObject delegate:delegate completionHandler:completionBlock];
}
- (void)popToViewControllerWithPageType:(nonnull NSString *)pageType navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:nil navigationController:navigationController viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePopTo];
navigationObject.pageTypeToPopTo = pageType;
[self startNavigationWithNavigationObject:navigationObject delegate:delegate completionHandler:completionBlock];
}
- (void)popToRootAnimated:(BOOL)animated navigationController:(nullable UINavigationController *)navigationController delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
MVMCoreNavigationObject *navigationObject = [[MVMCoreNavigationObject alloc] initWithViewController:nil navigationController:navigationController viewControllers:nil animated:animated tryToReplaceFirst:NO navigationType:NavigationTypePopToRoot];
[self startNavigationWithNavigationObject:navigationObject delegate:delegate completionHandler:completionBlock];
}
- (nullable NSArray<UIViewController *> *)getViewControllersForNavigationController:(nullable UINavigationController *)navigationController {
// Check if we are currently animating.
NSInteger index = [self.navigationQueue.operations indexOfObjectPassingTest:^BOOL(__kindof MVMCoreNavigationOperation * _Nonnull operation, NSUInteger idx, BOOL * _Nonnull stop) {
return operation.isExecuting && [operation isKindOfClass:[MVMCoreNavigationOperation class]];
}];
if (index != NSNotFound) {
// Test above ensures we have an MVMCoreNavigationOperation. There are situations where block operations are submitted to the queue to be executed.
MVMCoreNavigationOperation *operation = [self.navigationQueue.operations objectAtIndex:index];
if (operation.navigationObject.navigationController == navigationController && operation.futureViewControllers) {
// Return the future state if animating.
return operation.futureViewControllers;
}
}
return navigationController.viewControllers;
}
#pragma mark - Presentation Simple
- (void)presentViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated {
[self presentViewController:viewController animated:animated delegate:nil completionHandler:NULL];
}
- (void)dismissTopViewControllerAnimated:(BOOL)animated {
[self dismissTopViewControllerAnimated:animated delegate:nil completionHandler:NULL];
}
- (void)dismissViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated {
[self dismissViewController:viewController animated:animated delegate:nil completionHandler:NULL];
}
- (void)dismissToBottom:(BOOL)animated {
[self dismissToBottom:animated delegate:nil completionHandler:NULL];
}
- (void)cancelPresentation {
[self.presentationQueue cancelAllOperations];
}
#pragma mark - Presentation Extra
- (void)presentViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
UIViewController *controllerToPresentOn = self.viewControllerToPresentOn ?: [MVMCoreGetterUtility getKeyWindow].rootViewController;
if (!controllerToPresentOn) {
// Log if we are attempting to add an alert to nothing.
MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:nil message:@"The viewControllerToPresent on property is nil and an attempt was made to present." code:ErrorCodeNoViewControllerToPresentOn domain:ErrorDomainNative location:[NSString stringWithFormat:@"ViewController:%@|Delegate:%@",viewController.description,delegate]];
[MVMCoreLoggingHandler addErrorToLog:error];
} else {
MVMCorePresentViewControllerOperation *operation = [[MVMCorePresentViewControllerOperation alloc] initWithPresentingViewController:controllerToPresentOn presentedViewController:viewController animated:animated];
operation.delegate = delegate;
operation.completionBlock = completionBlock;
[self.presentationQueue addOperation:operation];
}
}];
}
- (void)dismissTopViewControllerAnimated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
// Dismiss on the main navigation controller.
UIViewController *controllerToPresentOn = self.viewControllerToPresentOn ?: [MVMCoreGetterUtility getKeyWindow].rootViewController;
MVMCoreDismissViewControllerOperation *operation = [[MVMCoreDismissViewControllerOperation alloc] initAndDismissTopViewController:controllerToPresentOn animated:animated];
operation.completionBlock = completionBlock;
[[NSOperationQueue mainQueue] addOperation:operation];
}];
}
- (void)dismissViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
MVMCoreDismissViewControllerOperation *operation = [[MVMCoreDismissViewControllerOperation alloc] initAndDismissViewController:viewController animated:animated];
operation.completionBlock = completionBlock;
[[NSOperationQueue mainQueue] addOperation:operation];
}
- (void)dismissToBottom:(BOOL)animated delegate:(nullable NSObject<MVMCorePresentationDelegateProtocol>*)delegate completionHandler:(nullable void (^)(void))completionBlock {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
// Dismiss on the main navigation controller.
UIViewController *controllerToPresentOn = self.viewControllerToPresentOn ?: [MVMCoreGetterUtility getKeyWindow].rootViewController;
MVMCoreDismissViewControllerOperation *operation = [[MVMCoreDismissViewControllerOperation alloc] initAndDismissViewController:controllerToPresentOn animated:animated];
operation.completionBlock = completionBlock;
[[NSOperationQueue mainQueue] addOperation:operation];
}];
}
@end

View File

@ -0,0 +1,27 @@
//
// MVMCoreNavigationObject.h
// mobilefirst
//
// Created by Scott Pfeil on 4/22/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <MVMCore/MVMCoreConstants.h>
@interface MVMCoreNavigationObject : NSObject
@property (nullable, strong, nonatomic) UIViewController *viewController;
@property (nullable, strong, nonatomic) UINavigationController *navigationController;
@property (nullable, strong, nonatomic) NSArray *viewControllers;
@property (nullable, strong, nonatomic) Class classToPopTo;
@property (nullable, strong, nonatomic) NSString *pageTypeToPopTo;
@property (nonatomic) NavigationType navigationType;
@property (nonatomic) BOOL animated;
@property (nonatomic) BOOL tryToReplaceFirst;
@property (nonatomic) BOOL stopLoadingOverlay;
- (nullable instancetype)initWithViewController:(nullable UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController viewControllers:(nullable NSArray *)viewControllers animated:(BOOL)animated tryToReplaceFirst:(BOOL)tryToReplaceFirst navigationType:(NavigationType)navigationType;
@end

View File

@ -0,0 +1,31 @@
//
// MVMCoreNavigationObject.m
// mobilefirst
//
// Created by Scott Pfeil on 4/22/16.
// Copyright © 2016 Verizon Wireless. All rights reserved.
//
#import "MVMCoreNavigationObject.h"
#import "MVMCoreNavigationHandler.h"
@implementation MVMCoreNavigationObject
- (nullable instancetype)initWithViewController:(nullable UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController viewControllers:(nullable NSArray *)viewControllers animated:(BOOL)animated tryToReplaceFirst:(BOOL)tryToReplaceFirst navigationType:(NavigationType)navigationType {
if (self = [super init]) {
self.viewController = viewController;
UINavigationController *navigationToUse = navigationController ?: [MVMCoreNavigationHandler sharedNavigationHandler].navigationController;
self.navigationController = navigationToUse;
self.viewControllers = viewControllers;
self.animated = animated;
self.tryToReplaceFirst = tryToReplaceFirst;
self.navigationType = navigationType;
}
return self;
}
@end

View File

@ -0,0 +1,26 @@
//
// MVMCoreNavigationOperation.h
// myverizon
//
// Created by Scott Pfeil on 11/17/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
// Handles navigation
#import <Foundation/Foundation.h>
#import <MVMCore/MVMCoreOperation.h>
#import <MVMCore/MVMCorePresentationDelegateProtocol.h>
@class MVMCoreNavigationObject;
@interface MVMCoreNavigationOperation : MVMCoreOperation <UINavigationControllerDelegate>
@property (nonnull, strong, nonatomic) MVMCoreNavigationObject *navigationObject;
@property (nullable, weak, nonatomic) NSObject<MVMCorePresentationDelegateProtocol> *delegate;
/// Used to keep track of future view controller state because .viewControllers is not reliable during animation.
@property (nullable, strong, nonatomic) NSArray <UIViewController *>*futureViewControllers;
- (nullable instancetype)initWithNavigationObject:(nonnull MVMCoreNavigationObject *)navigationObject;
@end

View File

@ -0,0 +1,334 @@
//
// MVMCoreNavigationOperation.m
// myverizon
//
// Created by Scott Pfeil on 11/17/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
#import "MVMCoreNavigationOperation.h"
#import "MVMCoreNavigationObject.h"
#import "MVMCoreViewManagerProtocol.h"
#import "MVMCoreViewControllerProtocol.h"
#import "MVMCoreLoggingHandler.h"
#import "MVMCoreLoadingOverlayHandler.h"
#import "MVMCoreViewControllerAnimatedTransitioning.h"
#import "MVMCoreNavigationHandler.h"
@interface MVMCoreNavigationOperation ()
@property (strong, nonatomic) id animatedTransitioning;
@end
@implementation MVMCoreNavigationOperation
- (nullable instancetype)initWithNavigationObject:(nonnull MVMCoreNavigationObject *)navigationObject {
if (self = [super init]) {
self.navigationObject = navigationObject;
}
return self;
}
- (void)main {
if ([self checkAndHandleForCancellation]) {
return;
}
[[NSOperationQueue mainQueue] addOperation:[NSBlockOperation blockOperationWithBlock:^{
self.navigationObject.navigationController.delegate = self;
// First, we will try to replace the view controller in the stack with any of the same.
NSInteger location = NSNotFound;
if (self.navigationObject.tryToReplaceFirst) {
if (self.navigationObject.navigationController.viewControllers.count > 0) {
for (NSInteger i = self.navigationObject.navigationController.viewControllers.count - 1; i >= 0; i--) {
UIViewController *controller = [self.navigationObject.navigationController.viewControllers objectAtIndex:i];
if ([self.navigationObject.viewController respondsToSelector:@selector(pageType)]) {
// Check if tab control.
NSString *pageType = [self.navigationObject.viewController performSelector:@selector(pageType)];
if ([controller conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)] && [self.navigationObject.viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) {
// Check if page is in view manager, if so, replace it.
if ([((UIViewController <MVMCoreViewManagerProtocol>*)controller) containsPageWithPageType:pageType]) {
location = i;
break;
}
} else if ([controller respondsToSelector:@selector(pageType)]) {
// Compare page type if possible.
if ([[controller performSelector:@selector(pageType)] isEqualToString:pageType]) {
location = i;
break;
}
} else if (controller.class == self.navigationObject.viewController.class) {
// Fallback to class compare if page type compare is unavailable.
location = i;
break;
}
} else if (controller.class == self.navigationObject.viewController.class) {
// Fallback to class compare if page type compare is unavailable.
location = i;
break;
}
}
}
}
if (location != NSNotFound) {
// Replace the view controller.
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[self.navigationObject.navigationController.viewControllers subarrayWithRange:NSMakeRange(0, location)]];
[viewControllers addObject:self.navigationObject.viewController];
[self setViewControllers:viewControllers];
} else {
// Load normal
switch (self.navigationObject.navigationType) {
case NavigationTypePush:
{
[self pushViewController];
}
break;
case NavigationTypeSet:
{
[self setViewControllers:self.navigationObject.viewControllers];
}
break;
case NavigationTypeReplaceTop:
{
if (self.navigationObject.navigationController.viewControllers.count > 0) {
// Replaces the current view controller.
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:self.navigationObject.navigationController.viewControllers];
[viewControllers replaceObjectAtIndex:([self.navigationObject.navigationController.viewControllers count] - 1) withObject:self.navigationObject.viewController];
[self setViewControllers:viewControllers];
} else {
// Just push
[self pushViewController];
}
}
break;
case NavigationTypeReplaceAfterRoot:
{
if (self.navigationObject.navigationController.viewControllers.count > 0) {
// Replaces all after the root.
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:self.navigationObject.navigationController.viewControllers];
NSArray *replaceViewControllers = @[viewControllers.firstObject,self.navigationObject.viewController];
[self setViewControllers:replaceViewControllers];
} else {
// Just push
[self pushViewController];
}
}
break;
case NavigationTypePop:
{
if (self.navigationObject.navigationController.viewControllers.count > 1) {
// Although the post animation state is currently fine with pop, store anyway as a precaution
self.futureViewControllers = [self.navigationObject.navigationController.viewControllers subarrayWithRange:NSMakeRange(0, self.navigationObject.navigationController.viewControllers.count - 1)];
[self prepareDisplay];
[self.navigationObject.navigationController popViewControllerAnimated:self.navigationObject.animated];
} else {
[self markAsFinished];
}
}
break;
case NavigationTypePopSpecific:
{
NSInteger location = NSNotFound;
if (self.navigationObject.navigationController.viewControllers.count > 1) {
for (NSInteger i = self.navigationObject.navigationController.viewControllers.count - 1; i >= 0; i--) {
UIViewController *controller = [self.navigationObject.navigationController.viewControllers objectAtIndex:i];
if (controller == self.navigationObject.viewController) {
location = i;
break;
}
}
}
if (location != NSNotFound) {
NSArray *viewControllers = [self.navigationObject.navigationController.viewControllers subarrayWithRange:NSMakeRange(0, location)];
[self setViewControllers:viewControllers];
} else if (self.navigationObject.viewControllers) {
// Controller not found and specific controller set present, replace controller set instead.
[self setViewControllers:self.navigationObject.viewControllers];
} else {
[self markAsFinished];
}
break;
}
case NavigationTypePopTo:
{
if (self.navigationObject.classToPopTo || self.navigationObject.pageTypeToPopTo) {
for (UIViewController *controller in [self.navigationObject.navigationController viewControllers]) {
// If the page type to pop to or class to pop to matches...
if ((self.navigationObject.pageTypeToPopTo && [controller respondsToSelector:@selector(pageType)] && [[controller performSelector:@selector(pageType)] isEqualToString:self.navigationObject.pageTypeToPopTo]) || (self.navigationObject.classToPopTo && [controller isKindOfClass:self.navigationObject.classToPopTo])) {
self.navigationObject.viewController = controller;
}
}
}
if (self.navigationObject.viewController && (self.navigationObject.viewController != self.navigationObject.navigationController.topViewController)) {
// Store post animation state.
NSInteger index = [[self.navigationObject.navigationController viewControllers] indexOfObject:self.navigationObject.viewController];
self.futureViewControllers = [self.navigationObject.navigationController.viewControllers subarrayWithRange:NSMakeRange(0, index + 1)];
[self prepareDisplay];
[self.navigationObject.navigationController popToViewController:self.navigationObject.viewController animated:self.navigationObject.animated];
} else {
[self markAsFinished];
}
}
break;
case NavigationTypePopToRoot:
{
if (self.navigationObject.navigationController.viewControllers.count > 1) {
// Store post animation state.
self.futureViewControllers = @[self.navigationObject.navigationController.viewControllers.firstObject];
[self prepareDisplay];
[self.navigationObject.navigationController popToRootViewControllerAnimated:self.navigationObject.animated];
} else {
[self markAsFinished];
}
}
break;
default:
[self markAsFinished];
break;
}
}
}]];
}
- (void)pushViewController {
// Although the post animation state is currently fine with push, store anyway as a precaution
self.futureViewControllers = [self.navigationObject.navigationController.viewControllers arrayByAddingObject:self.navigationObject.viewController];
[self prepareDisplay];
[self.navigationObject.navigationController pushViewController:self.navigationObject.viewController animated:self.navigationObject.animated];
}
- (void)setViewControllers:(NSArray *)viewControllers {
if (![self.navigationObject.navigationController.viewControllers isEqualToArray:viewControllers]) {
self.futureViewControllers = viewControllers;
[self prepareDisplay];
[self.navigationObject.navigationController setViewControllers:viewControllers animated:self.navigationObject.animated];
} else {
// If the arrays contain the same objects, then the delegate methods are never called. This is a workaround for that.
[self markAsFinished];
}
}
// Notify delegates to prepare for the controller to be added to the navigation controller.
- (void)prepareDisplay {
UINavigationController *navigationController = self.navigationObject.navigationController;
UIViewController *viewController = self.futureViewControllers.lastObject;
if (self.delegate && [self.delegate respondsToSelector:@selector(navigationController:prepareDisplayForViewController:)]) {
[self.delegate navigationController:navigationController
prepareDisplayForViewController:viewController];
}
for (NSObject<MVMCorePresentationDelegateProtocol> *delegate in [MVMCoreNavigationHandler sharedNavigationHandler].delegates.allObjects) {
if (delegate && [delegate respondsToSelector:@selector(navigationController:prepareDisplayForViewController:)]) {
[delegate navigationController:navigationController
prepareDisplayForViewController:viewController];
}
}
}
- (void)markAsFinished {
self.navigationObject.navigationController.delegate = nil;
if (self.navigationObject.stopLoadingOverlay) {
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:YES];
self.navigationObject.stopLoadingOverlay = NO;
}
if (self.animatedTransitioning) {
[self.animatedTransitioning removeObserver:self forKeyPath:@"interactiveTransitionCanceled"];
self.animatedTransitioning = nil;
}
[super markAsFinished];
}
#pragma mark - Delegate
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (self.navigationObject.stopLoadingOverlay) {
[[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:YES];
self.navigationObject.stopLoadingOverlay = NO;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(navigationController:willDisplayViewController:)]) {
[self.delegate navigationController:navigationController willDisplayViewController:viewController];
}
for (NSObject<MVMCorePresentationDelegateProtocol> *delegate in [MVMCoreNavigationHandler sharedNavigationHandler].delegates.allObjects) {
if (delegate && [delegate respondsToSelector:@selector(navigationController:willDisplayViewController:)]) {
[delegate navigationController:navigationController willDisplayViewController:viewController];
}
}
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
MVMCoreLog(@"finished %@",[viewController class]);
if (self.delegate && [self.delegate respondsToSelector:@selector(navigationController:displayedViewController:)]) {
[self.delegate navigationController:navigationController displayedViewController:viewController];
}
for (NSObject<MVMCorePresentationDelegateProtocol> *delegate in [MVMCoreNavigationHandler sharedNavigationHandler].delegates.allObjects) {
if (delegate && [delegate respondsToSelector:@selector(navigationController:displayedViewController:)]) {
[delegate navigationController:navigationController displayedViewController:viewController];
}
}
[self markAsFinished];
// Legacy notify that page has changed
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:MVMCoreNotificationViewControllerChanged object:nil];
});
}
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
if (self.delegate && [self.delegate respondsToSelector:@selector(navigationController:animationControllerForOperation:fromViewController:toViewController:)]) {
id animatedTransitioning = [self.delegate navigationController:navigationController animationControllerForOperation:operation fromViewController:fromVC toViewController:toVC];
if ([animatedTransitioning conformsToProtocol:@protocol(MVMCoreViewControllerAnimatedTransitioning)]) {
[animatedTransitioning addObserver:self forKeyPath:@"interactiveTransitionCanceled" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
self.animatedTransitioning = animatedTransitioning;
}
return animatedTransitioning;
} else {
return nil;
}
}
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
if (self.delegate && [self.delegate respondsToSelector:@selector(navigationController:interactionControllerForAnimationController:)]) {
return [self.delegate navigationController:navigationController interactionControllerForAnimationController:animationController];
} else {
return nil;
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"interactiveTransitionCanceled"]) {
BOOL transitionCanceled = ((NSNumber *)change[NSKeyValueChangeNewKey]).boolValue;
if (transitionCanceled) {
//When interactive transition canceled, the destination viewController should be removed from navigationController
[self.navigationObject.viewController removeFromParentViewController];
[self markAsFinished];
if (self.delegate && [self.delegate respondsToSelector:@selector(navigationController:interactiveTransitionWasCanceled:)]) {
[self.delegate navigationController:self.navigationObject.navigationController interactiveTransitionWasCanceled:true];
}
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
@end

View File

@ -0,0 +1,19 @@
//
// MVMCorePresentAnimationOperation.h
// myverizon
//
// Created by Scott Pfeil on 11/25/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
// Should be added to the main queue. Presents the view controller on the presenting view controller.
#import <MVMCore/MVMCoreOperation.h>
#import <MVMCore/MVMCorePresentationDelegateProtocol.h>
@interface MVMCorePresentAnimationOperation : MVMCoreOperation
@property (nullable, weak, nonatomic) NSObject<MVMCorePresentationDelegateProtocol> *delegate;
- (nullable instancetype)initWithPresentingViewController:(nullable UIViewController *)presentingViewController presentedViewController:(nullable UIViewController *)presentedViewController animated:(BOOL)animated;
@end

View File

@ -0,0 +1,63 @@
//
// MVMCorePresentAnimationOperation.m
// myverizon
//
// Created by Scott Pfeil on 11/25/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
#import "MVMCorePresentAnimationOperation.h"
#import "MVMCoreNavigationHandler.h"
@interface MVMCorePresentAnimationOperation ()
@property (nullable, strong, nonatomic) UIViewController *presentingViewController;
@property (nullable, strong, nonatomic) UIViewController *presentedViewController;
@property (nonatomic) BOOL animate;
@end
@implementation MVMCorePresentAnimationOperation
- (nullable instancetype)initWithPresentingViewController:(nullable UIViewController *)presentingViewController presentedViewController:(nullable UIViewController *)presentedViewController animated:(BOOL)animated {
if (self = [super init]) {
self.presentedViewController = presentedViewController;
self.presentingViewController = presentingViewController;
self.presentedViewController.modalPresentationStyle = UIModalPresentationFullScreen;
self.animate = animated;
}
return self;
}
- (void)main {
// Always check for cancellation before launching the task.
if ([self checkAndHandleForCancellation]) {
return;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(viewController:willPresentViewController:)]) {
[self.delegate viewController:self.presentingViewController willPresentViewController:self.presentedViewController];
}
for (NSObject<MVMCorePresentationDelegateProtocol> *delegate in [MVMCoreNavigationHandler sharedNavigationHandler].delegates.allObjects) {
if (delegate && [delegate respondsToSelector:@selector(viewController:willPresentViewController:)]) {
[delegate viewController:self.presentingViewController willPresentViewController:self.presentedViewController];
}
}
[self.presentingViewController presentViewController:self.presentedViewController animated:self.animate completion:^{
if (self.delegate && [self.delegate respondsToSelector:@selector(viewController:didPresentViewController:)]) {
[self.delegate viewController:self.presentingViewController didPresentViewController:self.presentedViewController];
}
for (NSObject<MVMCorePresentationDelegateProtocol> *delegate in [MVMCoreNavigationHandler sharedNavigationHandler].delegates.allObjects) {
if (delegate && [delegate respondsToSelector:@selector(viewController:didPresentViewController:)]) {
[delegate viewController:self.presentingViewController didPresentViewController:self.presentedViewController];
}
}
[self markAsFinished];
}];
}
@end

View File

@ -0,0 +1,20 @@
//
// MVMCorePresentViewControllerOperation.h
// myverizon
//
// Created by Scott Pfeil on 9/28/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
// An operation for presenting a view controller. Depends on alert as well. Should NOT go onto the main queue.
#import <Foundation/Foundation.h>
#import <MVMCore/MVMCoreOperation.h>
#import <MVMCore/MVMCorePresentationDelegateProtocol.h>
@interface MVMCorePresentViewControllerOperation : MVMCoreOperation
@property (nullable, weak, nonatomic) NSObject<MVMCorePresentationDelegateProtocol> *delegate;
- (nullable instancetype)initWithPresentingViewController:(nullable UIViewController *)presentingViewController presentedViewController:(nullable UIViewController *)presentedViewController animated:(BOOL)animated;
@end

View File

@ -0,0 +1,123 @@
//
// MVMCorePresentViewControllerOperation.m
// myverizon
//
// Created by Scott Pfeil on 9/28/15.
// Copyright © 2015 Verizon Wireless. All rights reserved.
//
#import "MVMCorePresentViewControllerOperation.h"
#import "MVMCoreAlertController.h"
#import "MVMCorePresentAnimationOperation.h"
#import "MVMCoreDispatchUtility.h"
#import "MVMCoreConstants.h"
@interface MVMCorePresentViewControllerOperation ()
@property (nullable, strong, nonatomic) UIViewController *presentingViewController;
@property (nullable, strong, nonatomic) UIViewController *presentedViewController;
@property (nonatomic) BOOL animate;
@property (nonatomic) BOOL beingObserved;
@property (nullable, weak, nonatomic) MVMCoreAlertController *alertController;
@end
@implementation MVMCorePresentViewControllerOperation
// The context for kvo
static void * XXContext = &XXContext;
- (void)dealloc {
[self stopObservingAlertView];
}
- (nullable instancetype)initWithPresentingViewController:(nullable UIViewController *)presentingViewController presentedViewController:(nullable UIViewController *)presentedViewController animated:(BOOL)animated {
if (self = [super init]) {
self.presentedViewController = presentedViewController;
self.presentingViewController = presentingViewController;
self.animate = animated;
}
return self;
}
- (void)main {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
// Always check for cancellation before launching the task.
if ([self checkAndHandleForCancellation]) {
return;
}
[self present];
}];
}
- (void)present {
if ([self checkAndHandleForCancellation]) {
return;
}
if (self.presentingViewController && self.presentedViewController) {
// Gets the top most presented.
UIViewController *controllerToPresentOn = self.presentingViewController;
while (controllerToPresentOn.presentedViewController) {
controllerToPresentOn = controllerToPresentOn.presentedViewController;
}
// If an alert is showing, don't present until the alerts are dismissed.
if ([controllerToPresentOn isKindOfClass:[MVMCoreAlertController class]]) {
[self observeForAlertDismissal:(MVMCoreAlertController *)controllerToPresentOn];
} else {
MVMCorePresentAnimationOperation *animationOperation = [[MVMCorePresentAnimationOperation alloc] initWithPresentingViewController:controllerToPresentOn presentedViewController:self.presentedViewController animated:self.animate];
animationOperation.delegate = self.delegate;
[animationOperation setCompletionBlock:^{
[self markAsFinished];
// Notify that page has changed
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:MVMCoreNotificationViewControllerChanged object:nil];
});
}];
[[NSOperationQueue mainQueue] addOperation:animationOperation];
}
} else {
[self markAsFinished];
}
}
- (void)cancel {
[super cancel];
[self stopObservingAlertView];
}
#pragma mark - Observer Functions
- (void)observeForAlertDismissal:(MVMCoreAlertController *)alertController {
if (!self.beingObserved) {
self.beingObserved = YES;
self.alertController = alertController;
[alertController addObserver:self forKeyPath:@"visible" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:XXContext];
}
}
- (void)stopObservingAlertView {
if (self.beingObserved) {
[self.alertController removeObserver:self forKeyPath:@"visible" context:XXContext];
self.alertController = nil;
self.beingObserved = NO;
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == XXContext && [keyPath isEqualToString:@"visible"]) {
if (![object isVisible]) {
[self stopObservingAlertView];
[self present];
}
}
}
@end

Some files were not shown because too many files have changed in this diff Show More