From b9835e72e7e14563771ea69d13fde296e0636be7 Mon Sep 17 00:00:00 2001 From: "Yang, Tianhang(Chris)" Date: Fri, 1 Dec 2017 15:13:30 -0500 Subject: [PATCH] core --- .DS_Store | Bin 0 -> 6148 bytes MVMCore/MVMCore.xcodeproj/project.pbxproj | 1018 +++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/MVMCore.xcscheme | 82 ++ .../xcschemes/xcschememanagement.plist | 14 + .../MVMCoreActionDelegateProtocol.h | 52 + .../ActionHandling/MVMCoreActionHandler.h | 92 ++ .../ActionHandling/MVMCoreActionHandler.m | 324 ++++++ .../AlertHandling/MVMCoreAlertController.h | 16 + .../AlertHandling/MVMCoreAlertController.m | 34 + .../MVMCoreAlertDelegateProtocol.h | 24 + .../AlertHandling/MVMCoreAlertHandler.h | 87 ++ .../AlertHandling/MVMCoreAlertHandler.m | 288 +++++ .../AlertHandling/MVMCoreAlertObject.h | 61 + .../AlertHandling/MVMCoreAlertObject.m | 181 +++ .../AlertHandling/MVMCoreAlertOperation.h | 36 + .../AlertHandling/MVMCoreAlertOperation.m | 242 ++++ ...MVMCoreTopAlertAnimationDelegateProtocol.h | 22 + .../MVMCoreTopAlertDelegateProtocol.h | 20 + .../AlertHandling/MVMCoreTopAlertObject.h | 72 ++ .../AlertHandling/MVMCoreTopAlertObject.m | 85 ++ .../AlertHandling/MVMCoreTopAlertOperation.h | 30 + .../AlertHandling/MVMCoreTopAlertOperation.m | 274 +++++ .../Categories/NSArray+MFConvenience.h | 43 + .../Categories/NSArray+MFConvenience.m | 99 ++ .../NSDecimalNumber+MFConvenience.h | 16 + .../NSDecimalNumber+MFConvenience.m | 23 + .../Categories/NSDictionary+MFConvenience.h | 66 ++ .../Categories/NSDictionary+MFConvenience.m | 113 ++ MVMCore/MVMCore/Categories/UIFont+MFSpacing.h | 15 + MVMCore/MVMCore/Categories/UIFont+MFSpacing.m | 24 + MVMCore/MVMCore/Categories/UILabel+MFCustom.h | 15 + MVMCore/MVMCore/Categories/UILabel+MFCustom.m | 67 ++ MVMCore/MVMCore/Constants/MVMCoreConstants.h | 47 + MVMCore/MVMCore/Constants/MVMCoreConstants.m | 31 + .../MVMCore/Constants/MVMCoreErrorConstants.h | 41 + .../MVMCore/Constants/MVMCoreErrorConstants.m | 14 + .../MVMCoreHardcodedStringsConstants.h | 25 + .../MVMCoreHardcodedStringsConstants.m | 25 + .../MVMCore/Constants/MVMCoreJSONConstants.h | 95 ++ .../MVMCore/Constants/MVMCoreJSONConstants.m | 95 ++ .../Reachability/Reachability.h | 62 + .../Reachability/Reachability.m | 242 ++++ MVMCore/MVMCore/Info.plist | 24 + .../LoadHandling/FreeBee/FreeBeeAuthObject.h | 23 + .../LoadHandling/FreeBee/FreeBeeAuthObject.m | 117 ++ .../LoadHandling/FreeBee/FreeBeeUrlObject.h | 28 + .../LoadHandling/FreeBee/FreeBeeUrlObject.m | 100 ++ .../LoadHandling/FreeBee/MFFreebeeHandler.h | 49 + .../LoadHandling/FreeBee/MFFreebeeHandler.m | 671 +++++++++++ .../LoadHandling/FreeBee/MFFreebeeOperation.h | 22 + .../LoadHandling/FreeBee/MFFreebeeOperation.m | 107 ++ .../MVMCore/LoadHandling/FreeBee/freebee.json | 14 + .../FreeBee/freebeelaunchApp.json | 25 + .../MVMCoreLoadingOverlayDelegateProtocol.h | 25 + .../MVMCoreLoadingOverlayHandler.h | 29 + .../MVMCoreLoadingOverlayHandler.m | 172 +++ .../MVMCoreLoadingViewControllerProtocol.h | 17 + .../MVMCoreLoadDelegateProtocol.h | 64 ++ .../MVMCore/LoadHandling/MVMCoreLoadHandler.h | 68 ++ .../MVMCore/LoadHandling/MVMCoreLoadHandler.m | 364 ++++++ .../MVMCore/LoadHandling/MVMCoreLoadObject.h | 60 + .../MVMCore/LoadHandling/MVMCoreLoadObject.m | 57 + .../MVMCoreLoadRequestOperation.h | 106 ++ .../MVMCoreLoadRequestOperation.m | 908 +++++++++++++++ .../LoadHandling/MVMCoreRequestParameters.h | 113 ++ .../LoadHandling/MVMCoreRequestParameters.m | 136 +++ .../Certificates/DigiCertGlobalRootCA.cer | Bin 0 -> 947 bytes ...PublicPrimaryCertificationAuthority-G5.cer | Bin 0 -> 1239 bytes .../SSLPinning/MFSSLPinningHandler.h | 44 + .../SSLPinning/MFSSLPinningHandler.m | 585 ++++++++++ .../SSLPinning/MFURLSessionChallengeResult.h | 18 + .../SSLPinning/MFURLSessionChallengeResult.m | 20 + MVMCore/MVMCore/MVMCore.h | 109 ++ .../MainProtocols/MVMCoreGlobalLoadProtocol.h | 35 + .../MVMCoreLoggingDelegateProtocol.h | 24 + .../MVMCoreViewControllerProtocol.h | 34 + .../MVMCoreViewManagerProtocol.h | 19 + .../MainProtocols/MVMCoreViewProtocol.h | 20 + .../MainProtocols/MobileFirstProtocol.h | 65 ++ .../MVMCoreMainSplitViewProtocol.h | 23 + .../MainStructure/MVMCoreTopAlertView.h | 33 + .../MainStructure/MVMCoreTopAlertView.m | 44 + MVMCore/MVMCore/MainStructure/PanelProtocol.h | 32 + MVMCore/MVMCore/OtherHandlers/MVMCoreCache.h | 135 +++ MVMCore/MVMCore/OtherHandlers/MVMCoreCache.m | 627 ++++++++++ .../OtherHandlers/MVMCoreLoggingHandler.h | 21 + .../OtherHandlers/MVMCoreLoggingHandler.m | 32 + .../MVMCoreDismissViewControllerOperation.h | 21 + .../MVMCoreDismissViewControllerOperation.m | 86 ++ .../MVMCoreNavigationHandler.h | 132 +++ .../MVMCoreNavigationHandler.m | 298 +++++ .../MVMCoreNavigationObject.h | 26 + .../MVMCoreNavigationObject.m | 31 + .../MVMCoreNavigationOperation.h | 22 + .../MVMCoreNavigationOperation.m | 235 ++++ .../MVMCorePresentAnimationOperation.h | 19 + .../MVMCorePresentAnimationOperation.m | 51 + .../MVMCorePresentViewControllerOperation.h | 20 + .../MVMCorePresentViewControllerOperation.m | 116 ++ .../MVMCorePresentationDelegateProtocol.h | 33 + .../MVMCore/Session/MVMCoreSessionObject.h | 47 + .../MVMCore/Session/MVMCoreSessionObject.m | 36 + .../Session/MVMCoreSessionTimeHandler.h | 56 + .../Session/MVMCoreSessionTimeHandler.m | 195 ++++ MVMCore/MVMCore/Singletons/MVMCoreObject.h | 45 + MVMCore/MVMCore/Singletons/MVMCoreObject.m | 25 + .../Strings/en.lproj/Localizable.strings | 23 + .../MFHardCodedServerResponse.h | 24 + .../MFHardCodedServerResponse.m | 98 ++ .../HardCodedServerResponse/canned.json | 678 +++++++++++ .../Utility/Helpers/MVMCoreActionUtility.h | 38 + .../Utility/Helpers/MVMCoreActionUtility.m | 165 +++ .../Utility/Helpers/MVMCoreDispatchUtility.h | 21 + .../Utility/Helpers/MVMCoreDispatchUtility.m | 45 + .../Utility/Helpers/MVMCoreGetterUtility.h | 22 + .../Utility/Helpers/MVMCoreGetterUtility.m | 26 + MVMCore/MVMCore/Utility/MVMCoreErrorObject.h | 65 ++ MVMCore/MVMCore/Utility/MVMCoreErrorObject.m | 142 +++ MVMCore/MVMCore/Utility/MVMCoreOperation.h | 20 + MVMCore/MVMCore/Utility/MVMCoreOperation.m | 108 ++ .../MVMCoreViewControllerMappingObject.h | 54 + .../MVMCoreViewControllerMappingObject.m | 132 +++ .../MVMCoreViewControllerNibMappingObject.h | 25 + .../MVMCoreViewControllerNibMappingObject.m | 24 + ...eViewControllerProgrammaticMappingObject.h | 19 + ...eViewControllerProgrammaticMappingObject.m | 22 + ...oreViewControllerStoryBoardMappingObject.h | 25 + ...oreViewControllerStoryBoardMappingObject.m | 24 + 129 files changed, 12732 insertions(+) create mode 100644 .DS_Store create mode 100644 MVMCore/MVMCore.xcodeproj/project.pbxproj create mode 100644 MVMCore/MVMCore.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 MVMCore/MVMCore.xcodeproj/xcshareddata/xcschemes/MVMCore.xcscheme create mode 100644 MVMCore/MVMCore.xcodeproj/xcuserdata/yangti5.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 MVMCore/MVMCore/ActionHandling/MVMCoreActionDelegateProtocol.h create mode 100644 MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h create mode 100644 MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreAlertController.h create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreAlertController.m create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreAlertDelegateProtocol.h create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.h create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.m create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreAlertObject.h create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreAlertObject.m create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreAlertOperation.h create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreAlertOperation.m create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertAnimationDelegateProtocol.h create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertDelegateProtocol.h create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertObject.h create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertObject.m create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.h create mode 100644 MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.m create mode 100644 MVMCore/MVMCore/Categories/NSArray+MFConvenience.h create mode 100644 MVMCore/MVMCore/Categories/NSArray+MFConvenience.m create mode 100644 MVMCore/MVMCore/Categories/NSDecimalNumber+MFConvenience.h create mode 100644 MVMCore/MVMCore/Categories/NSDecimalNumber+MFConvenience.m create mode 100644 MVMCore/MVMCore/Categories/NSDictionary+MFConvenience.h create mode 100644 MVMCore/MVMCore/Categories/NSDictionary+MFConvenience.m create mode 100644 MVMCore/MVMCore/Categories/UIFont+MFSpacing.h create mode 100644 MVMCore/MVMCore/Categories/UIFont+MFSpacing.m create mode 100644 MVMCore/MVMCore/Categories/UILabel+MFCustom.h create mode 100644 MVMCore/MVMCore/Categories/UILabel+MFCustom.m create mode 100644 MVMCore/MVMCore/Constants/MVMCoreConstants.h create mode 100644 MVMCore/MVMCore/Constants/MVMCoreConstants.m create mode 100644 MVMCore/MVMCore/Constants/MVMCoreErrorConstants.h create mode 100644 MVMCore/MVMCore/Constants/MVMCoreErrorConstants.m create mode 100644 MVMCore/MVMCore/Constants/MVMCoreHardcodedStringsConstants.h create mode 100644 MVMCore/MVMCore/Constants/MVMCoreHardcodedStringsConstants.m create mode 100644 MVMCore/MVMCore/Constants/MVMCoreJSONConstants.h create mode 100644 MVMCore/MVMCore/Constants/MVMCoreJSONConstants.m create mode 100644 MVMCore/MVMCore/EmbeddedLibraries/Reachability/Reachability.h create mode 100644 MVMCore/MVMCore/EmbeddedLibraries/Reachability/Reachability.m create mode 100644 MVMCore/MVMCore/Info.plist create mode 100644 MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeAuthObject.h create mode 100644 MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeAuthObject.m create mode 100644 MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeUrlObject.h create mode 100644 MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeUrlObject.m create mode 100644 MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeHandler.h create mode 100644 MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeHandler.m create mode 100644 MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeOperation.h create mode 100644 MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeOperation.m create mode 100644 MVMCore/MVMCore/LoadHandling/FreeBee/freebee.json create mode 100644 MVMCore/MVMCore/LoadHandling/FreeBee/freebeelaunchApp.json create mode 100644 MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayDelegateProtocol.h create mode 100644 MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayHandler.h create mode 100644 MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayHandler.m create mode 100644 MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingViewControllerProtocol.h create mode 100644 MVMCore/MVMCore/LoadHandling/MVMCoreLoadDelegateProtocol.h create mode 100644 MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.h create mode 100644 MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.m create mode 100644 MVMCore/MVMCore/LoadHandling/MVMCoreLoadObject.h create mode 100644 MVMCore/MVMCore/LoadHandling/MVMCoreLoadObject.m create mode 100644 MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.h create mode 100644 MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.m create mode 100644 MVMCore/MVMCore/LoadHandling/MVMCoreRequestParameters.h create mode 100644 MVMCore/MVMCore/LoadHandling/MVMCoreRequestParameters.m create mode 100644 MVMCore/MVMCore/LoadHandling/SSLPinning/Certificates/DigiCertGlobalRootCA.cer create mode 100644 MVMCore/MVMCore/LoadHandling/SSLPinning/Certificates/VeriSignClass3PublicPrimaryCertificationAuthority-G5.cer create mode 100644 MVMCore/MVMCore/LoadHandling/SSLPinning/MFSSLPinningHandler.h create mode 100644 MVMCore/MVMCore/LoadHandling/SSLPinning/MFSSLPinningHandler.m create mode 100644 MVMCore/MVMCore/LoadHandling/SSLPinning/MFURLSessionChallengeResult.h create mode 100644 MVMCore/MVMCore/LoadHandling/SSLPinning/MFURLSessionChallengeResult.m create mode 100644 MVMCore/MVMCore/MVMCore.h create mode 100644 MVMCore/MVMCore/MainProtocols/MVMCoreGlobalLoadProtocol.h create mode 100644 MVMCore/MVMCore/MainProtocols/MVMCoreLoggingDelegateProtocol.h create mode 100644 MVMCore/MVMCore/MainProtocols/MVMCoreViewControllerProtocol.h create mode 100644 MVMCore/MVMCore/MainProtocols/MVMCoreViewManagerProtocol.h create mode 100644 MVMCore/MVMCore/MainProtocols/MVMCoreViewProtocol.h create mode 100644 MVMCore/MVMCore/MainProtocols/MobileFirstProtocol.h create mode 100644 MVMCore/MVMCore/MainStructure/MVMCoreMainSplitViewProtocol.h create mode 100644 MVMCore/MVMCore/MainStructure/MVMCoreTopAlertView.h create mode 100644 MVMCore/MVMCore/MainStructure/MVMCoreTopAlertView.m create mode 100644 MVMCore/MVMCore/MainStructure/PanelProtocol.h create mode 100644 MVMCore/MVMCore/OtherHandlers/MVMCoreCache.h create mode 100644 MVMCore/MVMCore/OtherHandlers/MVMCoreCache.m create mode 100644 MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.h create mode 100644 MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.m create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.h create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.m create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationObject.h create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationObject.m create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.h create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCorePresentAnimationOperation.h create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCorePresentAnimationOperation.m create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCorePresentViewControllerOperation.h create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCorePresentViewControllerOperation.m create mode 100644 MVMCore/MVMCore/PresentationHandling/MVMCorePresentationDelegateProtocol.h create mode 100644 MVMCore/MVMCore/Session/MVMCoreSessionObject.h create mode 100644 MVMCore/MVMCore/Session/MVMCoreSessionObject.m create mode 100644 MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.h create mode 100644 MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.m create mode 100644 MVMCore/MVMCore/Singletons/MVMCoreObject.h create mode 100644 MVMCore/MVMCore/Singletons/MVMCoreObject.m create mode 100644 MVMCore/MVMCore/Strings/en.lproj/Localizable.strings create mode 100644 MVMCore/MVMCore/Utility/HardCodedServerResponse/MFHardCodedServerResponse.h create mode 100644 MVMCore/MVMCore/Utility/HardCodedServerResponse/MFHardCodedServerResponse.m create mode 100644 MVMCore/MVMCore/Utility/HardCodedServerResponse/canned.json create mode 100644 MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.h create mode 100644 MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.m create mode 100644 MVMCore/MVMCore/Utility/Helpers/MVMCoreDispatchUtility.h create mode 100644 MVMCore/MVMCore/Utility/Helpers/MVMCoreDispatchUtility.m create mode 100644 MVMCore/MVMCore/Utility/Helpers/MVMCoreGetterUtility.h create mode 100644 MVMCore/MVMCore/Utility/Helpers/MVMCoreGetterUtility.m create mode 100644 MVMCore/MVMCore/Utility/MVMCoreErrorObject.h create mode 100644 MVMCore/MVMCore/Utility/MVMCoreErrorObject.m create mode 100644 MVMCore/MVMCore/Utility/MVMCoreOperation.h create mode 100644 MVMCore/MVMCore/Utility/MVMCoreOperation.m create mode 100644 MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.h create mode 100644 MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.m create mode 100644 MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerNibMappingObject.h create mode 100644 MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerNibMappingObject.m create mode 100644 MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerProgrammaticMappingObject.h create mode 100644 MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerProgrammaticMappingObject.m create mode 100644 MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerStoryBoardMappingObject.h create mode 100644 MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerStoryBoardMappingObject.m diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..310236e865249046a339ed345b68fdd5d0a42c45 GIT binary patch literal 6148 zcmeHKyQ%^~3{AX(!fh-q=NJ5gLD%wDg0+QWBMO4nTlrmnnkNq~%4#Fn$O$B8CX+ME zo?^2R5uKja6OoaK4B>`yvCuU;H}6<4GYW*`j)NR!b6y=|KJrm@YIb@M#5Zf^iix literal 0 HcmV?d00001 diff --git a/MVMCore/MVMCore.xcodeproj/project.pbxproj b/MVMCore/MVMCore.xcodeproj/project.pbxproj new file mode 100644 index 0000000..93f65da --- /dev/null +++ b/MVMCore/MVMCore.xcodeproj/project.pbxproj @@ -0,0 +1,1018 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 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, ); }; }; + 881D26961FCC9D180079C521 /* MVMCoreOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 881D26921FCC9D180079C521 /* MVMCoreOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8876D5CE1FB50A9E00EB2E3D /* MVMCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 8876D5CC1FB50A9E00EB2E3D /* MVMCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8876D5E81FB50AB000EB2E3D /* NSArray+MFConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 8876D5DA1FB50AB000EB2E3D /* NSArray+MFConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8876D5E91FB50AB000EB2E3D /* NSArray+MFConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 8876D5DB1FB50AB000EB2E3D /* NSArray+MFConvenience.m */; }; + 8876D5EA1FB50AB000EB2E3D /* NSDecimalNumber+MFConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 8876D5DC1FB50AB000EB2E3D /* NSDecimalNumber+MFConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8876D5EB1FB50AB000EB2E3D /* NSDecimalNumber+MFConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 8876D5DD1FB50AB000EB2E3D /* NSDecimalNumber+MFConvenience.m */; }; + 8876D5EC1FB50AB000EB2E3D /* NSDictionary+MFConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 8876D5DE1FB50AB000EB2E3D /* NSDictionary+MFConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8876D5ED1FB50AB000EB2E3D /* NSDictionary+MFConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 8876D5DF1FB50AB000EB2E3D /* NSDictionary+MFConvenience.m */; }; + 8876D5F21FB50AB000EB2E3D /* UIFont+MFSpacing.h in Headers */ = {isa = PBXBuildFile; fileRef = 8876D5E41FB50AB000EB2E3D /* UIFont+MFSpacing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8876D5F31FB50AB000EB2E3D /* UIFont+MFSpacing.m in Sources */ = {isa = PBXBuildFile; fileRef = 8876D5E51FB50AB000EB2E3D /* UIFont+MFSpacing.m */; }; + 8876D5F41FB50AB000EB2E3D /* UILabel+MFCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = 8876D5E61FB50AB000EB2E3D /* UILabel+MFCustom.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8876D5F51FB50AB000EB2E3D /* UILabel+MFCustom.m in Sources */ = {isa = PBXBuildFile; fileRef = 8876D5E71FB50AB000EB2E3D /* UILabel+MFCustom.m */; }; + 88D1FBE11FCCCBA100338A3A /* MVMCoreMainSplitViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 88D1FBDF1FCCCADC00338A3A /* MVMCoreMainSplitViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 88D1FBE21FCCCBA500338A3A /* PanelProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 88D1FBE01FCCCB2C00338A3A /* PanelProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AF26DDAE1FCE6A37004E8F65 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = AF26DDB01FCE6A37004E8F65 /* Localizable.strings */; }; + AF26DDB51FCE7C76004E8F65 /* VeriSignClass3PublicPrimaryCertificationAuthority-G5.cer in Resources */ = {isa = PBXBuildFile; fileRef = AF26DDB31FCE7C76004E8F65 /* VeriSignClass3PublicPrimaryCertificationAuthority-G5.cer */; }; + AF26DDB71FCE802D004E8F65 /* DigiCertGlobalRootCA.cer in Resources */ = {isa = PBXBuildFile; fileRef = AF26DDB61FCE802D004E8F65 /* DigiCertGlobalRootCA.cer */; }; + 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, ); }; }; + AF43A57C1FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A57A1FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.m */; }; + AF43A5831FBB66DE008E9347 /* MVMCoreConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A5811FBB66DE008E9347 /* MVMCoreConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AF43A5841FBB66DE008E9347 /* MVMCoreConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A5821FBB66DE008E9347 /* MVMCoreConstants.m */; }; + AF43A5871FBB67D6008E9347 /* MVMCoreActionUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A5851FBB67D6008E9347 /* MVMCoreActionUtility.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AF43A5881FBB67D6008E9347 /* MVMCoreActionUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A5861FBB67D6008E9347 /* MVMCoreActionUtility.m */; }; + AF43A6F41FBCE21D008E9347 /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = AF43A6F31FBCE21D008E9347 /* libsqlite3.0.tbd */; }; + AF43A6FD1FBE2F65008E9347 /* Reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A6FA1FBE2F2A008E9347 /* Reachability.h */; }; + 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, ); }; }; + AF43A7201FC5D2BA008E9347 /* MVMCoreViewManagerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A71F1FC5D2BA008E9347 /* MVMCoreViewManagerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AF43A7271FC5E80E008E9347 /* MVMCoreTopAlertView.h in Headers */ = {isa = PBXBuildFile; fileRef = AF43A7251FC5E80E008E9347 /* MVMCoreTopAlertView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AF43A7281FC5E80E008E9347 /* MVMCoreTopAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = AF43A7261FC5E80E008E9347 /* MVMCoreTopAlertView.m */; }; + 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 */; }; + 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, ); }; }; + AFBB96531FBA3A570008D868 /* MVMCoreLoadHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96391FBA3A550008D868 /* MVMCoreLoadHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB96561FBA3A570008D868 /* freebee.json in Resources */ = {isa = PBXBuildFile; fileRef = AFBB963D1FBA3A550008D868 /* freebee.json */; }; + AFBB96571FBA3A570008D868 /* FreeBeeAuthObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB963E1FBA3A560008D868 /* FreeBeeAuthObject.h */; }; + AFBB96581FBA3A570008D868 /* FreeBeeAuthObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB963F1FBA3A560008D868 /* FreeBeeAuthObject.m */; }; + AFBB96591FBA3A570008D868 /* freebeelaunchApp.json in Resources */ = {isa = PBXBuildFile; fileRef = AFBB96401FBA3A560008D868 /* freebeelaunchApp.json */; }; + AFBB965A1FBA3A570008D868 /* FreeBeeUrlObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96411FBA3A560008D868 /* FreeBeeUrlObject.h */; }; + AFBB965B1FBA3A570008D868 /* FreeBeeUrlObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96421FBA3A560008D868 /* FreeBeeUrlObject.m */; }; + AFBB965C1FBA3A570008D868 /* MFFreebeeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96431FBA3A560008D868 /* MFFreebeeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB965D1FBA3A570008D868 /* MFFreebeeHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96441FBA3A560008D868 /* MFFreebeeHandler.m */; }; + AFBB965E1FBA3A570008D868 /* MFFreebeeOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96451FBA3A560008D868 /* MFFreebeeOperation.h */; }; + AFBB965F1FBA3A570008D868 /* MFFreebeeOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96461FBA3A560008D868 /* MFFreebeeOperation.m */; }; + AFBB96601FBA3A570008D868 /* MVMCoreLoadObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96471FBA3A560008D868 /* MVMCoreLoadObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB96611FBA3A570008D868 /* MVMCoreLoadObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96481FBA3A560008D868 /* MVMCoreLoadObject.m */; }; + AFBB96621FBA3A570008D868 /* MVMCoreRequestParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96491FBA3A560008D868 /* MVMCoreRequestParameters.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB96631FBA3A570008D868 /* MVMCoreLoadRequestOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB964A1FBA3A560008D868 /* MVMCoreLoadRequestOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB96641FBA3A570008D868 /* MVMCoreLoadHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB964B1FBA3A560008D868 /* MVMCoreLoadHandler.m */; }; + AFBB96651FBA3A570008D868 /* MFSSLPinningHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB964D1FBA3A560008D868 /* MFSSLPinningHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB96661FBA3A570008D868 /* MFSSLPinningHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB964E1FBA3A560008D868 /* MFSSLPinningHandler.m */; }; + AFBB96671FBA3A570008D868 /* MFURLSessionChallengeResult.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB964F1FBA3A560008D868 /* MFURLSessionChallengeResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB96681FBA3A570008D868 /* MFURLSessionChallengeResult.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96501FBA3A560008D868 /* MFURLSessionChallengeResult.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 */; }; + AFBB969A1FBA3A9A0008D868 /* MVMCoreAlertDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB967D1FBA3A9A0008D868 /* MVMCoreAlertDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB969B1FBA3A9A0008D868 /* MVMCoreAlertHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB967E1FBA3A9A0008D868 /* MVMCoreAlertHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB969C1FBA3A9A0008D868 /* MVMCoreAlertHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB967F1FBA3A9A0008D868 /* MVMCoreAlertHandler.m */; }; + AFBB969D1FBA3A9A0008D868 /* MVMCoreAlertObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96801FBA3A9A0008D868 /* MVMCoreAlertObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB969E1FBA3A9A0008D868 /* MVMCoreAlertObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96811FBA3A9A0008D868 /* MVMCoreAlertObject.m */; }; + AFBB969F1FBA3A9A0008D868 /* MVMCoreAlertOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96821FBA3A9A0008D868 /* MVMCoreAlertOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB96A01FBA3A9A0008D868 /* MVMCoreAlertOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96831FBA3A9A0008D868 /* MVMCoreAlertOperation.m */; }; + AFBB96A11FBA3A9A0008D868 /* MVMCoreTopAlertAnimationDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96841FBA3A9A0008D868 /* MVMCoreTopAlertAnimationDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB96A21FBA3A9A0008D868 /* MVMCoreTopAlertDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96851FBA3A9A0008D868 /* MVMCoreTopAlertDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB96A31FBA3A9A0008D868 /* MVMCoreTopAlertObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96861FBA3A9A0008D868 /* MVMCoreTopAlertObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB96A41FBA3A9A0008D868 /* MVMCoreTopAlertObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96871FBA3A9A0008D868 /* MVMCoreTopAlertObject.m */; }; + AFBB96A51FBA3A9A0008D868 /* MVMCoreTopAlertOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96881FBA3A9A0008D868 /* MVMCoreTopAlertOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFBB96A61FBA3A9A0008D868 /* MVMCoreTopAlertOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96891FBA3A9A0008D868 /* MVMCoreTopAlertOperation.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 */; }; + AFBB96EB1FBA4A260008D868 /* canned.json in Resources */ = {isa = PBXBuildFile; fileRef = AFBB96E81FBA4A260008D868 /* canned.json */; }; + AFBB96EC1FBA4A260008D868 /* MFHardCodedServerResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = AFBB96E91FBA4A260008D868 /* MFHardCodedServerResponse.h */; }; + AFBB96ED1FBA4A260008D868 /* MFHardCodedServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = AFBB96EA1FBA4A260008D868 /* MFHardCodedServerResponse.m */; }; + AFED77A11FCCA29400BAE689 /* MVMCoreViewControllerNibMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AFED77991FCCA29300BAE689 /* MVMCoreViewControllerNibMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFED77A21FCCA29400BAE689 /* MVMCoreViewControllerMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AFED779A1FCCA29300BAE689 /* MVMCoreViewControllerMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AFED77A31FCCA29400BAE689 /* MVMCoreViewControllerNibMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AFED779B1FCCA29300BAE689 /* MVMCoreViewControllerNibMappingObject.m */; }; + AFED77A41FCCA29400BAE689 /* MVMCoreViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = AFED779C1FCCA29300BAE689 /* MVMCoreViewControllerMappingObject.m */; }; + AFED77A51FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = AFED779D1FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 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, ); }; }; + AFEEE81B1FCDEBFA00B5EDD0 /* MobileFirstProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFEEE81A1FCDEBFA00B5EDD0 /* MobileFirstProtocol.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 */; }; + AFFCFA681FCCC0D700FD0730 /* MVMCoreLoadingViewControllerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AFFCFA641FCCC0D600FD0730 /* MVMCoreLoadingViewControllerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 30349BEF1FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreSessionTimeHandler.h; sourceTree = ""; }; + 30349BF01FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreSessionTimeHandler.m; sourceTree = ""; }; + 881D268F1FCC9D180079C521 /* MVMCoreErrorObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreErrorObject.m; sourceTree = ""; }; + 881D26901FCC9D180079C521 /* MVMCoreOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreOperation.m; sourceTree = ""; }; + 881D26911FCC9D180079C521 /* MVMCoreErrorObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreErrorObject.h; sourceTree = ""; }; + 881D26921FCC9D180079C521 /* MVMCoreOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreOperation.h; sourceTree = ""; }; + 8876D5C91FB50A9E00EB2E3D /* MVMCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MVMCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8876D5CC1FB50A9E00EB2E3D /* MVMCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCore.h; sourceTree = ""; }; + 8876D5CD1FB50A9E00EB2E3D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8876D5DA1FB50AB000EB2E3D /* NSArray+MFConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+MFConvenience.h"; sourceTree = ""; }; + 8876D5DB1FB50AB000EB2E3D /* NSArray+MFConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+MFConvenience.m"; sourceTree = ""; }; + 8876D5DC1FB50AB000EB2E3D /* NSDecimalNumber+MFConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDecimalNumber+MFConvenience.h"; sourceTree = ""; }; + 8876D5DD1FB50AB000EB2E3D /* NSDecimalNumber+MFConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDecimalNumber+MFConvenience.m"; sourceTree = ""; }; + 8876D5DE1FB50AB000EB2E3D /* NSDictionary+MFConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+MFConvenience.h"; sourceTree = ""; }; + 8876D5DF1FB50AB000EB2E3D /* NSDictionary+MFConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+MFConvenience.m"; sourceTree = ""; }; + 8876D5E41FB50AB000EB2E3D /* UIFont+MFSpacing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIFont+MFSpacing.h"; sourceTree = ""; }; + 8876D5E51FB50AB000EB2E3D /* UIFont+MFSpacing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIFont+MFSpacing.m"; sourceTree = ""; }; + 8876D5E61FB50AB000EB2E3D /* UILabel+MFCustom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UILabel+MFCustom.h"; sourceTree = ""; }; + 8876D5E71FB50AB000EB2E3D /* UILabel+MFCustom.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UILabel+MFCustom.m"; sourceTree = ""; }; + 88D1FBDF1FCCCADC00338A3A /* MVMCoreMainSplitViewProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreMainSplitViewProtocol.h; sourceTree = ""; }; + 88D1FBE01FCCCB2C00338A3A /* PanelProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PanelProtocol.h; sourceTree = ""; }; + AF26DDAF1FCE6A37004E8F65 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + AF26DDB31FCE7C76004E8F65 /* VeriSignClass3PublicPrimaryCertificationAuthority-G5.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = "VeriSignClass3PublicPrimaryCertificationAuthority-G5.cer"; sourceTree = ""; }; + AF26DDB61FCE802D004E8F65 /* DigiCertGlobalRootCA.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = DigiCertGlobalRootCA.cer; sourceTree = ""; }; + AF43A5751FBA5B7C008E9347 /* MVMCoreJSONConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreJSONConstants.h; sourceTree = ""; }; + AF43A5761FBA5B7C008E9347 /* MVMCoreJSONConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreJSONConstants.m; sourceTree = ""; }; + AF43A5791FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreHardcodedStringsConstants.h; sourceTree = ""; }; + AF43A57A1FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreHardcodedStringsConstants.m; sourceTree = ""; }; + AF43A5811FBB66DE008E9347 /* MVMCoreConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreConstants.h; sourceTree = ""; }; + AF43A5821FBB66DE008E9347 /* MVMCoreConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreConstants.m; sourceTree = ""; }; + AF43A5851FBB67D6008E9347 /* MVMCoreActionUtility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreActionUtility.h; sourceTree = ""; }; + AF43A5861FBB67D6008E9347 /* MVMCoreActionUtility.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreActionUtility.m; sourceTree = ""; }; + AF43A5BF1FBB76C3008E9347 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + AF43A5C01FBB76D5008E9347 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + AF43A6F31FBCE21D008E9347 /* libsqlite3.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.0.tbd; path = usr/lib/libsqlite3.0.tbd; sourceTree = SDKROOT; }; + AF43A6FA1FBE2F2A008E9347 /* Reachability.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; }; + AF43A6FB1FBE2F2A008E9347 /* Reachability.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; }; + 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 = ""; }; + AF43A7041FC4D7A2008E9347 /* MVMCoreObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreObject.h; sourceTree = ""; }; + AF43A7051FC4D7A2008E9347 /* MVMCoreObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreObject.m; sourceTree = ""; }; + AF43A7081FC4F415008E9347 /* MVMCoreCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreCache.m; sourceTree = ""; }; + AF43A7091FC4F415008E9347 /* MVMCoreCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreCache.h; sourceTree = ""; }; + AF43A71A1FC5BEBB008E9347 /* MVMCoreViewControllerProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewControllerProtocol.h; sourceTree = ""; }; + AF43A71F1FC5D2BA008E9347 /* MVMCoreViewManagerProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewManagerProtocol.h; sourceTree = ""; }; + AF43A7251FC5E80E008E9347 /* MVMCoreTopAlertView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreTopAlertView.h; sourceTree = ""; }; + AF43A7261FC5E80E008E9347 /* MVMCoreTopAlertView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreTopAlertView.m; sourceTree = ""; }; + AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewProtocol.h; sourceTree = ""; }; + AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreSessionObject.h; sourceTree = ""; }; + AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreSessionObject.m; sourceTree = ""; }; + AFBB96321FBA34310008D868 /* MVMCoreErrorConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreErrorConstants.h; sourceTree = ""; }; + AFBB96331FBA34310008D868 /* MVMCoreErrorConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreErrorConstants.m; sourceTree = ""; }; + AFBB96371FBA39E70008D868 /* MVMCoreLoadDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadDelegateProtocol.h; sourceTree = ""; }; + AFBB96391FBA3A550008D868 /* MVMCoreLoadHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadHandler.h; sourceTree = ""; }; + AFBB963D1FBA3A550008D868 /* freebee.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = freebee.json; sourceTree = ""; }; + AFBB963E1FBA3A560008D868 /* FreeBeeAuthObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FreeBeeAuthObject.h; sourceTree = ""; }; + AFBB963F1FBA3A560008D868 /* FreeBeeAuthObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FreeBeeAuthObject.m; sourceTree = ""; }; + AFBB96401FBA3A560008D868 /* freebeelaunchApp.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = freebeelaunchApp.json; sourceTree = ""; }; + AFBB96411FBA3A560008D868 /* FreeBeeUrlObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FreeBeeUrlObject.h; sourceTree = ""; }; + AFBB96421FBA3A560008D868 /* FreeBeeUrlObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FreeBeeUrlObject.m; sourceTree = ""; }; + AFBB96431FBA3A560008D868 /* MFFreebeeHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFFreebeeHandler.h; sourceTree = ""; }; + AFBB96441FBA3A560008D868 /* MFFreebeeHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFFreebeeHandler.m; sourceTree = ""; }; + AFBB96451FBA3A560008D868 /* MFFreebeeOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFFreebeeOperation.h; sourceTree = ""; }; + AFBB96461FBA3A560008D868 /* MFFreebeeOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFFreebeeOperation.m; sourceTree = ""; }; + AFBB96471FBA3A560008D868 /* MVMCoreLoadObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadObject.h; sourceTree = ""; }; + AFBB96481FBA3A560008D868 /* MVMCoreLoadObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreLoadObject.m; sourceTree = ""; }; + AFBB96491FBA3A560008D868 /* MVMCoreRequestParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreRequestParameters.h; sourceTree = ""; }; + AFBB964A1FBA3A560008D868 /* MVMCoreLoadRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadRequestOperation.h; sourceTree = ""; }; + AFBB964B1FBA3A560008D868 /* MVMCoreLoadHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreLoadHandler.m; sourceTree = ""; }; + AFBB964D1FBA3A560008D868 /* MFSSLPinningHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFSSLPinningHandler.h; sourceTree = ""; }; + AFBB964E1FBA3A560008D868 /* MFSSLPinningHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFSSLPinningHandler.m; sourceTree = ""; }; + AFBB964F1FBA3A560008D868 /* MFURLSessionChallengeResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFURLSessionChallengeResult.h; sourceTree = ""; }; + AFBB96501FBA3A560008D868 /* MFURLSessionChallengeResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFURLSessionChallengeResult.m; sourceTree = ""; }; + AFBB96511FBA3A560008D868 /* MVMCoreRequestParameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreRequestParameters.m; sourceTree = ""; }; + AFBB96521FBA3A570008D868 /* MVMCoreLoadRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreLoadRequestOperation.m; sourceTree = ""; }; + AFBB966D1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreDismissViewControllerOperation.h; sourceTree = ""; }; + AFBB966E1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreDismissViewControllerOperation.m; sourceTree = ""; }; + AFBB966F1FBA3A9A0008D868 /* MVMCoreNavigationHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreNavigationHandler.h; sourceTree = ""; }; + AFBB96701FBA3A9A0008D868 /* MVMCoreNavigationHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreNavigationHandler.m; sourceTree = ""; }; + AFBB96711FBA3A9A0008D868 /* MVMCoreNavigationObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreNavigationObject.h; sourceTree = ""; }; + AFBB96721FBA3A9A0008D868 /* MVMCoreNavigationObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreNavigationObject.m; sourceTree = ""; }; + AFBB96731FBA3A9A0008D868 /* MVMCoreNavigationOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreNavigationOperation.h; sourceTree = ""; }; + AFBB96741FBA3A9A0008D868 /* MVMCoreNavigationOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreNavigationOperation.m; sourceTree = ""; }; + AFBB96751FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCorePresentAnimationOperation.h; sourceTree = ""; }; + AFBB96761FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCorePresentAnimationOperation.m; sourceTree = ""; }; + AFBB96771FBA3A9A0008D868 /* MVMCorePresentationDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCorePresentationDelegateProtocol.h; sourceTree = ""; }; + AFBB96781FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCorePresentViewControllerOperation.h; sourceTree = ""; }; + AFBB96791FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCorePresentViewControllerOperation.m; sourceTree = ""; }; + AFBB967B1FBA3A9A0008D868 /* MVMCoreAlertController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreAlertController.h; sourceTree = ""; }; + AFBB967C1FBA3A9A0008D868 /* MVMCoreAlertController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreAlertController.m; sourceTree = ""; }; + AFBB967D1FBA3A9A0008D868 /* MVMCoreAlertDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreAlertDelegateProtocol.h; sourceTree = ""; }; + AFBB967E1FBA3A9A0008D868 /* MVMCoreAlertHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreAlertHandler.h; sourceTree = ""; }; + AFBB967F1FBA3A9A0008D868 /* MVMCoreAlertHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreAlertHandler.m; sourceTree = ""; }; + AFBB96801FBA3A9A0008D868 /* MVMCoreAlertObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreAlertObject.h; sourceTree = ""; }; + AFBB96811FBA3A9A0008D868 /* MVMCoreAlertObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreAlertObject.m; sourceTree = ""; }; + AFBB96821FBA3A9A0008D868 /* MVMCoreAlertOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreAlertOperation.h; sourceTree = ""; }; + AFBB96831FBA3A9A0008D868 /* MVMCoreAlertOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreAlertOperation.m; sourceTree = ""; }; + AFBB96841FBA3A9A0008D868 /* MVMCoreTopAlertAnimationDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreTopAlertAnimationDelegateProtocol.h; sourceTree = ""; }; + AFBB96851FBA3A9A0008D868 /* MVMCoreTopAlertDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreTopAlertDelegateProtocol.h; sourceTree = ""; }; + AFBB96861FBA3A9A0008D868 /* MVMCoreTopAlertObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreTopAlertObject.h; sourceTree = ""; }; + AFBB96871FBA3A9A0008D868 /* MVMCoreTopAlertObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreTopAlertObject.m; sourceTree = ""; }; + AFBB96881FBA3A9A0008D868 /* MVMCoreTopAlertOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreTopAlertOperation.h; sourceTree = ""; }; + AFBB96891FBA3A9A0008D868 /* MVMCoreTopAlertOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreTopAlertOperation.m; sourceTree = ""; }; + AFBB96AC1FBA3B590008D868 /* MVMCoreDispatchUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreDispatchUtility.h; sourceTree = ""; }; + AFBB96AD1FBA3B590008D868 /* MVMCoreDispatchUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreDispatchUtility.m; sourceTree = ""; }; + AFBB96AE1FBA3B590008D868 /* MVMCoreGetterUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreGetterUtility.h; sourceTree = ""; }; + AFBB96AF1FBA3B590008D868 /* MVMCoreGetterUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreGetterUtility.m; sourceTree = ""; }; + AFBB96B51FBA3CEC0008D868 /* MVMCoreActionDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreActionDelegateProtocol.h; sourceTree = ""; }; + AFBB96B61FBA3CEC0008D868 /* MVMCoreActionHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreActionHandler.h; sourceTree = ""; }; + AFBB96B71FBA3CEC0008D868 /* MVMCoreActionHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreActionHandler.m; sourceTree = ""; }; + AFBB96D21FBA44420008D868 /* VZWAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VZWAuthentication.framework; path = ../../SharedFrameworks/VZWAuthentication.framework; sourceTree = ""; }; + AFBB96E81FBA4A260008D868 /* canned.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = canned.json; sourceTree = ""; }; + AFBB96E91FBA4A260008D868 /* MFHardCodedServerResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFHardCodedServerResponse.h; sourceTree = ""; }; + AFBB96EA1FBA4A260008D868 /* MFHardCodedServerResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFHardCodedServerResponse.m; sourceTree = ""; }; + AFED77991FCCA29300BAE689 /* MVMCoreViewControllerNibMappingObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewControllerNibMappingObject.h; sourceTree = ""; }; + AFED779A1FCCA29300BAE689 /* MVMCoreViewControllerMappingObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewControllerMappingObject.h; sourceTree = ""; }; + AFED779B1FCCA29300BAE689 /* MVMCoreViewControllerNibMappingObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreViewControllerNibMappingObject.m; sourceTree = ""; }; + AFED779C1FCCA29300BAE689 /* MVMCoreViewControllerMappingObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreViewControllerMappingObject.m; sourceTree = ""; }; + AFED779D1FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewControllerProgrammaticMappingObject.h; sourceTree = ""; }; + AFED779E1FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreViewControllerStoryBoardMappingObject.m; sourceTree = ""; }; + AFED779F1FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreViewControllerProgrammaticMappingObject.m; sourceTree = ""; }; + AFED77A01FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewControllerStoryBoardMappingObject.h; sourceTree = ""; }; + AFEEE8181FCDDE6B00B5EDD0 /* MVMCoreLoggingDelegateProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoggingDelegateProtocol.h; sourceTree = ""; }; + AFEEE81A1FCDEBFA00B5EDD0 /* MobileFirstProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MobileFirstProtocol.h; sourceTree = ""; }; + AFEEE81C1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoggingHandler.h; sourceTree = ""; }; + AFEEE81D1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreLoggingHandler.m; sourceTree = ""; }; + AFFCFA611FCCC0D600FD0730 /* MVMCoreLoadingOverlayDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadingOverlayDelegateProtocol.h; sourceTree = ""; }; + AFFCFA621FCCC0D600FD0730 /* MVMCoreLoadingOverlayHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadingOverlayHandler.h; sourceTree = ""; }; + AFFCFA631FCCC0D600FD0730 /* MVMCoreLoadingOverlayHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreLoadingOverlayHandler.m; sourceTree = ""; }; + AFFCFA641FCCC0D600FD0730 /* MVMCoreLoadingViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreLoadingViewControllerProtocol.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8876D5C51FB50A9E00EB2E3D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AF43A6F41FBCE21D008E9347 /* libsqlite3.0.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8876D5BF1FB50A9E00EB2E3D = { + isa = PBXGroup; + children = ( + 8876D5CB1FB50A9E00EB2E3D /* MVMCore */, + 8876D5CA1FB50A9E00EB2E3D /* Products */, + AFBB96D11FBA44410008D868 /* Frameworks */, + ); + sourceTree = ""; + }; + 8876D5CA1FB50A9E00EB2E3D /* Products */ = { + isa = PBXGroup; + children = ( + 8876D5C91FB50A9E00EB2E3D /* MVMCore.framework */, + ); + name = Products; + sourceTree = ""; + }; + 8876D5CB1FB50A9E00EB2E3D /* MVMCore */ = { + isa = PBXGroup; + children = ( + AFBB96311FBA341E0008D868 /* Constants */, + 8876D5D41FB50AAB00EB2E3D /* Utility */, + AF43A7191FC5BE9E008E9347 /* MainProtocols */, + AFBB96B41FBA3CEC0008D868 /* ActionHandling */, + AFBB967A1FBA3A9A0008D868 /* AlertHandling */, + AFBB966B1FBA3A9A0008D868 /* PresentationHandling */, + AFBB96361FBA39E70008D868 /* LoadHandling */, + AFBB96131FBA26650008D868 /* ViewControllerMapping */, + 8876D5D91FB50AB000EB2E3D /* Categories */, + AFBB96DE1FBA48CE0008D868 /* Singletons */, + AF43A70C1FC4F42B008E9347 /* Session */, + AFBB96D41FBA48240008D868 /* OtherHandlers */, + AF43A5941FBB700D008E9347 /* MainStructure */, + AF26DDAB1FCE5CF2004E8F65 /* Strings */, + 8876D5CC1FB50A9E00EB2E3D /* MVMCore.h */, + 8876D5CD1FB50A9E00EB2E3D /* Info.plist */, + ); + path = MVMCore; + sourceTree = ""; + }; + 8876D5D41FB50AAB00EB2E3D /* Utility */ = { + isa = PBXGroup; + children = ( + 881D26911FCC9D180079C521 /* MVMCoreErrorObject.h */, + 881D268F1FCC9D180079C521 /* MVMCoreErrorObject.m */, + 881D26921FCC9D180079C521 /* MVMCoreOperation.h */, + 881D26901FCC9D180079C521 /* MVMCoreOperation.m */, + AFBB96E71FBA4A260008D868 /* HardCodedServerResponse */, + AFBB96AB1FBA3B590008D868 /* Helpers */, + ); + path = Utility; + sourceTree = ""; + }; + 8876D5D91FB50AB000EB2E3D /* Categories */ = { + isa = PBXGroup; + children = ( + 8876D5DA1FB50AB000EB2E3D /* NSArray+MFConvenience.h */, + 8876D5DB1FB50AB000EB2E3D /* NSArray+MFConvenience.m */, + 8876D5DC1FB50AB000EB2E3D /* NSDecimalNumber+MFConvenience.h */, + 8876D5DD1FB50AB000EB2E3D /* NSDecimalNumber+MFConvenience.m */, + 8876D5DE1FB50AB000EB2E3D /* NSDictionary+MFConvenience.h */, + 8876D5DF1FB50AB000EB2E3D /* NSDictionary+MFConvenience.m */, + 8876D5E41FB50AB000EB2E3D /* UIFont+MFSpacing.h */, + 8876D5E51FB50AB000EB2E3D /* UIFont+MFSpacing.m */, + 8876D5E61FB50AB000EB2E3D /* UILabel+MFCustom.h */, + 8876D5E71FB50AB000EB2E3D /* UILabel+MFCustom.m */, + ); + path = Categories; + sourceTree = ""; + }; + AF26DDAB1FCE5CF2004E8F65 /* Strings */ = { + isa = PBXGroup; + children = ( + AF26DDB01FCE6A37004E8F65 /* Localizable.strings */, + ); + path = Strings; + sourceTree = ""; + }; + AF26DDB11FCE7C76004E8F65 /* Certificates */ = { + isa = PBXGroup; + children = ( + AF26DDB61FCE802D004E8F65 /* DigiCertGlobalRootCA.cer */, + AF26DDB31FCE7C76004E8F65 /* VeriSignClass3PublicPrimaryCertificationAuthority-G5.cer */, + ); + path = Certificates; + sourceTree = ""; + }; + AF43A5941FBB700D008E9347 /* MainStructure */ = { + isa = PBXGroup; + children = ( + 88D1FBE01FCCCB2C00338A3A /* PanelProtocol.h */, + 88D1FBDF1FCCCADC00338A3A /* MVMCoreMainSplitViewProtocol.h */, + AF43A7251FC5E80E008E9347 /* MVMCoreTopAlertView.h */, + AF43A7261FC5E80E008E9347 /* MVMCoreTopAlertView.m */, + ); + path = MainStructure; + sourceTree = ""; + }; + AF43A6FC1FBE2F2A008E9347 /* Reachability */ = { + isa = PBXGroup; + children = ( + AF43A6FA1FBE2F2A008E9347 /* Reachability.h */, + AF43A6FB1FBE2F2A008E9347 /* Reachability.m */, + ); + name = Reachability; + path = MVMCore/EmbeddedLibraries/Reachability; + sourceTree = ""; + }; + AF43A70C1FC4F42B008E9347 /* Session */ = { + isa = PBXGroup; + children = ( + 30349BEF1FCCA78A00546A1E /* MVMCoreSessionTimeHandler.h */, + 30349BF01FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m */, + AF43A74A1FC6109F008E9347 /* MVMCoreSessionObject.h */, + AF43A74B1FC6109F008E9347 /* MVMCoreSessionObject.m */, + ); + path = Session; + sourceTree = ""; + }; + AF43A7191FC5BE9E008E9347 /* MainProtocols */ = { + isa = PBXGroup; + children = ( + AFEEE81A1FCDEBFA00B5EDD0 /* MobileFirstProtocol.h */, + AF43A71A1FC5BEBB008E9347 /* MVMCoreViewControllerProtocol.h */, + AF43A71F1FC5D2BA008E9347 /* MVMCoreViewManagerProtocol.h */, + AF43A7401FC5FA6F008E9347 /* MVMCoreViewProtocol.h */, + AFEEE8181FCDDE6B00B5EDD0 /* MVMCoreLoggingDelegateProtocol.h */, + AF43A7001FC4B227008E9347 /* MVMCoreGlobalLoadProtocol.h */, + ); + path = MainProtocols; + sourceTree = ""; + }; + AFBB96131FBA26650008D868 /* ViewControllerMapping */ = { + isa = PBXGroup; + children = ( + AFED779A1FCCA29300BAE689 /* MVMCoreViewControllerMappingObject.h */, + AFED779C1FCCA29300BAE689 /* MVMCoreViewControllerMappingObject.m */, + AFED77991FCCA29300BAE689 /* MVMCoreViewControllerNibMappingObject.h */, + AFED779B1FCCA29300BAE689 /* MVMCoreViewControllerNibMappingObject.m */, + AFED779D1FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.h */, + AFED779F1FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.m */, + AFED77A01FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.h */, + AFED779E1FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.m */, + ); + path = ViewControllerMapping; + sourceTree = ""; + }; + AFBB96311FBA341E0008D868 /* Constants */ = { + isa = PBXGroup; + children = ( + AFBB96321FBA34310008D868 /* MVMCoreErrorConstants.h */, + AFBB96331FBA34310008D868 /* MVMCoreErrorConstants.m */, + AF43A5751FBA5B7C008E9347 /* MVMCoreJSONConstants.h */, + AF43A5761FBA5B7C008E9347 /* MVMCoreJSONConstants.m */, + AF43A5791FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.h */, + AF43A57A1FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.m */, + AF43A5811FBB66DE008E9347 /* MVMCoreConstants.h */, + AF43A5821FBB66DE008E9347 /* MVMCoreConstants.m */, + ); + path = Constants; + sourceTree = ""; + }; + AFBB96361FBA39E70008D868 /* LoadHandling */ = { + isa = PBXGroup; + children = ( + AFFCFA601FCCC0D600FD0730 /* LoadingOverlay */, + AFBB963C1FBA3A550008D868 /* FreeBee */, + AFBB964C1FBA3A560008D868 /* SSLPinning */, + AFBB96371FBA39E70008D868 /* MVMCoreLoadDelegateProtocol.h */, + AFBB96391FBA3A550008D868 /* MVMCoreLoadHandler.h */, + AFBB964B1FBA3A560008D868 /* MVMCoreLoadHandler.m */, + AFBB964A1FBA3A560008D868 /* MVMCoreLoadRequestOperation.h */, + AFBB96521FBA3A570008D868 /* MVMCoreLoadRequestOperation.m */, + AFBB96471FBA3A560008D868 /* MVMCoreLoadObject.h */, + AFBB96481FBA3A560008D868 /* MVMCoreLoadObject.m */, + AFBB96491FBA3A560008D868 /* MVMCoreRequestParameters.h */, + AFBB96511FBA3A560008D868 /* MVMCoreRequestParameters.m */, + ); + path = LoadHandling; + sourceTree = ""; + }; + AFBB963C1FBA3A550008D868 /* FreeBee */ = { + isa = PBXGroup; + children = ( + AFBB963D1FBA3A550008D868 /* freebee.json */, + AFBB963E1FBA3A560008D868 /* FreeBeeAuthObject.h */, + AFBB963F1FBA3A560008D868 /* FreeBeeAuthObject.m */, + AFBB96401FBA3A560008D868 /* freebeelaunchApp.json */, + AFBB96411FBA3A560008D868 /* FreeBeeUrlObject.h */, + AFBB96421FBA3A560008D868 /* FreeBeeUrlObject.m */, + AFBB96431FBA3A560008D868 /* MFFreebeeHandler.h */, + AFBB96441FBA3A560008D868 /* MFFreebeeHandler.m */, + AFBB96451FBA3A560008D868 /* MFFreebeeOperation.h */, + AFBB96461FBA3A560008D868 /* MFFreebeeOperation.m */, + ); + path = FreeBee; + sourceTree = ""; + }; + AFBB964C1FBA3A560008D868 /* SSLPinning */ = { + isa = PBXGroup; + children = ( + AF26DDB11FCE7C76004E8F65 /* Certificates */, + AFBB964D1FBA3A560008D868 /* MFSSLPinningHandler.h */, + AFBB964E1FBA3A560008D868 /* MFSSLPinningHandler.m */, + AFBB964F1FBA3A560008D868 /* MFURLSessionChallengeResult.h */, + AFBB96501FBA3A560008D868 /* MFURLSessionChallengeResult.m */, + ); + path = SSLPinning; + sourceTree = ""; + }; + AFBB966B1FBA3A9A0008D868 /* PresentationHandling */ = { + isa = PBXGroup; + children = ( + 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 */, + ); + path = PresentationHandling; + sourceTree = ""; + }; + AFBB967A1FBA3A9A0008D868 /* AlertHandling */ = { + isa = PBXGroup; + children = ( + AFBB967B1FBA3A9A0008D868 /* MVMCoreAlertController.h */, + AFBB967C1FBA3A9A0008D868 /* MVMCoreAlertController.m */, + AFBB967D1FBA3A9A0008D868 /* MVMCoreAlertDelegateProtocol.h */, + AFBB967E1FBA3A9A0008D868 /* MVMCoreAlertHandler.h */, + AFBB967F1FBA3A9A0008D868 /* MVMCoreAlertHandler.m */, + AFBB96801FBA3A9A0008D868 /* MVMCoreAlertObject.h */, + AFBB96811FBA3A9A0008D868 /* MVMCoreAlertObject.m */, + AFBB96821FBA3A9A0008D868 /* MVMCoreAlertOperation.h */, + AFBB96831FBA3A9A0008D868 /* MVMCoreAlertOperation.m */, + AFBB96841FBA3A9A0008D868 /* MVMCoreTopAlertAnimationDelegateProtocol.h */, + AFBB96851FBA3A9A0008D868 /* MVMCoreTopAlertDelegateProtocol.h */, + AFBB96861FBA3A9A0008D868 /* MVMCoreTopAlertObject.h */, + AFBB96871FBA3A9A0008D868 /* MVMCoreTopAlertObject.m */, + AFBB96881FBA3A9A0008D868 /* MVMCoreTopAlertOperation.h */, + AFBB96891FBA3A9A0008D868 /* MVMCoreTopAlertOperation.m */, + ); + path = AlertHandling; + sourceTree = ""; + }; + AFBB96AB1FBA3B590008D868 /* Helpers */ = { + isa = PBXGroup; + children = ( + AFBB96AC1FBA3B590008D868 /* MVMCoreDispatchUtility.h */, + AFBB96AD1FBA3B590008D868 /* MVMCoreDispatchUtility.m */, + AFBB96AE1FBA3B590008D868 /* MVMCoreGetterUtility.h */, + AFBB96AF1FBA3B590008D868 /* MVMCoreGetterUtility.m */, + AF43A5851FBB67D6008E9347 /* MVMCoreActionUtility.h */, + AF43A5861FBB67D6008E9347 /* MVMCoreActionUtility.m */, + ); + path = Helpers; + sourceTree = ""; + }; + AFBB96B41FBA3CEC0008D868 /* ActionHandling */ = { + isa = PBXGroup; + children = ( + AFBB96B51FBA3CEC0008D868 /* MVMCoreActionDelegateProtocol.h */, + AFBB96B61FBA3CEC0008D868 /* MVMCoreActionHandler.h */, + AFBB96B71FBA3CEC0008D868 /* MVMCoreActionHandler.m */, + ); + path = ActionHandling; + sourceTree = ""; + }; + AFBB96D11FBA44410008D868 /* Frameworks */ = { + isa = PBXGroup; + children = ( + AF43A6FE1FBE2FEE008E9347 /* SystemConfiguration.framework */, + AF43A6F31FBCE21D008E9347 /* libsqlite3.0.tbd */, + AF43A6FC1FBE2F2A008E9347 /* Reachability */, + AF43A5C01FBB76D5008E9347 /* CoreGraphics.framework */, + AF43A5BF1FBB76C3008E9347 /* UIKit.framework */, + AFBB96D21FBA44420008D868 /* VZWAuthentication.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + AFBB96D41FBA48240008D868 /* OtherHandlers */ = { + isa = PBXGroup; + children = ( + AF43A7091FC4F415008E9347 /* MVMCoreCache.h */, + AF43A7081FC4F415008E9347 /* MVMCoreCache.m */, + AFEEE81C1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.h */, + AFEEE81D1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.m */, + ); + path = OtherHandlers; + sourceTree = ""; + }; + AFBB96DE1FBA48CE0008D868 /* Singletons */ = { + isa = PBXGroup; + children = ( + AF43A7041FC4D7A2008E9347 /* MVMCoreObject.h */, + AF43A7051FC4D7A2008E9347 /* MVMCoreObject.m */, + ); + path = Singletons; + sourceTree = ""; + }; + AFBB96E71FBA4A260008D868 /* HardCodedServerResponse */ = { + isa = PBXGroup; + children = ( + AFBB96E81FBA4A260008D868 /* canned.json */, + AFBB96E91FBA4A260008D868 /* MFHardCodedServerResponse.h */, + AFBB96EA1FBA4A260008D868 /* MFHardCodedServerResponse.m */, + ); + path = HardCodedServerResponse; + sourceTree = ""; + }; + AFFCFA601FCCC0D600FD0730 /* LoadingOverlay */ = { + isa = PBXGroup; + children = ( + AFFCFA611FCCC0D600FD0730 /* MVMCoreLoadingOverlayDelegateProtocol.h */, + AFFCFA621FCCC0D600FD0730 /* MVMCoreLoadingOverlayHandler.h */, + AFFCFA631FCCC0D600FD0730 /* MVMCoreLoadingOverlayHandler.m */, + AFFCFA641FCCC0D600FD0730 /* MVMCoreLoadingViewControllerProtocol.h */, + ); + path = LoadingOverlay; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8876D5C61FB50A9E00EB2E3D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + AFBB96A21FBA3A9A0008D868 /* MVMCoreTopAlertDelegateProtocol.h in Headers */, + AF43A7411FC5FA6F008E9347 /* MVMCoreViewProtocol.h in Headers */, + AFBB96EC1FBA4A260008D868 /* MFHardCodedServerResponse.h in Headers */, + AFBB96B21FBA3B590008D868 /* MVMCoreGetterUtility.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 */, + AFBB96A31FBA3A9A0008D868 /* MVMCoreTopAlertObject.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 */, + 88D1FBE21FCCCBA500338A3A /* PanelProtocol.h in Headers */, + AFBB968D1FBA3A9A0008D868 /* MVMCoreNavigationHandler.h in Headers */, + AFBB96A51FBA3A9A0008D868 /* MVMCoreTopAlertOperation.h in Headers */, + AF43A7271FC5E80E008E9347 /* MVMCoreTopAlertView.h in Headers */, + AFBB96A11FBA3A9A0008D868 /* MVMCoreTopAlertAnimationDelegateProtocol.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 */, + AFBB96671FBA3A570008D868 /* MFURLSessionChallengeResult.h in Headers */, + AFBB969F1FBA3A9A0008D868 /* MVMCoreAlertOperation.h in Headers */, + AFBB965A1FBA3A570008D868 /* FreeBeeUrlObject.h in Headers */, + AF43A71B1FC5BEBB008E9347 /* MVMCoreViewControllerProtocol.h in Headers */, + AFBB96571FBA3A570008D868 /* FreeBeeAuthObject.h in Headers */, + AFBB96651FBA3A570008D868 /* MFSSLPinningHandler.h in Headers */, + AFBB96381FBA39E70008D868 /* MVMCoreLoadDelegateProtocol.h in Headers */, + AFBB96B01FBA3B590008D868 /* MVMCoreDispatchUtility.h in Headers */, + 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 */, + 88D1FBE11FCCCBA100338A3A /* MVMCoreMainSplitViewProtocol.h in Headers */, + AFBB96631FBA3A570008D868 /* MVMCoreLoadRequestOperation.h in Headers */, + AFEEE81B1FCDEBFA00B5EDD0 /* MobileFirstProtocol.h in Headers */, + AFBB969D1FBA3A9A0008D868 /* MVMCoreAlertObject.h in Headers */, + 8876D5CE1FB50A9E00EB2E3D /* MVMCore.h in Headers */, + AFBB965E1FBA3A570008D868 /* MFFreebeeOperation.h in Headers */, + AF43A57B1FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.h in Headers */, + AFBB96B81FBA3CEC0008D868 /* MVMCoreActionDelegateProtocol.h in Headers */, + AFBB968B1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.h in Headers */, + AF43A70B1FC4F415008E9347 /* MVMCoreCache.h in Headers */, + AFBB965C1FBA3A570008D868 /* MFFreebeeHandler.h in Headers */, + AF43A5831FBB66DE008E9347 /* MVMCoreConstants.h in Headers */, + AFBB969A1FBA3A9A0008D868 /* MVMCoreAlertDelegateProtocol.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 */, + AFBB969B1FBA3A9A0008D868 /* MVMCoreAlertHandler.h in Headers */, + AFED77A21FCCA29400BAE689 /* MVMCoreViewControllerMappingObject.h in Headers */, + AFFCFA661FCCC0D700FD0730 /* MVMCoreLoadingOverlayHandler.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 8876D5C81FB50A9E00EB2E3D /* MVMCore */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8876D5D11FB50A9E00EB2E3D /* Build configuration list for PBXNativeTarget "MVMCore" */; + buildPhases = ( + 8876D5C41FB50A9E00EB2E3D /* Sources */, + 8876D5C51FB50A9E00EB2E3D /* Frameworks */, + 8876D5C61FB50A9E00EB2E3D /* Headers */, + 8876D5C71FB50A9E00EB2E3D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MVMCore; + productName = MVMCore; + productReference = 8876D5C91FB50A9E00EB2E3D /* MVMCore.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8876D5C01FB50A9E00EB2E3D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0910; + ORGANIZATIONNAME = myverizon; + TargetAttributes = { + 8876D5C81FB50A9E00EB2E3D = { + CreatedOnToolsVersion = 9.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 8876D5C31FB50A9E00EB2E3D /* Build configuration list for PBXProject "MVMCore" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 8876D5BF1FB50A9E00EB2E3D; + productRefGroup = 8876D5CA1FB50A9E00EB2E3D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8876D5C81FB50A9E00EB2E3D /* MVMCore */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8876D5C71FB50A9E00EB2E3D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AFBB96EB1FBA4A260008D868 /* canned.json in Resources */, + AF26DDAE1FCE6A37004E8F65 /* Localizable.strings in Resources */, + AF26DDB71FCE802D004E8F65 /* DigiCertGlobalRootCA.cer in Resources */, + AFBB96561FBA3A570008D868 /* freebee.json in Resources */, + AFBB96591FBA3A570008D868 /* freebeelaunchApp.json in Resources */, + AF26DDB51FCE7C76004E8F65 /* VeriSignClass3PublicPrimaryCertificationAuthority-G5.cer in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8876D5C41FB50A9E00EB2E3D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AFED77A71FCCA29400BAE689 /* MVMCoreViewControllerProgrammaticMappingObject.m in Sources */, + AFBB96A01FBA3A9A0008D868 /* MVMCoreAlertOperation.m in Sources */, + AFBB96641FBA3A570008D868 /* MVMCoreLoadHandler.m in Sources */, + AF43A7281FC5E80E008E9347 /* MVMCoreTopAlertView.m in Sources */, + AFFCFA671FCCC0D700FD0730 /* MVMCoreLoadingOverlayHandler.m in Sources */, + AFBB968E1FBA3A9A0008D868 /* MVMCoreNavigationHandler.m in Sources */, + AFBB96991FBA3A9A0008D868 /* MVMCoreAlertController.m in Sources */, + 881D26941FCC9D180079C521 /* MVMCoreOperation.m in Sources */, + AFED77A41FCCA29400BAE689 /* MVMCoreViewControllerMappingObject.m in Sources */, + 8876D5ED1FB50AB000EB2E3D /* NSDictionary+MFConvenience.m in Sources */, + AFBB968C1FBA3A9A0008D868 /* MVMCoreDismissViewControllerOperation.m in Sources */, + AFBB96BA1FBA3CEC0008D868 /* MVMCoreActionHandler.m in Sources */, + AFBB96ED1FBA4A260008D868 /* MFHardCodedServerResponse.m in Sources */, + AF43A74D1FC6109F008E9347 /* MVMCoreSessionObject.m in Sources */, + AFBB965F1FBA3A570008D868 /* MFFreebeeOperation.m in Sources */, + AFBB96901FBA3A9A0008D868 /* MVMCoreNavigationObject.m in Sources */, + AFBB96A61FBA3A9A0008D868 /* MVMCoreTopAlertOperation.m in Sources */, + 881D26931FCC9D180079C521 /* MVMCoreErrorObject.m in Sources */, + 30349BF21FCCA78A00546A1E /* MVMCoreSessionTimeHandler.m in Sources */, + 8876D5E91FB50AB000EB2E3D /* NSArray+MFConvenience.m in Sources */, + AFBB96661FBA3A570008D868 /* MFSSLPinningHandler.m in Sources */, + AFBB96971FBA3A9A0008D868 /* MVMCorePresentViewControllerOperation.m in Sources */, + AFBB96581FBA3A570008D868 /* FreeBeeAuthObject.m in Sources */, + AF43A5781FBA5B7C008E9347 /* MVMCoreJSONConstants.m in Sources */, + AFBB96691FBA3A570008D868 /* MVMCoreRequestParameters.m in Sources */, + AFED77A31FCCA29400BAE689 /* MVMCoreViewControllerNibMappingObject.m in Sources */, + 8876D5EB1FB50AB000EB2E3D /* NSDecimalNumber+MFConvenience.m in Sources */, + AFBB96A41FBA3A9A0008D868 /* MVMCoreTopAlertObject.m in Sources */, + AFBB96351FBA34310008D868 /* MVMCoreErrorConstants.m in Sources */, + AFBB969C1FBA3A9A0008D868 /* MVMCoreAlertHandler.m in Sources */, + AF43A5881FBB67D6008E9347 /* MVMCoreActionUtility.m in Sources */, + AFED77A61FCCA29400BAE689 /* MVMCoreViewControllerStoryBoardMappingObject.m in Sources */, + AF43A57C1FBA5E6A008E9347 /* MVMCoreHardcodedStringsConstants.m in Sources */, + AFBB96681FBA3A570008D868 /* MFURLSessionChallengeResult.m in Sources */, + AFBB965D1FBA3A570008D868 /* MFFreebeeHandler.m in Sources */, + AFEEE81F1FCDF3CA00B5EDD0 /* MVMCoreLoggingHandler.m in Sources */, + AFBB969E1FBA3A9A0008D868 /* MVMCoreAlertObject.m in Sources */, + 8876D5F51FB50AB000EB2E3D /* UILabel+MFCustom.m in Sources */, + AFBB96B31FBA3B590008D868 /* MVMCoreGetterUtility.m in Sources */, + AF43A7071FC4D7A2008E9347 /* MVMCoreObject.m in Sources */, + AFBB96B11FBA3B590008D868 /* MVMCoreDispatchUtility.m in Sources */, + AFBB965B1FBA3A570008D868 /* FreeBeeUrlObject.m in Sources */, + AF43A70A1FC4F415008E9347 /* MVMCoreCache.m in Sources */, + AF43A6FF1FBE3252008E9347 /* Reachability.m in Sources */, + AFBB96921FBA3A9A0008D868 /* MVMCoreNavigationOperation.m in Sources */, + AFBB96611FBA3A570008D868 /* MVMCoreLoadObject.m in Sources */, + AFBB96941FBA3A9A0008D868 /* MVMCorePresentAnimationOperation.m in Sources */, + AF43A5841FBB66DE008E9347 /* MVMCoreConstants.m in Sources */, + AFBB966A1FBA3A570008D868 /* MVMCoreLoadRequestOperation.m in Sources */, + 8876D5F31FB50AB000EB2E3D /* UIFont+MFSpacing.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + AF26DDB01FCE6A37004E8F65 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + AF26DDAF1FCE6A37004E8F65 /* en */, + ); + name = Localizable.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 8876D5CF1FB50A9E00EB2E3D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = NO; + SDKROOT = iphoneos; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 8876D5D01FB50A9E00EB2E3D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 8876D5D21FB50A9E00EB2E3D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../SharedFrameworks"; + INFOPLIST_FILE = MVMCore/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MVMCore/EmbeddedLibaries/AdobeMobileLibrary", + "$(PROJECT_DIR)/MVMCore/EmbeddedLibraries/VZAnalytics", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCore; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8876D5D31FB50A9E00EB2E3D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BITCODE_GENERATION_MODE = bitcode; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../SharedFrameworks"; + INFOPLIST_FILE = MVMCore/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MVMCore/EmbeddedLibaries/AdobeMobileLibrary", + "$(PROJECT_DIR)/MVMCore/EmbeddedLibraries/VZAnalytics", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCore; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8876D5C31FB50A9E00EB2E3D /* Build configuration list for PBXProject "MVMCore" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8876D5CF1FB50A9E00EB2E3D /* Debug */, + 8876D5D01FB50A9E00EB2E3D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8876D5D11FB50A9E00EB2E3D /* Build configuration list for PBXNativeTarget "MVMCore" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8876D5D21FB50A9E00EB2E3D /* Debug */, + 8876D5D31FB50A9E00EB2E3D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 8876D5C01FB50A9E00EB2E3D /* Project object */; +} diff --git a/MVMCore/MVMCore.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MVMCore/MVMCore.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..facb49a --- /dev/null +++ b/MVMCore/MVMCore.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/MVMCore/MVMCore.xcodeproj/xcshareddata/xcschemes/MVMCore.xcscheme b/MVMCore/MVMCore.xcodeproj/xcshareddata/xcschemes/MVMCore.xcscheme new file mode 100644 index 0000000..440c9ba --- /dev/null +++ b/MVMCore/MVMCore.xcodeproj/xcshareddata/xcschemes/MVMCore.xcscheme @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MVMCore/MVMCore.xcodeproj/xcuserdata/yangti5.xcuserdatad/xcschemes/xcschememanagement.plist b/MVMCore/MVMCore.xcodeproj/xcuserdata/yangti5.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..acdb3cc --- /dev/null +++ b/MVMCore/MVMCore.xcodeproj/xcuserdata/yangti5.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + MVMCore.xcscheme_^#shared#^_ + + orderHint + 11 + + + + diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionDelegateProtocol.h b/MVMCore/MVMCore/ActionHandling/MVMCoreActionDelegateProtocol.h new file mode 100644 index 0000000..0a6fd5a --- /dev/null +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionDelegateProtocol.h @@ -0,0 +1,52 @@ +// +// MVMCoreActionDelegateProtocol.h +// myverizon +// +// Created by Scott Pfeil on 11/20/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// + +#import + +@class MVMCoreErrorObject; +@class MVMCoreRequestParameters; +@class MVMCoreAlertObject; + +@protocol MVMCoreActionDelegateProtocol + +@optional + +// Gives the delegate an opportunity to do it's own logging. If not it will do it's own logging. +- (void)logActionWithActionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; + +// Handles the open page actions. Can overwrite for special loading. +- (void)handleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; + +// 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. Return YES if successful, NO if an error should be returned. +- (BOOL)linkedAwaySuccessfullyWithURL:(nullable NSString *)URL appURL:(nullable NSString *)appURL actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; + +// Gives the delegate a chance to alter the alert object +- (void)willShowPopupWithAlertObject:(nonnull MVMCoreAlertObject *)alertObject alertJson:(nonnull NSDictionary *)alertJson; + +// Gives the delegate a chance to alter the alert object +- (nullable MVMCoreAlertObject *)willShowTopAlertWithAlertObject:(nonnull MVMCoreAlertObject *)alertObject alertJson:(nonnull NSDictionary *)alertJson; + +// Handle cancel +- (void)handleCancel:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData; + +// 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; + +@end diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h new file mode 100644 index 0000000..0df0809 --- /dev/null +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.h @@ -0,0 +1,92 @@ +// +// 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 +#import +#import +#import + + +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 delegate:(nullable NSObject *)delegate; + +// Handles actions. Used by server driven user actions.. +- (void)handleAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +#pragma mark - Actions + +// Logs the action. Currently is not action information driven... depends on delegate. +- (void)logAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Tries to open a page +- (void)openPageAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Links away to app or browser +- (void)linkAwayAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// restarts the app +- (void)restartAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Goes back +- (void)backAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Makes a phone call +- (void)callAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Makes the previous request, needs the delegate for this +- (void)previousSubmitAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Shows a popup +- (void)popupAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Shows a top alert +- (void)topAlertAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Redirects to another experience +- (void)redirectAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Cancels (like in a popup) +- (void)cancelAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Goes to settings app +- (void)settingsAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Collapses the current top notification +- (void)collapseNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Subclass this to handle other custom actions. Return YES if handled, and NO if not. +- (BOOL)handleOtherActions:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Last chance to handle unknown actions before throwing an error +- (void)unknownAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Handles action errors. +- (void)handleActionError:(nullable MVMCoreErrorObject *)error actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +#pragma mark - Default Action Protocol Functions + +// Sends the request to the load handler. ++ (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject*)delegate; + +// By default, throws an error, calling defaultHandleActionError. ++ (void)defaultHandleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate; + +// Shows a popup error ++ (void)defaultHandleActionError:(nonnull MVMCoreErrorObject *)error additionalData:(nullable NSDictionary *)additionalData; + +@end diff --git a/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m new file mode 100644 index 0000000..947ad6b --- /dev/null +++ b/MVMCore/MVMCore/ActionHandling/MVMCoreActionHandler.m @@ -0,0 +1,324 @@ +// +// MVMCoreActionHandler.m +// myverizon +// +// Created by Scott Pfeil on 11/20/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// + +#import +#import "MVMCoreLoggingHandler.h" +#import "MVMCoreAlertHandler.h" +#import "MVMCoreCache.h" +#import "MVMCoreAlertObject.h" +#import "MVMCoreSessionTimeHandler.h" +#import "MVMCoreLoadHandler.h" +#import "MVMCoreNavigationHandler.h" +#import "MFFreebeeHandler.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" + +NSString * const KeyActionType = @"actionType"; +NSString * const KeyActionTypeLinkAway = @"openURL"; +NSString * const KeyActionTypeOpen = @"openPage"; + +@implementation MVMCoreActionHandler + ++ (nullable instancetype)sharedActionHandler { + return [MVMCoreObject sharedInstance].actionHandler; +} + +- (void)handleActionWithDictionary:(nullable NSDictionary *)dictionary additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + + NSString *action = [dictionary stringForKey:KeyActionType]; + [self handleAction:action actionInformation:dictionary additionalData:additionalData delegate:delegate]; +} + +- (void)handleAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + // Logs the action. + [self logAction:actionInformation additionalData:additionalData delegate:delegate]; + + if ([actionType isEqualToString:KeyActionTypeOpen]) { + [self openPageAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if ([actionType isEqualToString:KeyActionTypeLinkAway]) { + [self linkAwayAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if ([actionType isEqualToString:KeyActionTypeRestart]) { + [self restartAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if ([actionType isEqualToString:KeyActionTypeBack]) { + [self backAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if ([actionType isEqualToString:KeyActionTypeCall]) { + [self callAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if ([actionType isEqualToString:KeyActionTypePreviousSubmit]) { + [self previousSubmitAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if ([actionType isEqualToString:KeyActionTypePopup]) { + [self popupAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if ([actionType isEqualToString:KeyActionTypeTopAlert]) { + [self topAlertAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if ([actionType isEqualToString:KeyActionTypeRedirect]) { + [self redirectAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if ([actionType isEqualToString:KeyActionTypeCancel]) { + [self cancelAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if ([actionType isEqualToString:KeyActionTypeSettings]) { + [self settingsAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if ([actionType isEqualToString:KeyActionTypeCollapseNotification]) { + [self collapseNotificationAction:actionInformation additionalData:additionalData delegate:delegate]; + } else if (![self handleOtherActions:actionType actionInformation:actionInformation additionalData:additionalData delegate:delegate]) { + // not a known action type. + [self unknownAction:actionType actionInformation:actionInformation additionalData:additionalData delegate:delegate]; + } + }); +} + +#pragma mark - Actions + +- (void)logAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + if ([delegate respondsToSelector:@selector(logActionWithActionInformation:additionalData:)]) { + [delegate logActionWithActionInformation:actionInformation additionalData:additionalData]; + } else { + [MVMCoreActionHandler defaultLogAction:actionInformation additionalData:additionalData delegate:delegate]; + } +} + +- (void)openPageAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + + // Loads the given page type. + NSString *pageType = [actionInformation stringForKey:KeyPageType]; + if (pageType.length > 0) { + + //if freebee enabled for account,and state isNotDetermined or is expired + //launchFreebee auth request + //else if invalid or valid or busy do nothing +#warning i'd like to move this out of here. + MFFreebeeHandler *freebeeHandler = [MFFreebeeHandler sharedHandler]; + if (YES == [freebeeHandler canProceedWithFreebeeAuthRequest]) { + + MVMCoreLog(@"Processing Freebee Auth request"); + [freebeeHandler processFreeBeeAuthRequestWithCompletionHandler:^(MVMCoreOperation* _Nullable freebeeOperation, BOOL isValid) { + + MVMCoreLog(@"Processing Freebee Auth request Initiated"); + }]; + } + + MVMCoreLog(@"Continuing operation after freebee request"); + MVMCoreRequestParameters *requestParameters = [[MVMCoreRequestParameters alloc] initWithActionMap:actionInformation]; + if ([delegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) { + [delegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:additionalData]; + } else { + [MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters additionalData:additionalData delegate:delegate]; + } + } else { + + // No page type to load, show error. + MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeNoPageType domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@_%@",NSStringFromClass([delegate class]),KeyActionTypeOpen]]; + [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegate:delegate]; + } +} + +- (void)linkAwayAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + // Linkaway to the browser or app. + NSString *appURL = [actionInformation stringForKey:KeyLinkAwayAppURL]; + NSString *otherURL = [actionInformation stringForKey:KeyLinkAwayURL]; + + BOOL successful = NO; + if ([delegate respondsToSelector:@selector(linkedAwaySuccessfullyWithURL:appURL:actionInformation:additionalData:)]) { + successful = [delegate linkedAwaySuccessfullyWithURL:otherURL appURL:appURL actionInformation:actionInformation additionalData:additionalData]; + } else { + successful = [MVMCoreActionUtility linkAway:otherURL appURLString:appURL]; + } + + // Cannot linkaway, show error. + if (!successful) { + MVMCoreErrorObject *error = error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeLinkawayFailed domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@_%@",NSStringFromClass([delegate class]),KeyActionTypeLinkAway]]; + [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegate:delegate]; + } +} + +- (void)restartAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + + // Invalidates the session before restarting. + [[MVMCoreSessionTimeHandler sharedSessionHandler] invalidateSession:^(MVMCoreErrorObject * _Nullable error) { + + // Restarts the app (forcing any passed in page types). + if (error.code != NSURLErrorCancelled) { + + if (error) { + + // Error invalidating. + [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegate:delegate]; + } else { + + // Restart the application with the page type. + NSString *pageType = [actionInformation string:KeyPageType]; + [[MVMCoreObject sharedInstance].mobileFirstDelegate addModifiedSendParametersForLaunch:[actionInformation dict:KeyExtraParameters]]; + [[MVMCoreObject sharedInstance].mobileFirstDelegate restartWithPageType:pageType clearSessionVariables:YES]; + } + } + }]; +} + +- (void)backAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + // Go back. + if ([delegate respondsToSelector:@selector(handleBackAction:additionalData:)]) { + [delegate handleBackAction:actionInformation additionalData:additionalData]; + } else { + [[MVMCoreNavigationHandler sharedNavigationHandler] removeCurrentViewController]; + } +} + +- (void)callAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + // Call + NSString *callNumber = [actionInformation stringForKey:KeyCallNumber]; + [MVMCoreActionUtility linkAway:[@"tel://" stringByAppendingString:callNumber] appURLString:nil]; +} + +- (void)previousSubmitAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + // Perform the previous submission. + if ([delegate respondsToSelector:@selector(prepareRequestForPreviousSubmission:additionalData:submit:)]) { + [delegate prepareRequestForPreviousSubmission:actionInformation additionalData:additionalData submit:^(MVMCoreRequestParameters * _Nonnull requestParameters, NSDictionary * _Nullable dataForPage) { + + // Give the delegate a chance to alter the request parameters + if ([delegate respondsToSelector:@selector(handleOpenPageForRequestParameters:actionInformation:additionalData:)]) { + [delegate handleOpenPageForRequestParameters:requestParameters actionInformation:actionInformation additionalData:dataForPage]; + } else { + [MVMCoreActionHandler defaultHandleOpenPageForRequestParameters:requestParameters additionalData:additionalData delegate:delegate]; + } + }]; + } +} + +- (void)popupAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + // Perform a popup. + NSString *pageTypeForPopup = [actionInformation stringForKey:KeyPageType]; + [[MVMCoreCache sharedCache] fetchJSONForPageType:pageTypeForPopup queue:nil waitUntilFinished:YES completionHandler:^(NSDictionary * _Nullable jsonDictionary) { + + MVMCoreErrorObject *error = nil; + MVMCoreAlertObject *alertObject = [MVMCoreAlertObject alertObjectWithPage:jsonDictionary isGreedy:NO additionalData:additionalData delegate:delegate error:&error]; + if ([delegate respondsToSelector:@selector(willShowPopupWithAlertObject:alertJson:)]) { + [delegate willShowPopupWithAlertObject:alertObject alertJson:jsonDictionary]; + } + + if (alertObject) { + [[MVMCoreAlertHandler sharedAlertHandler] showAlertWithAlertObject:alertObject]; + } else { + [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegate:delegate]; + } + }]; +} + +- (void)topAlertAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + // Perform a top alert. + NSString *pageTypeForTopAlert = [actionInformation stringForKey:KeyPageType]; + [[MVMCoreCache sharedCache] fetchJSONForPageType:pageTypeForTopAlert queue:nil waitUntilFinished:YES completionHandler:^(NSDictionary * _Nullable jsonDictionary) { + + NSDictionary *responseInfo = [jsonDictionary dict:KeyResponseInfo]; + if (responseInfo) { + MVMCoreAlertObject *alertObject = [MVMCoreAlertObject alertObjectForPageType:pageTypeForTopAlert responseInfo:responseInfo additionalData:additionalData actionDelegate:delegate]; + if ([delegate respondsToSelector:@selector(willShowPopupWithAlertObject:alertJson:)]) { + alertObject = [delegate willShowTopAlertWithAlertObject:alertObject alertJson:jsonDictionary]; + } + [alertObject showAlert]; + } + }]; +} + +- (void)redirectAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + // Have delegate redirect. + [[MVMCoreSessionObject sharedGlobal] redirectWithInfo:actionInformation]; +} + +- (void)cancelAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + if ([delegate respondsToSelector:@selector(handleCancel:additionalData:)]) { + [delegate handleCancel:actionInformation additionalData:additionalData]; + } +} + +- (void)settingsAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + // Opens the settings. + NSString *type = [actionInformation string:KeyPageType]; + if ([@"location" isEqualToString:type] || [@"push" isEqualToString:type]) { + [MVMCoreActionUtility linkAway:UIApplicationOpenSettingsURLString appURLString:nil]; + } else if ([@"airplaneMode" isEqualToString:type]) { + + // Stopped working in iOS 10. + [MVMCoreActionUtility linkAway:@"prefs:root=AIRPLANE_MODE" appURLString:nil]; + } else { + + // No known settings type + MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeInvalidSettingType domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@_%@",NSStringFromClass([delegate class]),KeyActionTypeSettings]]; + [self handleActionError:error actionInformation:actionInformation additionalData:additionalData delegate:delegate]; + } +} + +- (void)collapseNotificationAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + // Collapse the current notification. + [[MVMCoreObject sharedInstance].topAlertView collapseNotification]; +} + +- (BOOL)handleOtherActions:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + return NO; +} + +- (void)unknownAction:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + if ([delegate respondsToSelector:@selector(handleUnknownActionType:actionInformation:additionalData:)]) { + [delegate handleUnknownActionType:actionType actionInformation:actionInformation additionalData:additionalData]; + } else { + [MVMCoreActionHandler defaultHandleUnknownActionType:actionType actionInformation:actionInformation additionalData:additionalData delegate:delegate]; + } +} + +- (void)handleActionError:(nullable MVMCoreErrorObject *)error actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + if (error) { + if ([delegate respondsToSelector:@selector(handleActionError:additionalData:)]) { + [delegate handleActionError:error additionalData:additionalData]; + } else { + [MVMCoreActionHandler defaultHandleActionError:error additionalData:additionalData]; + } + } +} + +#pragma mark - Default Action Protocol Functions + ++ (void)defaultLogAction:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate{ + // Currently no default log action but this will eventually be server driven. +} + ++ (void)defaultHandleOpenPageForRequestParameters:(nonnull MVMCoreRequestParameters *)requestParameters additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject*)delegate { + [[MVMCoreLoadHandler sharedGlobal] loadRequest:requestParameters dataForPage:additionalData delegate:delegate]; +} + ++ (void)defaultHandleUnknownActionType:(nullable NSString *)actionType actionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate { + + MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeUnknownActionType domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@Requests_%@",NSStringFromClass([delegate class]),actionType]]; + [MVMCoreActionHandler defaultHandleActionError:error additionalData:additionalData]; +} + ++ (void)defaultHandleActionError:(nonnull MVMCoreErrorObject *)error additionalData:(nullable NSDictionary *)additionalData { + + // Logs the error. + if (error.logError) { + [MVMCoreLoggingHandler addErrorToLog:error]; + } + + MVMCoreLog(@"Error: %@ %@ %@ %@",[error stringErrorCode], error.domain, error.location,error.messageToDisplay); + if (!error.silentError) { + + // Show alert + MVMCoreAlertObject *alertObject = [[MVMCoreAlertObject alloc] initPopupAlertWithError:error isGreedy:NO]; + [[MVMCoreAlertHandler sharedAlertHandler] showAlertWithAlertObject:alertObject]; + } +} + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertController.h b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertController.h new file mode 100644 index 0000000..977911f --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertController.h @@ -0,0 +1,16 @@ +// +// 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 + +@interface MVMCoreAlertController : UIAlertController + +@property (nonatomic, readonly, getter=isVisible) BOOL visible; + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertController.m b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertController.m new file mode 100644 index 0000000..4771309 --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertController.m @@ -0,0 +1,34 @@ +// +// MVMAlertController.m +// alerts +// +// Created by Scott Pfeil on 10/22/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreAlertController.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"]; +} + +- (void)viewDidDisappear:(BOOL)animated { + [super viewDidDisappear:animated]; + [self willChangeValueForKey:@"isVisible"]; + self.visible = NO; + [self didChangeValueForKey:@"isVisible"]; +} + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertDelegateProtocol.h b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertDelegateProtocol.h new file mode 100644 index 0000000..2bd6a6b --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertDelegateProtocol.h @@ -0,0 +1,24 @@ +// +// MVMCoreAlertDelegateProtocol.h +// mobilefirst +// +// Created by Pfeil, Scott Robert on 8/8/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// +// Called for popup style alerts. + +#import + +@protocol MVMCoreAlertDelegateProtocol + +@optional + +// All are performed on the main thread. +- (void)alertShown:(nonnull UIAlertController *)alertController; +- (void)alertCancelled:(nonnull UIAlertController *)alertController; +- (void)alertDismissed:(nonnull UIAlertController *)alertController; +- (void)alertPaused:(nonnull UIAlertController *)alertController; +- (void)alertUnpaused:(nonnull UIAlertController *)alertController; + +@end + diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.h b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.h new file mode 100644 index 0000000..c09eafe --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.h @@ -0,0 +1,87 @@ +// +// MVMCoreAlertHandler.h +// myverizon +// +// Created by Scott Pfeil on 3/10/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// +// Keeps track of alerts and handles them. Should always use this to present alerts in mf. + +#import +#import +#import +#import + +@class MVMCoreAlertObject; + +@interface MVMCoreAlertHandler : NSObject + +// Returns the shared instance of this singleton ++ (nullable instancetype)sharedAlertHandler; + +#pragma mark - Popup Alert Functions + +// Returns if any alert is currently showing (even if supressed). +- (BOOL)alertCurrentlyShowing; + +// Returns if a greedy alert is currently showing (even if supressed). +- (BOOL)greedyAlertShowing; + +/** Shows the popup with the passed in parameter. This is a convenience method that automatically handles using the proper alert type based on what's available. + * @param title The title of the alert. + * @param message The message of the alert. + * @param actions An array of actions for the alert. + * @param isGreedy Sets up a greedy popup. In other words, any popups currently shown or queued are dismissed. + * @return Returns the UIAlertController. + */ +- (nonnull UIAlertController *)showAlertWithTitle:(nullable NSString *)title message:(nullable NSString *)message actions:(nullable NSArray*)actions isGreedy:(BOOL)isGreedy; + +/** Shows the popup with the passed in alert object. This is a convenience method that automatically handles using the proper alert type based on what's available. + * @param alertObject The alert object to use for the alert. + * @return Returns UIAlertController. + */ +- (nonnull UIAlertController *)showAlertWithAlertObject:(nonnull MVMCoreAlertObject *)alertObject; + +// Removes all alerts. +- (void)removeAllAlertViews; + +#pragma mark - Supression Functions + +// Returns true if alerts are supressed. +- (BOOL)mfAlertsSupressed; + +// Supresses the alerts (Used by other "apps" in our app). +- (void)supressMFAlerts; + +// Unsupresses the alerts (Used by other "apps" in our app). +- (void)unSupressMFAlerts; + +#pragma mark - Top Alert Functions + +// Show based on the object. Will be used by the architecture. +- (void)showTopAlertWithObject:(nullable MVMCoreTopAlertObject *)topAlertObject; + +// Convenience functions +- (void)showTopAlertErrorWithMessage:(nullable NSString *)message; +- (void)showTopAlertConfirmationWithMessage:(nullable NSString *)message; +- (void)showTopAlertWithType:(MFTopType)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData; +- (void)showTopAlertWithType:(MFTopType)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler; + +// Hides the current alert view. +- (void)hideTopAlertView; + +// Hides a persistent alert based on the type string. +- (void)hidePersistentTopAlertViewOfType:(MFTopType)type; +- (void)hidePersistentTopAlertViewOfTypeString:(nullable NSString *)type; + +// Removes all top alerts. +- (void)removeAllTopAlerts; + +// Returns a top type based on type string and vice versa ++ (MFTopType)topTypeForString:(nullable NSString *)type; ++ (nonnull NSString *)stringForTopType:(MFTopType)type; + +/// Returns YES if the persistent type is already registered in the alert queue. +- (BOOL)hasPersistentTopAlertOfType:(MFTopType)type; + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.m b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.m new file mode 100644 index 0000000..d0bcea9 --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertHandler.m @@ -0,0 +1,288 @@ +// +// MVMCoreAlertHandler.m +// myverizon +// +// Created by Scott Pfeil on 3/10/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreAlertHandler.h" +#import "MVMCoreAlertObject.h" +#import "MVMCoreAlertController.h" +#import "MVMCoreAlertOperation.h" +#import "MVMCoreTopAlertOperation.h" +#import "MVMCoreJSONConstants.h" +#import "NSDictionary+MFConvenience.h" +#import "NSArray+MFConvenience.h" + +@interface MVMCoreAlertHandler () + +// Flag that keeps track of if the alerts are supressed or not. +@property (assign, nonatomic) BOOL mfAlertsSupressed; + +// An operation queue for displaying popup alerts. +@property (nonnull, strong, nonatomic) NSOperationQueue *popupAlertQueue; + +// An operation queue for top alerts +@property (nonnull, strong, nonatomic) NSOperationQueue *topAlertQueue; + +@end + +@implementation MVMCoreAlertHandler + ++ (instancetype)sharedAlertHandler { + static dispatch_once_t once; + static id sharedInstance; + + dispatch_once(&once, ^{ + sharedInstance = [[self alloc] init]; + }); + + return sharedInstance; +} + +- (nullable instancetype)init { + if (self = [super init]) { + self.popupAlertQueue = [[NSOperationQueue alloc] init]; + self.popupAlertQueue.maxConcurrentOperationCount = 1; + self.topAlertQueue = [[NSOperationQueue alloc] init]; + self.topAlertQueue.maxConcurrentOperationCount = 1; + } + return self; +} + +#pragma mark - Popup Alert Functions + +- (BOOL)alertCurrentlyShowing { + return (self.popupAlertQueue.operationCount > 0); +} + +- (BOOL)greedyAlertShowing { + return [self alertCurrentlyShowing] && ((MVMCoreAlertOperation *)self.popupAlertQueue.operations[0]).isGreedy; +} + +- (nonnull UIAlertController *)showAlertWithTitle:(nullable NSString *)title message:(nullable NSString *)message actions:(nullable NSArray*)actions isGreedy:(BOOL)isGreedy { + return [self showAlertWithTitle:title message:message actions:actions isGreedy:isGreedy alertDelegate:nil]; +} + +- (nonnull UIAlertController *)showAlertWithTitle:(nullable NSString *)title message:(nullable NSString *)message actions:(nullable NSArray*)actions isGreedy:(BOOL)isGreedy alertDelegate:(nullable NSObject *)alertDelegate { + + // It's a greedy alert! Clear all alerts that are queued up and the one that is showing + if (isGreedy) { + [self removeAllAlertViews]; + } + + // Create the alert. Adds the actions one by one. + MVMCoreAlertController *alertController = [MVMCoreAlertController alertControllerWithTitle:(title ?: @"") message:message preferredStyle:UIAlertControllerStyleAlert]; + for (NSUInteger i = 0; i < [actions count]; i++) { + UIAlertAction *action = [actions objectAtIndex:i ofType:[UIAlertAction class]]; + if (action) { + [alertController addAction:action]; + } + } + + MVMCoreAlertOperation *alertOperation = [[MVMCoreAlertOperation alloc] initWithAlert:alertController isGreedy:isGreedy alertDelegate:alertDelegate]; + [self.popupAlertQueue addOperation:alertOperation]; + return alertController; +} + +- (nonnull UIAlertController *)showAlertWithAlertObject:(nonnull MVMCoreAlertObject *)alertObject { + return [self showAlertWithTitle:alertObject.title message:alertObject.message actions:alertObject.actions isGreedy:alertObject.isGreedy alertDelegate:alertObject.alertDelegate]; +} + +- (void)removeAllAlertViews { + [self.popupAlertQueue cancelAllOperations]; +} + +#pragma mark - Supression Functions + +- (BOOL)mfAlertsSupressed { + return _mfAlertsSupressed; +} + +- (void)supressMFAlerts { + if (!self.mfAlertsSupressed) { + self.mfAlertsSupressed = YES; + if ([self alertCurrentlyShowing]) { + [((MVMCoreAlertOperation *)self.popupAlertQueue.operations[0]) pause]; + } + + for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) { + [operation pause]; + } + } +} + +- (void)unSupressMFAlerts { + if (self.mfAlertsSupressed) { + self.mfAlertsSupressed = NO; + if ([self alertCurrentlyShowing]) { + [((MVMCoreAlertOperation *)self.popupAlertQueue.operations[0]) unpause]; + } + + for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) { + [operation unpause]; + } + } +} + +#pragma mark - Top Alert Functions + +- (void)showTopAlertWithObject:(nullable MVMCoreTopAlertObject *)topAlertObject { + + // Don't add the operation if there is currently a clear spot top alert and it is of the same type + if (topAlertObject) { + + __block MVMCoreTopAlertOperation *alertOperation = [[MVMCoreTopAlertOperation alloc] initWithTopAlertObject:topAlertObject]; + [alertOperation setCompletionBlock:^{ + + // If the alert was cancelled to show another with higher priority, re-add to the operation when cancelled to the queue. + if (alertOperation.reAddAfterCancel) { + [self showTopAlertWithObject:alertOperation.topAlertObject]; + } + alertOperation = nil; + }]; + [self.topAlertQueue addOperation:alertOperation]; + + // If the current running operation is persistent and has a lower queue priority then the added operation, cancel it and re-add it. + for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) { + + if (operation.isExecuting && !operation.isCancelled && operation.topAlertObject.persistent && operation.queuePriority < alertOperation.queuePriority) { + operation.reAddAfterCancel = YES; + [operation cancel]; + break; + } + } + } +} + +- (void)showTopAlertErrorWithMessage:(nullable NSString *)message { + + MVMCoreTopAlertObject *topAlertObject = [[MVMCoreTopAlertObject alloc] initWithType:MFTopTypeError message:message]; + [self showTopAlertWithObject:topAlertObject]; +} + +- (void)showTopAlertConfirmationWithMessage:(nullable NSString *)message { + + MVMCoreTopAlertObject *topAlertObject = [[MVMCoreTopAlertObject alloc] initWithType:MFTopTypeConfirmation message:message]; + [self showTopAlertWithObject:topAlertObject]; +} + +- (void)showTopAlertWithType:(MFTopType)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData { + + MVMCoreTopAlertObject *topAlertObject = [[MVMCoreTopAlertObject alloc] initWithType:type message:message subMessage:subMessage persistent:persistent actionMap:actionMap additionalData:additionalData]; + [self showTopAlertWithObject:topAlertObject]; +} + +- (void)showTopAlertWithType:(MFTopType)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler { + + MVMCoreTopAlertObject *topAlertObject = [[MVMCoreTopAlertObject alloc] initWithType:type message:message subMessage:subMessage persistent:persistent buttonTitle:buttonTitle userActionHandler:userActionHandler]; + [self showTopAlertWithObject:topAlertObject]; +} + +- (void)hideTopAlertView { + + MVMCoreTopAlertOperation *currentOperation = [self.topAlertQueue.operations firstObject]; + currentOperation.topAlertObject.persistent = NO; + currentOperation.reAddAfterCancel = NO; + [currentOperation cancel]; +} + +- (BOOL)hasPersistentTopAlertOfType:(MFTopType)type { + BOOL hasAlert = NO; + NSString *stringType = [MVMCoreAlertHandler stringForTopType:type]; + for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) { + if (operation.topAlertObject.persistent && [operation.topAlertObject.type isEqualToString:stringType]) { + hasAlert = YES; + } + } + return hasAlert; +} + +- (void)hidePersistentTopAlertViewOfType:(MFTopType)type { + [self hidePersistentTopAlertViewOfTypeString:[MVMCoreAlertHandler stringForTopType:type]]; +} + +- (void)hidePersistentTopAlertViewOfTypeString:(nullable NSString *)type { + if (type) { + for (MVMCoreTopAlertOperation *operation in self.topAlertQueue.operations) { + + // Cancel all persistent operations of this type. + if (operation.topAlertObject.persistent && [operation.topAlertObject.type isEqualToString:type]) { + operation.reAddAfterCancel = NO; + [operation cancel]; + } + } + } +} + +- (void)removeAllTopAlerts { + [self.topAlertQueue cancelAllOperations]; +} + ++ (MFTopType)topTypeForString:(nullable NSString *)type { + + if ([ValueTypeSuccess isEqualToString:type]) { + return MFTopTypeConfirmation; + } else if ([ValueTypeError isEqualToString:type]) { + return MFTopTypeError; + } else if ([ValueTypeSurvivalMode isEqualToString:type]) { + return MFTopTypeSurvival; + } else if ([ValueTypeClearSpotMain isEqualToString:type]) { + return MFTopTypeClearspotMain; + } else if ([ValueTypeClearSpotEnded isEqualToString:type]) { + return MFTopTypeClearspotFinished; + } else if ([ValueTypeClearSpotProcessing isEqualToString:type]) { + return MFTopTypeClearspotProcessing; + } else if ([ValueTypeLiveChat isEqualToString:type]) { + return MFTopTypeAgentChatInSession; + } else if ([ValueTypeFamilyBaseChild isEqualToString:type]) { + return MFTopTypeFamilyBaseChild; + } else if ([ValueTypeRetailShop isEqualToString:type]) { + return MFTopTypeShop; + } else if ([ValueTypeRemoteIn isEqualToString:type]) { + return MFTopTypeRemoteIn; + } else { + return MFTopTypeOther; + } +} + ++ (NSString *)stringForTopType:(MFTopType)type { + switch (type) { + case MFTopTypeConfirmation: + return ValueTypeSuccess; + break; + case MFTopTypeError: + return ValueTypeError; + break; + case MFTopTypeSurvival: + return ValueTypeSurvivalMode; + break; + case MFTopTypeClearspotMain: + return ValueTypeClearSpotMain; + break; + case MFTopTypeClearspotFinished: + return ValueTypeClearSpotEnded; + break; + case MFTopTypeClearspotProcessing: + return ValueTypeClearSpotProcessing; + break; + case MFTopTypeAgentChatInSession: + return ValueTypeLiveChat; + break; + case MFTopTypeFamilyBaseChild: + return ValueTypeFamilyBaseChild; + break; + case MFTopTypeShop: + return ValueTypeRetailShop; + break; + case MFTopTypeRemoteIn: + return ValueTypeRemoteIn; + break; + default: + return @"Other"; + break; + } +} + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertObject.h b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertObject.h new file mode 100644 index 0000000..2c9ad40 --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertObject.h @@ -0,0 +1,61 @@ +// +// MVMCoreAlertObject.h +// myverizon +// +// Created by Scott Pfeil on 11/21/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// +// An object for keeping track of all alert variables. Easier to pass around. + +#import +#import +#import +#import +#import + +@class MVMCoreErrorObject; +@class MVMCoreLoadObject; + +typedef NS_ENUM(NSInteger, MFAlertType) { + MFAlertTypePopup = 0, + MFAlertTypeField, + MFAlertTypeTop, + MFAlertTypeNone +}; + +typedef void (^TextFieldErrorHandler)(NSArray * _Nonnull fieldErrors); + +@interface MVMCoreAlertObject : NSObject + +@property (nullable, strong, nonatomic) NSString *title; +@property (nullable, strong, nonatomic) NSString *message; +@property (nonnull, strong, nonatomic) NSArray *actions; +@property (nonatomic) BOOL isGreedy; +@property (nonatomic) MFAlertType type; + +@property (nonnull, strong, nonatomic) NSArray *fieldErrors; +@property (nullable, nonatomic, copy) TextFieldErrorHandler textFieldErrorHandler; + +// Set to be notified of popup style alert events. +@property (nonatomic, weak, nullable) NSObject *alertDelegate; + +// Creates an alert object for an error with the passed in load object response info ++ (nullable instancetype)alertObjectForLoadObject:(nullable MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error actionDelegate:(nullable NSObject *)actionDelegate; ++ (nullable instancetype)alertObjectForPageType:(nullable NSString *)pageType responseInfo:(nullable NSDictionary *)responseInfo additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)actionDelegate; + +// Initializes a popup style alert object. Look at the alert handler to see what each is used for. +- (nullable instancetype)initPopupAlertWithTitle:(nullable NSString *)title message:(nullable NSString *)message actions:(nonnull NSArray *)actions isGreedy:(BOOL)isGreedy; + +// Initializes a popup style alert object using the error passed in. Message is formatted default style. By defualt uses the Okay button to just dismiss the error. +- (nullable instancetype)initPopupAlertWithError:(nullable MVMCoreErrorObject *)error isGreedy:(BOOL)isGreedy; + +// Same as above but no default actions. They are passed in. +- (nullable instancetype)initPopupAlertWithError:(nullable MVMCoreErrorObject *)error actions:(nonnull NSArray *)actions isGreedy:(BOOL)isGreedy; + +// Returns the alert object made with the page json. If there is not enough data to make, we will set error ++ (nullable instancetype)alertObjectWithPage:(nullable NSDictionary *)page isGreedy:(BOOL)isGreedy additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate error:(MVMCoreErrorObject *_Nullable *_Nullable)error; + +// Will show this alert in it's appropriate type style. +- (void)showAlert; + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertObject.m b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertObject.m new file mode 100644 index 0000000..3b31f42 --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertObject.m @@ -0,0 +1,181 @@ +// +// MVMCoreAlertObject.m +// myverizon +// +// Created by Scott Pfeil on 11/21/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreAlertObject.h" +#import "MVMCoreAlertHandler.h" +#import "MVMCoreTopAlertObject.h" +#import "MVMCoreCache.h" +#import "MVMCoreErrorConstants.h" +#import "MVMCoreErrorObject.h" +#import "MVMCoreLoadObject.h" +#import "MVMCoreGetterUtility.h" +#import "NSDictionary+MFConvenience.h" +#import "MVMCoreHardcodedStringsConstants.h" +#import "MVMCoreJSONConstants.h" +#import + +@interface MVMCoreAlertObject () + +@property (strong, nonatomic) MVMCoreLoadObject *loadObject; +@property (nullable, strong, nonatomic) NSString *systemDomain; +@property (nullable, strong, nonatomic) MVMCoreTopAlertObject *topAlertObject; +@end + +@implementation MVMCoreAlertObject + ++ (nullable instancetype)alertObjectForLoadObject:(nullable MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error actionDelegate:(nullable NSObject *)actionDelegate { + + MVMCoreAlertObject *alert = nil; + if (!error || [ErrorDomainServer isEqualToString:error.domain]) { + alert = [MVMCoreAlertObject alertObjectForPageType:loadObject.pageType responseInfo:loadObject.responseInfoMap additionalData:loadObject.dataForPage actionDelegate:actionDelegate]; + } else { + alert = [[MVMCoreAlertObject alloc] initPopupAlertWithError:error isGreedy:NO]; + } + + // only if actions are empty, then go inside and set OK as default action + if (alert.type == MFAlertTypePopup && alert.actions.count == 0) { + alert.actions = @[[UIAlertAction actionWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedOK] style:UIAlertActionStyleDefault handler:nil]]; + } + return alert; +} + ++ (nullable instancetype)alertObjectForPageType:(nullable NSString *)pageType responseInfo:(nullable NSDictionary *)responseInfo additionalData:(nullable NSDictionary *)additionalData actionDelegate:(nullable NSObject *)actionDelegate { + + __block MVMCoreAlertObject *alert = [[MVMCoreAlertObject alloc] init]; + alert.title = [responseInfo stringForKey:KeyErrorHeading]; + alert.message = [responseInfo stringForKey:KeyUserMessage]; + + NSString *messageStyle = [responseInfo stringForKey:KeyMessageStyle]; + if ([ValueTypeFieldErrors isEqualToString:[responseInfo string:KeyType]]) { + + // field errors. + alert.type = MFAlertTypeField; + alert.fieldErrors = [responseInfo array:ValueTypeFieldErrors]; + } else { + + // Check for top alert (persistent or regular). + if ([messageStyle isEqualToString:ValueMessageStyleTopPersistent] || [messageStyle isEqualToString:ValueMessageStyleTop]) { + + alert.topAlertObject = [[MVMCoreTopAlertObject alloc] initWithResponseInfo:responseInfo]; + alert.topAlertObject.delegate = actionDelegate; + alert.topAlertObject.pageType = pageType; + alert.type = MFAlertTypeTop; + } else if ([messageStyle isEqualToString:ValueMessageStylePopup]) { + + // Perform a popup. + alert.type = MFAlertTypePopup; + + // Check if we have a popup driven by page object (otherwise by default it will just use response info title message with an OK button). + NSString *pageTypeForPopup = [responseInfo stringForKey:@"popupPageType"]; + [[MVMCoreCache sharedCache] fetchJSONForPageType:pageTypeForPopup queue:nil waitUntilFinished:YES completionHandler:^(NSDictionary * _Nullable jsonDictionary) { + + MVMCoreErrorObject *error = nil; + MVMCoreAlertObject *popupAlert = [MVMCoreAlertObject alertObjectWithPage:jsonDictionary isGreedy:NO additionalData:additionalData delegate:actionDelegate error:&error]; + if (error) { + + // Error, popup page not found for page type. + popupAlert = [[MVMCoreAlertObject alloc] initPopupAlertWithError:error isGreedy:NO]; + } + + if (popupAlert) { + alert = popupAlert; + } + }]; + } else if (messageStyle.length == 0 && pageType) { + + // No message style! + alert.type = MFAlertTypeNone; + } else { + + // Default to popup + alert.type = MFAlertTypePopup; + } + } + return alert; +} + +- (nullable instancetype)initPopupAlertWithTitle:(nullable NSString *)title message:(nullable NSString *)message actions:(nonnull NSArray *)actions isGreedy:(BOOL)isGreedy { + if (self = [super init]) { + self.title = title; + self.message = message; + self.actions = actions; + self.isGreedy = isGreedy; + self.type = MFAlertTypePopup; + } + return self; +} + +- (nullable instancetype)initPopupAlertWithError:(nullable MVMCoreErrorObject *)error isGreedy:(BOOL)isGreedy { + + if (self = [super init]) { + self.title = error.title; + self.message = [NSString stringWithFormat:@"%@ (%@)",error.messageToDisplay,[error stringErrorCode]]; + self.actions = @[[UIAlertAction actionWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedOK] style:UIAlertActionStyleDefault handler:nil]]; + self.isGreedy = isGreedy; + self.type = MFAlertTypePopup; + } + return self; +} + +- (nullable instancetype)initPopupAlertWithError:(nullable MVMCoreErrorObject *)error actions:(nonnull NSArray *)actions isGreedy:(BOOL)isGreedy { + + if (self = [super init]) { + self.title = error.title; + self.message = [NSString stringWithFormat:@"%@ (%@)",error.messageToDisplay,[error stringErrorCode]]; + self.actions = actions; + self.isGreedy = isGreedy; + self.type = MFAlertTypePopup; + } + return self; +} + ++ (nullable instancetype)alertObjectWithPage:(nullable NSDictionary *)page isGreedy:(BOOL)isGreedy additionalData:(nullable NSDictionary *)additionalData delegate:(nullable NSObject *)delegate error:(MVMCoreErrorObject *_Nullable *_Nullable)error { + + MVMCoreAlertObject *alert = [[MVMCoreAlertObject alloc] init]; + alert.title = [page stringForKey:KeyTitle]; + alert.message = [page stringForKey:KeyMessage]; + alert.isGreedy = isGreedy; + alert.type = MFAlertTypePopup; + + NSArray *actions = [page array:KeyLinks]; + NSMutableArray *actionsForAlert = [NSMutableArray array]; + for (NSDictionary *actionMap in actions) { + [actionsForAlert addObject:[UIAlertAction actionWithTitle:[actionMap stringForKey:KeyTitle] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [[MVMCoreActionHandler sharedActionHandler] handleActionWithDictionary:actionMap additionalData:additionalData delegate:delegate]; + }]]; + } + alert.actions = actionsForAlert; + + if ((alert.title.length > 0 || alert.message.length > 0) && alert.actions.count > 0) { + return alert; + } else { + if (error) { + *error = [[MVMCoreErrorObject alloc] initWithTitle:nil messageToLog:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodePopupFailed domain:ErrorDomainNative location:[NSString stringWithFormat:@"%@_Popup_pageType:%@",NSStringFromClass([delegate class]),[page stringForKey:KeyPageType]]]; + } + return nil; + } +} + +- (void)showAlert { + + switch (self.type) { + case MFAlertTypeField: + self.textFieldErrorHandler(self.fieldErrors); + break; + case MFAlertTypeTop: + [[MVMCoreAlertHandler sharedAlertHandler] showTopAlertWithObject:self.topAlertObject]; + break; + case MFAlertTypePopup: + [[MVMCoreAlertHandler sharedAlertHandler] showAlertWithAlertObject:self]; + break; + default: + break; + } +} + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertOperation.h b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertOperation.h new file mode 100644 index 0000000..e83ef07 --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertOperation.h @@ -0,0 +1,36 @@ +// +// MVMCoreAlertOperation.h +// myverizon +// +// Created by Scott Pfeil on 9/28/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// +// Operation for handling an alert. Should NOT be on the main queue. + +#import +#import +#import "MVMCoreOperation.h" +#import "MVMCoreAlertDelegateProtocol.h" + +@interface MVMCoreAlertOperation : MVMCoreOperation + +// If this operation is temporarily paused. +@property (readonly, getter=isPaused) BOOL paused; + +// If this alert is a greedy alert (See MVMCoreAlertHandler). +@property (readonly, getter=isGreedy) BOOL greedy; + +// The alert delegate if needed. +@property (readonly, nullable, nonatomic, weak) NSObject *alertDelegate; + +// Initializes the operation with the alert to display and if it is greedy or not. +- (nullable instancetype)initWithAlert:(nonnull UIAlertController *)alert isGreedy:(BOOL)isGreedy; +- (nullable instancetype)initWithAlert:(nonnull UIAlertController *)alert isGreedy:(BOOL)isGreedy alertDelegate:(nullable id )alertDelegate; + +// Pauses the operation. Temporarily removes any alert. +- (void)pause; + +// Unpauses the operation, resuming any alert. +- (void)unpause; + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreAlertOperation.m b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertOperation.m new file mode 100644 index 0000000..1a2901f --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreAlertOperation.m @@ -0,0 +1,242 @@ +// +// MVMCoreAlertOperation.m +// myverizon +// +// Created by Scott Pfeil on 9/28/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreAlertOperation.h" +#import "MVMCoreAlertHandler.h" +#import "MVMCoreAlertController.h" +#import "MVMCoreNavigationHandler.h" + +@interface MVMCoreAlertOperation () { + __block BOOL _paused; + __block BOOL _displayed; +} + +@property (readwrite, getter=isPaused) BOOL paused; + +@property (readwrite, getter=isGreedy) BOOL greedy; + +@property (readwrite, getter=isDisplayed) BOOL displayed; + +@property (readwrite, nullable, nonatomic, weak) NSObject *alertDelegate; + +// The currently displayed alert view. +@property (nullable, strong, nonatomic) UIAlertController *currentAlertView; + +// A boolean to keep track of if we alreadys signed up to observe. +@property (assign, nonatomic) BOOL alertBeingObserved; + +// For thread safety +@property (strong, nonatomic) dispatch_queue_t pausedQueue; +@property (strong, nonatomic) dispatch_queue_t displayedQueue; + +// Dismisses the alert. +- (void)dismissAlertView; + +// Begins observing for when the alert is dismissed. +- (void)observeForCurrentAlertViewDismissal; + +// Stops observing for when the alert is dismissed. +- (void)stopObservingAlertView; + +@end + +@implementation MVMCoreAlertOperation + +// The context for kvo +static void * XXContext = &XXContext; + +- (instancetype)init { + + self = [super init]; + if (self) { + self.pausedQueue = dispatch_queue_create("paused", DISPATCH_QUEUE_CONCURRENT); + self.displayedQueue = dispatch_queue_create("displayed", DISPATCH_QUEUE_CONCURRENT); + } + return self; +} + +- (nullable instancetype)initWithAlert:(nonnull UIAlertController *)alert isGreedy:(BOOL)isGreedy { + + if (self = [self init]) { + self.currentAlertView = alert; + self.greedy = isGreedy; + } + return self; +} + +- (nullable instancetype)initWithAlert:(nonnull UIAlertController *)alert isGreedy:(BOOL)isGreedy alertDelegate:(nullable NSObject *)alertDelegate { + if ([self initWithAlert:alert isGreedy:isGreedy]) { + self.alertDelegate = alertDelegate; + } + return self; +} + +- (void)dealloc { + [self stopObservingAlertView]; +} + +- (BOOL)isPaused { + __block BOOL isPaused; + dispatch_sync(self.pausedQueue, ^{ + isPaused = _paused; + }); + return isPaused; +} + +- (void)setPaused:(BOOL)paused { + dispatch_barrier_async(self.pausedQueue, ^{ + _paused = paused; + }); +} + +- (BOOL)isDisplayed { + __block BOOL isDisplayed; + dispatch_sync(self.displayedQueue, ^{ + isDisplayed = _displayed; + }); + return isDisplayed; +} + +- (void)setDisplayed:(BOOL)displayed { + dispatch_barrier_async(self.displayedQueue, ^{ + _displayed = displayed; + }); +} + +- (void)main { + + // Always check for cancellation before launching the task. + if ([self checkAndHandleForCancellation]) { + return; + } + + // Display alert only if alerts aren't supressed. + if (![[MVMCoreAlertHandler sharedAlertHandler] mfAlertsSupressed] && self.currentAlertView) { + + // Observe for when it is removed. + [self observeForCurrentAlertViewDismissal]; + + // Adds the presentation to the animation queue. + [[MVMCoreNavigationHandler sharedNavigationHandler] presentViewController:self.currentAlertView animated:YES delegate:nil completionHandler:^{ + + // We finished but it was not displayed yet. It's possible that it was cancelled. Finish this task + if (!self.isDisplayed) { + [self markAsFinished]; + } else if (self.isCancelled) { + [self dismissAlertView]; + } + }]; + } +} + +- (void)cancel { + [super cancel]; + + // Notify delegate that the alert is cancelled. + if ([self.alertDelegate respondsToSelector:@selector(alertCancelled:)]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.alertDelegate alertCancelled:self.currentAlertView]; + }); + } + + [self dismissAlertView]; +} + +- (void)dismissAlertView { + + if (self.isDisplayed) { + + // Dismisses. + [[MVMCoreNavigationHandler sharedNavigationHandler] dismissViewController:self.currentAlertView animated:YES]; + } +} + +- (void)pause { + [self willChangeValueForKey:@"isPaused"]; + self.paused = YES; + [self didChangeValueForKey:@"isPaused"]; + + // Notify delegate of pause. + if ([self.alertDelegate respondsToSelector:@selector(alertPaused:)]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.alertDelegate alertPaused:self.currentAlertView]; + }); + } + + // Dismiss until unpaused. + [self dismissAlertView]; +} + +- (void)unpause { + [self willChangeValueForKey:@"isPaused"]; + self.paused = NO; + [self didChangeValueForKey:@"isPaused"]; + + // Notify delegate of unpause. + if ([self.alertDelegate respondsToSelector:@selector(alertUnpaused:)]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.alertDelegate alertUnpaused:self.currentAlertView]; + }); + } + + // Show alert... + if (self.currentAlertView) { + [self start]; + } +} + +#pragma mark - Observer Functions + +- (void)observeForCurrentAlertViewDismissal { + if (!self.alertBeingObserved && ![[MVMCoreAlertHandler sharedAlertHandler] mfAlertsSupressed] && self.currentAlertView && [self.currentAlertView isKindOfClass:[UIAlertController class]]) { + self.alertBeingObserved = YES; + [self.currentAlertView addObserver:self forKeyPath:@"visible" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:XXContext]; + } +} + +- (void)stopObservingAlertView { + if (self.alertBeingObserved) { + [self.currentAlertView removeObserver:self forKeyPath:@"visible" context:XXContext]; + self.alertBeingObserved = NO; + } +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if (context == XXContext && [keyPath isEqualToString:@"visible"]) { + if (![object isVisible]) { + + self.displayed = NO; + + // Notify delegate that the alert is dismissed. + if ([self.alertDelegate respondsToSelector:@selector(alertDismissed:)]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.alertDelegate alertDismissed:self.currentAlertView]; + }); + } + + // Is visible was set to NO, meaning that the alertview is no longer visible. + if (!self.isPaused) { + [self stopObservingAlertView]; + self.currentAlertView = nil; + [self markAsFinished]; + } + } else { + + self.displayed = YES; + + // Notify delegate that the alert is shown. + if ([self.alertDelegate respondsToSelector:@selector(alertShown:)]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.alertDelegate alertShown:self.currentAlertView]; + }); + } + } + } +} + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertAnimationDelegateProtocol.h b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertAnimationDelegateProtocol.h new file mode 100644 index 0000000..bb9f6df --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertAnimationDelegateProtocol.h @@ -0,0 +1,22 @@ +// +// MVMCoreTopAlertAnimationDelegateProtocol.h +// mobilefirst +// +// Created by Scott Pfeil on 6/4/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import + +@protocol MVMCoreTopAlertAnimationDelegateProtocol + +// Called when the top alert is starting an animation +- (void)topAlertViewBeginAnimation; + +// Called when the top alert is ending an animation +- (void)topAlertViewFinishAnimation; + +// Called when the top alert close button was pressed by the user +- (void)topAlertCloseButtonPressed; + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertDelegateProtocol.h b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertDelegateProtocol.h new file mode 100644 index 0000000..4e3158f --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertDelegateProtocol.h @@ -0,0 +1,20 @@ +// +// MVMCoreTopAlertDelegateProtocol.h +// mobilefirst +// +// Created by Scott Pfeil on 6/4/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import + +@class MVMCoreTopAlertObject; + +@protocol MVMCoreTopAlertDelegateProtocol + +@optional + +- (void)topAlertViewShown:(nonnull id)topAlert topAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject; +- (void)topAlertViewDismissed:(nonnull id)topAlert; + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertObject.h b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertObject.h new file mode 100644 index 0000000..bb5c2e3 --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertObject.h @@ -0,0 +1,72 @@ +// +// MVMCoreTopAlertObject.h +// mobilefirst +// +// Created by Scott Pfeil on 5/24/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import +#import + +#warning Scott, Drive this from protocol. +typedef NS_ENUM(NSInteger, MFTopType) { + MFTopTypeError, + MFTopTypeConfirmation, + MFTopTypeSurvival, + MFTopTypeShop, + MFTopTypeBlueWithButton, + MFTopTypeClearspotMain, + MFTopTypeClearspotFinished, + MFTopTypeClearspotProcessing, + MFTopTypeRemoteIn, + MFTopTypeAgentChatInSession, + MFTopTypeFamilyBaseChild, + MFTopTypeRewardAlert, + MFTopTypeOther +}; + +extern NSUInteger const TopAlertDismissTime; + +@interface MVMCoreTopAlertObject : NSObject + +@property (nonatomic) BOOL persistent; +@property (nullable, nonatomic) NSString *type; + +// The text +@property (nullable, strong, nonatomic) NSString *title; +@property (nullable, strong, nonatomic) NSString *message; +@property (nullable, strong, nonatomic) NSString *topMessage; + +// For the button. +@property (nullable, strong, nonatomic) NSDictionary *buttonMap; +@property (nullable, strong, nonatomic) NSDictionary *additionalData; +@property (nonatomic) BOOL useCloseButton; + +// For non action map driven button. +@property (nullable, strong, nonatomic) NSString *buttonTitle; +@property (nullable, copy, nonatomic) void (^userActionHandler)(id _Nonnull sender); + +@property (nullable, weak, nonatomic) NSObject *delegate; + +// This is used to ensure legacy style stays intact with new changes +@property (nonatomic) BOOL useNewStyle; + +// The page type used for the top alert +@property (nullable, strong, nonatomic) NSString *pageType; + +// image name or url used when iconView is an image +@property (nullable, strong, nonatomic) NSString *imageNameOrURL; + +// If 0, uses default 5 seconds. +@property (nonatomic) NSInteger topAlertDismissTime; + +- (nullable instancetype)initWithResponseInfo:(nullable NSDictionary *)responseInfo; + +- (nullable instancetype)initWithType:(MFTopType)type message:(nullable NSString *)message; + +- (nullable instancetype)initWithType:(MFTopType)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData; + +- (nullable instancetype)initWithType:(MFTopType)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler; + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertObject.m b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertObject.m new file mode 100644 index 0000000..2359d9e --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertObject.m @@ -0,0 +1,85 @@ +// +// MVMCoreTopAlertObject.m +// mobilefirst +// +// Created by Scott Pfeil on 5/24/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreTopAlertObject.h" +#import +#import "MVMCoreAlertHandler.h" +#import "MVMCoreJSONConstants.h" + +NSUInteger const TopAlertDismissTime = 5; + +@implementation MVMCoreTopAlertObject + +- (nullable instancetype)initWithResponseInfo:(nullable NSDictionary *)responseInfo { + if (self = [super init]) { + + self.type = [responseInfo string:KeyType]; + self.persistent = [[responseInfo stringForKey:KeyMessageStyle] isEqualToString:ValueMessageStyleTopPersistent]; + + self.title = [responseInfo string:KeyMessage]; + self.message = [responseInfo string:KeyUserMessage]; + self.buttonMap = [responseInfo dict:KeyButtonMap]; + self.topMessage = [responseInfo string:KeyTopMessage]; + self.imageNameOrURL = [responseInfo string:@"topAlertImageUrl"]; + + // The default is yes if not sent by server (for legacy to work as is) + NSNumber *closeButton = [responseInfo objectForKey:KeyCloseButton ofType:[NSNumber class]]; + if (closeButton) { + self.useCloseButton = [closeButton boolValue]; + } else { + self.useCloseButton = YES; + } + + self.useNewStyle = [responseInfo boolForKey:@"newTopAlertStyle"]; + + // Server driven dismiss time. + if (self.useNewStyle) { + NSNumber *topAlertTime = [responseInfo objectForKey:@"topAlertTime" ofType:[NSNumber class]]; + if (topAlertTime) { + self.topAlertDismissTime = [topAlertTime integerValue]; + } + } + } + return self; +} + +- (nullable instancetype)initWithType:(MFTopType)type message:(nullable NSString *)message { + return [self initWithType:type message:nil subMessage:message persistent:NO actionMap:nil additionalData:nil]; +} + +- (nullable instancetype)initWithType:(MFTopType)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent actionMap:(nullable NSDictionary *)actionMap additionalData:(nullable NSDictionary *)additionalData { + + if (self = [super init]) { + + self.type = [MVMCoreAlertHandler stringForTopType:type]; + self.persistent = persistent; + + self.title = message; + self.message = subMessage; + self.buttonMap = actionMap; + self.additionalData = additionalData; + } + return self; +} + +- (nullable instancetype)initWithType:(MFTopType)type message:(nullable NSString *)message subMessage:(nullable NSString *)subMessage persistent:(BOOL)persistent buttonTitle:(nullable NSString *)buttonTitle userActionHandler:(nullable void (^)(id _Nonnull sender))userActionHandler { + + if (self = [super init]) { + + self.type = [MVMCoreAlertHandler stringForTopType:type]; + self.persistent = persistent; + + self.title = message; + self.message = subMessage; + self.buttonTitle = buttonTitle; + self.userActionHandler = userActionHandler; + } + return self; +} + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.h b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.h new file mode 100644 index 0000000..82cba7d --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.h @@ -0,0 +1,30 @@ +// +// MVMCoreTopAlertOperation.h +// mobilefirst +// +// Created by Scott Pfeil on 6/4/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreOperation.h" +#import "MVMCoreTopAlertAnimationDelegateProtocol.h" + +@class MVMCoreTopAlertObject; + +@interface MVMCoreTopAlertOperation : MVMCoreOperation + +@property (readonly, getter=isPaused) BOOL paused; + +@property (nonatomic) BOOL reAddAfterCancel; + +@property (nonnull, nonatomic, strong) MVMCoreTopAlertObject *topAlertObject; + +- (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject; + +// Pauses the operation. Temporarily removes any alert. +- (void)pause; + +// Unpauses the operation, resuming any alert. +- (void)unpause; + +@end diff --git a/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.m b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.m new file mode 100644 index 0000000..db23ce2 --- /dev/null +++ b/MVMCore/MVMCore/AlertHandling/MVMCoreTopAlertOperation.m @@ -0,0 +1,274 @@ +// +// MVMCoreTopAlertOperation.m +// mobilefirst +// +// Created by Scott Pfeil on 6/4/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreTopAlertOperation.h" +#import "MVMCoreTopAlertObject.h" +#import "MVMCoreAlertHandler.h" +#import "MVMCoreObject.h" +#import "MVMCoreTopAlertView.h" +#import "MVMCoreSessionObject.h" + +@interface MVMCoreTopAlertOperation () { + __block BOOL _paused; + __block BOOL _displayed; + __block BOOL _animating; + __block NSTimer *_dismissTimer; +} + +@property (readwrite, getter=isPaused) BOOL paused; + +@property (readwrite, getter=isDisplayed) BOOL displayed; + +@property (readwrite, getter=isAnimating) BOOL animating; + +@property (strong, nonatomic) NSTimer *dismissTimer; + +// For thread safety +@property (strong, nonatomic) dispatch_queue_t pausedQueue; +@property (strong, nonatomic) dispatch_queue_t displayedQueue; +@property (strong, nonatomic) dispatch_queue_t animatingQueue; +@property (strong, nonatomic) dispatch_queue_t timerQueue; + +@end + +@implementation MVMCoreTopAlertOperation + +- (instancetype)init { + + self = [super init]; + if (self) { + self.pausedQueue = dispatch_queue_create("paused", DISPATCH_QUEUE_CONCURRENT); + self.displayedQueue = dispatch_queue_create("displayed", DISPATCH_QUEUE_CONCURRENT); + self.animatingQueue = dispatch_queue_create("animating", DISPATCH_QUEUE_CONCURRENT); + self.timerQueue = dispatch_queue_create("timer", DISPATCH_QUEUE_CONCURRENT); + } + return self; +} + +- (nullable instancetype)initWithTopAlertObject:(nonnull MVMCoreTopAlertObject *)topAlertObject { + + if (self = [self init]) { + self.topAlertObject = topAlertObject; + + // Sets the queue priority for various types of alerts. + switch ([MVMCoreAlertHandler topTypeForString:topAlertObject.type]) { + case MFTopTypeSurvival: + self.queuePriority = NSOperationQueuePriorityVeryLow; + break; + case MFTopTypeShop: + self.queuePriority = NSOperationQueuePriorityLow; + break; + case MFTopTypeClearspotProcessing: + self.queuePriority = NSOperationQueuePriorityNormal; + break; + case MFTopTypeClearspotMain: + self.queuePriority = NSOperationQueuePriorityNormal; + break; + case MFTopTypeAgentChatInSession: + self.queuePriority = NSOperationQueuePriorityHigh - 1; + case MFTopTypeRemoteIn: + self.queuePriority = NSOperationQueuePriorityHigh; + break; + case MFTopTypeFamilyBaseChild: + self.queuePriority = NSOperationQueuePriorityHigh + 1; + break; + default: + if (topAlertObject.persistent) { + self.queuePriority = NSOperationQueuePriorityVeryHigh - 1; + } else { + self.queuePriority = NSOperationQueuePriorityVeryHigh; + } + break; + } + } + return self; +} + +- (BOOL)isPaused { + __block BOOL isPaused; + dispatch_sync(self.pausedQueue, ^{ + isPaused = _paused; + }); + return isPaused; +} + +- (void)setPaused:(BOOL)paused { + dispatch_barrier_async(self.pausedQueue, ^{ + _paused = paused; + }); +} + +- (BOOL)isDisplayed { + __block BOOL isDisplayed; + dispatch_sync(self.displayedQueue, ^{ + isDisplayed = _displayed; + }); + return isDisplayed; +} + +- (void)setDisplayed:(BOOL)displayed { + dispatch_barrier_async(self.displayedQueue, ^{ + _displayed = displayed; + }); +} + +- (BOOL)isAnimating { + __block BOOL isAnimating; + dispatch_sync(self.animatingQueue, ^{ + isAnimating = _animating; + }); + return isAnimating; +} + +- (void)setAnimating:(BOOL)animating { + dispatch_barrier_async(self.animatingQueue, ^{ + _animating = animating; + }); +} + +- (NSTimer *)dismissTimer { + __block NSTimer *timer; + dispatch_sync(self.timerQueue, ^{ + timer = _dismissTimer; + }); + return timer; +} + +- (void)setDismissTimer:(NSTimer *)dismissTimer { + dispatch_barrier_async(self.timerQueue, ^{ + _dismissTimer = dismissTimer; + }); +} + +- (void)main { + + // Always check for cancellation before launching the task. + if ([self checkAndHandleForCancellation]) { + return; + } + + // Do nothing if paused + if (!self.isPaused) { + + // Display alert only if alerts aren't supressed. + if (![[MVMCoreAlertHandler sharedAlertHandler] mfAlertsSupressed]) { + + // Show + if (![MVMCoreObject sharedInstance].topAlertView) { + + // Needs to be a top alert view.... + [self markAsFinished]; + } else { + [[MVMCoreObject sharedInstance].topAlertView showWithTopAlertObject:self.topAlertObject animationDelegate:self completionHandler:^(BOOL finished) { + + self.displayed = YES; + if (self.isCancelled) { + + // Cancelled, dismiss immediately. + [self dismissAlertView:YES]; + } else if (self.isPaused) { + + // Paused, dismiss for the time being if persistent. + [self dismissAlertView:!self.topAlertObject.persistent]; + } else if (!self.topAlertObject.persistent) { + + // Set timer to dismiss top alert if it's not persistent (or it's survival mode and not short bar). + NSInteger dismissTime; + if (self.topAlertObject.topAlertDismissTime > 0) { + dismissTime = self.topAlertObject.topAlertDismissTime; + } else { + dismissTime = TopAlertDismissTime; + } + self.dismissTimer = [NSTimer scheduledTimerWithTimeInterval:dismissTime target:self selector:@selector(timerFinished) userInfo:nil repeats:NO]; + } + }]; + } + } else { + [self pause]; + } + } +} + +- (void)timerFinished { + [self dismissAlertView:YES]; +} + +- (void)cancel { + [super cancel]; + + // Kill the dismiss timer. + if (self.dismissTimer) { + [self.dismissTimer invalidate]; + self.dismissTimer = nil; + } + + // Do nothing if animating. + if (!self.isAnimating) { + + if (self.isDisplayed) { + [self dismissAlertView:YES]; + } else if (self.isExecuting) { + [self markAsFinished]; + } + } +} + +- (void)dismissAlertView:(BOOL)andFinish { + + self.dismissTimer = nil; + + if (self.isDisplayed && !self.isAnimating) { + + // Dismisses. + [[MVMCoreObject sharedInstance].topAlertView hideAlertView:^(BOOL finished) { + + self.displayed = NO; + if (andFinish) { + [self markAsFinished]; + } + }]; + } +} + +- (void)pause { + + if (!self.paused) { + [self willChangeValueForKey:@"isPaused"]; + self.paused = YES; + [self didChangeValueForKey:@"isPaused"]; + } + [self dismissAlertView:!self.topAlertObject.persistent]; +} + +- (void)unpause { + + if (self.paused) { + [self willChangeValueForKey:@"isPaused"]; + self.paused = NO; + [self didChangeValueForKey:@"isPaused"]; + } + if (self.executing) { + [self start]; + } +} + +#pragma mark - Delegate functions + +- (void)topAlertViewBeginAnimation { + self.animating = YES; +} + +- (void)topAlertViewFinishAnimation { + self.animating = NO; +} + +- (void)topAlertCloseButtonPressed { + [self dismissAlertView:YES]; +} + +@end diff --git a/MVMCore/MVMCore/Categories/NSArray+MFConvenience.h b/MVMCore/MVMCore/Categories/NSArray+MFConvenience.h new file mode 100644 index 0000000..73a9c89 --- /dev/null +++ b/MVMCore/MVMCore/Categories/NSArray+MFConvenience.h @@ -0,0 +1,43 @@ +// +// NSArray+MFConvenience.h +// myverizon +// +// Created by Scott Pfeil on 12/5/13. +// Copyright (c) 2013 Verizon Wireless. All rights reserved. +// +// Adds frequently used convenience methods to NSArray. + +#import +#import + +@interface NSArray (MFConvenience) + +// Gets the object from the array and verfies that it is of a given type. +- (nullable id)objectAtIndex:(NSUInteger)index ofType:(nonnull Class)type; + +// shorthand for objectAtIndex +- (nullable NSString *)optionalStringAtIndex:(NSUInteger)index; +- (nullable NSDictionary *)dictionaryAtIndex:(NSUInteger)index; +- (nullable NSArray *)arrayAtIndex:(NSUInteger)index; +- (BOOL)boolAtIndex:(NSUInteger)index; +- (NSInteger)integerAtIndex:(NSUInteger)index; +- (CGFloat)floatAtIndex:(NSUInteger)index; +- (double)doubleAtIndex:(NSUInteger)index; + +// Gets an object that is nested using a series of keys or indexes to reach it. +// All keys should be of type NSString and is used for nested dictionaries. +// All indexes should be of type NSNumber and is used for nested arrays. +// Root object should be either a NSDictionary or NSArray. +- (nullable id)objectChainOfIndexesOrKeys:(nonnull NSArray *)indexesOrKeys; + +// Gets an object that is nested using a series of keys or indexes to reach it and verifies it is of a specific type. +// See objectChainOfIndexesOrKeys +- (nullable id)objectChainOfIndexesOrKeys:(nonnull NSArray *)indexesOrKeys ofType:(nonnull Class)type; + +// Gets the string object at a given index. Returns empty string if object at index is not a valid string. +- (nonnull NSString *)stringAtIndex:(NSUInteger)index; + +// Maps this array to a new array of T objects given a conversion block. Objective-C implementation of Swift's map. https://developer.apple.com/documentation/swift/array/2908681-map +- (nonnull NSArray *) map:(id _Nullable (^_Nonnull)(id _Nonnull obj))mapBlock; + +@end diff --git a/MVMCore/MVMCore/Categories/NSArray+MFConvenience.m b/MVMCore/MVMCore/Categories/NSArray+MFConvenience.m new file mode 100644 index 0000000..8810bea --- /dev/null +++ b/MVMCore/MVMCore/Categories/NSArray+MFConvenience.m @@ -0,0 +1,99 @@ +// +// NSArray+MFConvenience.m +// myverizon +// +// Created by Scott Pfeil on 12/5/13. +// Copyright (c) 2013 Verizon Wireless. All rights reserved. +// + +#import + +@implementation NSArray (MFConvenience) + +- (nullable id)objectAtIndex:(NSUInteger)index ofType:(nonnull Class)type { + id theObject = nil; + + if ([self count] > index) { + theObject = [self objectAtIndex:index]; + } + + return ([theObject isKindOfClass:type] ? theObject : nil); +} + +- (nonnull NSString *)stringAtIndex:(NSUInteger)index { + id object = [self objectAtIndex:index ofType:[NSString class]]; + + return (object ? object : @""); +} + +- (nullable NSString *)optionalStringAtIndex:(NSUInteger)index { + return [self objectAtIndex:index ofType:[NSString class]]; +} + +- (nullable NSDictionary *)dictionaryAtIndex:(NSUInteger)index { + return [self objectAtIndex:index ofType:[NSDictionary class]]; +} + +- (nullable NSArray *)arrayAtIndex:(NSUInteger)index { + return [self objectAtIndex:index ofType:[NSArray class]]; +} + +- (BOOL)boolAtIndex:(NSUInteger)index { + return [[self objectAtIndex:index ofType:[NSNumber class]] boolValue]; +} + +- (NSInteger)integerAtIndex:(NSUInteger)index { + return [[self objectAtIndex:index ofType:[NSNumber class]] integerValue]; +} + +- (CGFloat)floatAtIndex:(NSUInteger)index { + return [[self objectAtIndex:index ofType:[NSNumber class]] floatValue]; +} + +- (double)doubleAtIndex:(NSUInteger)index { + return [[self objectAtIndex:index ofType:[NSNumber class]] doubleValue]; +} + +- (nullable id)objectChainOfIndexesOrKeys:(nonnull NSArray *)indexesOrKeys { + + __block id previousObject = self; + [indexesOrKeys enumerateObjectsUsingBlock:^(id currentKeyOrIndex, NSUInteger index, BOOL *stop){ + + if ([currentKeyOrIndex isKindOfClass:[NSNumber class]] && [previousObject isKindOfClass:[NSArray class]]) { + + NSInteger i = [currentKeyOrIndex integerValue]; + if (i < [previousObject count]) { + + // If it is a number key and the previous object is an array, grab the next object. + previousObject = [previousObject objectAtIndex:i]; + } else { + previousObject = nil; + *stop = YES; + } + } else if ([currentKeyOrIndex isKindOfClass:[NSString class]] && [previousObject isKindOfClass:[NSDictionary class]]) { + + // If it is a string key and the previous object in the chain is a dictionary, grab the next object. + previousObject = [previousObject objectForKey:currentKeyOrIndex]; + } else { + previousObject = nil; + *stop = YES; + } + } ]; + + return previousObject; +} + +- (nullable id)objectChainOfIndexesOrKeys:(nonnull NSArray *)indexesOrKeys ofType:(nonnull Class)type { + id object = [self objectChainOfIndexesOrKeys:indexesOrKeys]; + return ([object isKindOfClass:type] ? object : nil); +} + +- (NSArray *) map:(id(^)(id))mapBlock { + NSMutableArray *mapped = [NSMutableArray arrayWithCapacity:[self count]]; + for (id obj in self) { + [mapped addObject:mapBlock(obj)]; + } + return mapped; +} + +@end diff --git a/MVMCore/MVMCore/Categories/NSDecimalNumber+MFConvenience.h b/MVMCore/MVMCore/Categories/NSDecimalNumber+MFConvenience.h new file mode 100644 index 0000000..86f7006 --- /dev/null +++ b/MVMCore/MVMCore/Categories/NSDecimalNumber+MFConvenience.h @@ -0,0 +1,16 @@ +// +// NSDecimalNumber+MFConvenience.h +// mobilefirst +// +// Created by Yang, Tianhang (Chris) on 10/5/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import + +@interface NSDecimalNumber (MFConvenience) + +//function to prevent iphone 5 crash when using default decimalNumberWithString function ++ (nullable NSDecimalNumber *)mfDecimalNumberWithString:(nullable NSString *)numberValue; + +@end diff --git a/MVMCore/MVMCore/Categories/NSDecimalNumber+MFConvenience.m b/MVMCore/MVMCore/Categories/NSDecimalNumber+MFConvenience.m new file mode 100644 index 0000000..78b69ff --- /dev/null +++ b/MVMCore/MVMCore/Categories/NSDecimalNumber+MFConvenience.m @@ -0,0 +1,23 @@ +// +// NSDecimalNumber+MFConvenience.m +// mobilefirst +// +// Created by Yang, Tianhang (Chris) on 10/5/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import + +@implementation NSDecimalNumber (MFConvenience) + + ++ (NSDecimalNumber *)mfDecimalNumberWithString:(nullable NSString *)numberValue { + + if (numberValue.length == 0) { + numberValue = @"0"; + } + return [NSDecimalNumber decimalNumberWithString:numberValue]; + +} + +@end diff --git a/MVMCore/MVMCore/Categories/NSDictionary+MFConvenience.h b/MVMCore/MVMCore/Categories/NSDictionary+MFConvenience.h new file mode 100644 index 0000000..ed5931f --- /dev/null +++ b/MVMCore/MVMCore/Categories/NSDictionary+MFConvenience.h @@ -0,0 +1,66 @@ +// +// NSNSDictionary+Convenience.h +// myverizon +// +// Created by Scott Pfeil on 11/12/13. +// Copyright (c) 2013 Verizon Wireless. All rights reserved. +// +// Adds frequently used convenience methods to NSDictionary. + +#import +#import + +@interface NSDictionary (MFConvenience) + +// Gets the object from the dictionary and verfies that it is of a given type. +- (nullable id)objectForKey:(nonnull id)key ofType:(nonnull Class)type; + +// shorthand for objectForKey +- (nullable NSString *)string:(nonnull id)key; +- (nullable id)dict:(nonnull id)key; +- (nullable id)array:(nonnull id)key; +- (BOOL)boolForKey:(nonnull id)key; +- (NSInteger)integer:(nonnull id)key; +- (CGFloat)floatForKey:(nonnull id)key; +- (double)doubleForKey:(nonnull id)key; + +// Gets an object that is nested using a series of keys or indexes to reach it. +// All keys should be of type NSString and is used for nested dictionaries. +// All indexes should be of type NSNumber and is used for nested arrays. +// Root object should be either a NSDictionary or NSArray. +- (nullable id)objectChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes; + +// Gets an object that is nested using a series of keys or indexes to reach it and verifies it is of a specific type. +// See objectChainOfKeysOrIndexes +- (nullable id)objectChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes ofType:(nonnull Class)type; + +// Returns array or nil +// See objectChainOfKeysOrIndexes +- (nullable NSArray *)arrayForChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes; + +// Returns dictionary or nil. +// See objectChainOfKeysOrIndexes +- (nullable NSDictionary *)dictWithChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes; + +// Returns a non-nil dictionary, Can be used if trying to add dictionary object to a dictionary or array +// See objectChainOfKeysOrIndexes +- (nonnull NSDictionary *)dictionaryWithChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes; + +// Gets string that is nested using a series of keys or indexes to reach it. +// See objectChainOfKeysOrIndexes +- (nonnull NSString *)stringForChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes; + +// Gets the string object for a given key. Returns empty string if object for key is not a valid string. +- (nonnull NSString *)stringForKey:(nonnull NSString *)key; + +// Gets the dictionary object for a given key. Returns empty dictionary if object for key is not a valid dictionary. +- (nonnull NSDictionary *)dictionaryForKey:(nonnull NSString *)key; + +// Gets the array object for a given key. Returns empty array if object for key is not a valid array. +- (nonnull NSArray *)arrayForKey:(nonnull NSString *)key; + +// Gets bool that is nested using a series of keys or indexes to reach it. +// See objectChainOfKeysOrIndexes +- (BOOL)boolForChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes; + +@end diff --git a/MVMCore/MVMCore/Categories/NSDictionary+MFConvenience.m b/MVMCore/MVMCore/Categories/NSDictionary+MFConvenience.m new file mode 100644 index 0000000..8de5180 --- /dev/null +++ b/MVMCore/MVMCore/Categories/NSDictionary+MFConvenience.m @@ -0,0 +1,113 @@ +// +// NSDictionary+MFConvenience.m +// myverizon +// +// Created by Scott Pfeil on 11/12/13. +// Copyright (c) 2013 Verizon Wireless. All rights reserved. +// + +#import + +@implementation NSDictionary (MFConvenience) + +- (nullable id)objectForKey:(nonnull id)key ofType:(nonnull Class)type { + id theObject = [self objectForKey:key]; + + return ([theObject isKindOfClass:type] ? theObject : nil); +} + +- (nullable NSString *)string:(nonnull id)key { + return [self objectForKey:key ofType:[NSString class]]; +} + +- (nullable id)dict:(nonnull id)key { + return [self objectForKey:key ofType:[NSDictionary class]]; +} + +- (nullable id)array:(nonnull id)key { + return [self objectForKey:key ofType:[NSArray class]]; +} + +- (BOOL)boolForKey:(nonnull id)key { + return [[self objectForKey:key ofType:[NSNumber class]] boolValue]; +} + +- (CGFloat)floatForKey:(nonnull id)key { + return [[self objectForKey:key ofType:[NSNumber class]] floatValue]; +} + +- (double)doubleForKey:(id)key { + return [[self objectForKey:key ofType:[NSNumber class]] doubleValue]; +} + +- (NSInteger)integer:(nonnull id)key { + return [[self objectForKey:key ofType:[NSNumber class]] integerValue]; +} + +- (nullable id)objectChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes { + + __block id previousObject = self; + [keysOrIndexes enumerateObjectsUsingBlock:^(id currentKeyOrIndex, NSUInteger index, BOOL *stop){ + + if ([currentKeyOrIndex isKindOfClass:[NSString class]] && [previousObject isKindOfClass:[NSDictionary class]]) { + + // If it is a string key and the previous object in the chain is a dictionary, grab the next object. + previousObject = [previousObject objectForKey:currentKeyOrIndex]; + } else if ([currentKeyOrIndex isKindOfClass:[NSNumber class]] && [previousObject isKindOfClass:[NSArray class]]) { + + NSInteger i = [currentKeyOrIndex integerValue]; + if (i < [previousObject count]) { + + // If it is a number key and the previous object is an array, grab the next object. + previousObject = [previousObject objectAtIndex:i]; + } else { + previousObject = nil; + *stop = YES; + } + } else { + previousObject = nil; + *stop = YES; + } + } ]; + + return previousObject; +} + +- (nullable id)objectChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes ofType:(nonnull Class)type { + id object = [self objectChainOfKeysOrIndexes:keysOrIndexes]; + return ([object isKindOfClass:type] ? object : nil); +} + +- (nullable NSArray *)arrayForChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes { + return [self objectChainOfKeysOrIndexes:keysOrIndexes ofType:[NSArray class]]; +} + +- (nullable NSDictionary *)dictWithChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes { + return [self objectChainOfKeysOrIndexes:keysOrIndexes ofType:[NSDictionary class]]; +} + +- (nonnull NSDictionary *)dictionaryWithChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes { + return [self dictWithChainOfKeysOrIndexes:keysOrIndexes] ?: @{}; +} + +- (nonnull NSString *)stringForChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes { + return ([self objectChainOfKeysOrIndexes:keysOrIndexes ofType:[NSString class]] ?: @""); +} + +- (BOOL)boolForChainOfKeysOrIndexes:(nonnull NSArray *)keysOrIndexes { + return [[self objectChainOfKeysOrIndexes:keysOrIndexes ofType:[NSNumber class]] boolValue]; +} + +- (nonnull NSString *)stringForKey:(nonnull NSString *)key { + return ([self objectForKey:key ofType:[NSString class]] ?: @""); +} + +- (nonnull NSDictionary *)dictionaryForKey:(nonnull NSString *)key { + return ([self dict:key] ?: @{}); +} + +- (nonnull NSArray *)arrayForKey:(nonnull NSString *)key { + return ([self array:key] ?: @[]); +} + +@end diff --git a/MVMCore/MVMCore/Categories/UIFont+MFSpacing.h b/MVMCore/MVMCore/Categories/UIFont+MFSpacing.h new file mode 100644 index 0000000..db3c32c --- /dev/null +++ b/MVMCore/MVMCore/Categories/UIFont+MFSpacing.h @@ -0,0 +1,15 @@ +// +// UIFont+UIFont_Spacing.h +// mobilefirst +// +// Created by Yang, Tianhang (Chris) on 7/19/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import + +@interface UIFont (MFSpacing) + +- (nonnull UIFont *)monospacedDigitFont; + +@end diff --git a/MVMCore/MVMCore/Categories/UIFont+MFSpacing.m b/MVMCore/MVMCore/Categories/UIFont+MFSpacing.m new file mode 100644 index 0000000..d62376b --- /dev/null +++ b/MVMCore/MVMCore/Categories/UIFont+MFSpacing.m @@ -0,0 +1,24 @@ +// +// UIFont+UIFont_Spacing.m +// mobilefirst +// +// Created by Yang, Tianhang (Chris) on 7/19/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import + +#import + +@implementation UIFont (MFSpacing) + +- (UIFont *)monospacedDigitFont { + UIFontDescriptor *old = [[UIFontDescriptor alloc]init]; + NSDictionary *attributes = @{UIFontFeatureTypeIdentifierKey:@(kNumberSpacingType), + UIFontFeatureSelectorIdentifierKey: @(kMonospacedNumbersSelector) + }; + UIFontDescriptor *new = [old fontDescriptorByAddingAttributes:@{UIFontDescriptorFeatureSettingsAttribute:attributes}]; + return [UIFont fontWithDescriptor:new size:self.pointSize]; +} + +@end diff --git a/MVMCore/MVMCore/Categories/UILabel+MFCustom.h b/MVMCore/MVMCore/Categories/UILabel+MFCustom.h new file mode 100644 index 0000000..c40a313 --- /dev/null +++ b/MVMCore/MVMCore/Categories/UILabel+MFCustom.h @@ -0,0 +1,15 @@ +// +// UILabel+MFCustom.h +// myverizon +// +// Created by Chris Yang on 2/1/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import + +@interface UILabel (MFCustom) + +- (CGRect)boundingRectForCharacterRange:(NSRange)range; + +@end diff --git a/MVMCore/MVMCore/Categories/UILabel+MFCustom.m b/MVMCore/MVMCore/Categories/UILabel+MFCustom.m new file mode 100644 index 0000000..f1575b9 --- /dev/null +++ b/MVMCore/MVMCore/Categories/UILabel+MFCustom.m @@ -0,0 +1,67 @@ +// +// UILabel+MFCustom.m +// myverizon +// +// Created by Chris Yang on 2/1/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import + + +@implementation UILabel (MFCustom) + +- (CGRect)boundingRectForCharacterRange:(NSRange)range +{ + + NSMutableAttributedString *attribute; + + // consider text alignment for the label into account + if (self.textAlignment != NSTextAlignmentLeft || self.textAlignment != NSTextAlignmentNatural) { + + __block NSMutableParagraphStyle *style; + + [self.attributedText enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, self.attributedText.length) options:0 usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) { + if ([value isKindOfClass:[NSMutableParagraphStyle class]]) { + style = value; + *stop = YES; + } + + }]; + + if (!style) { + style = [[NSMutableParagraphStyle alloc]init]; + } + + style.alignment = self.textAlignment; + + NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc]initWithAttributedString:self.attributedText]; + [attributeString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, self.attributedText.length)]; + + attribute = [[NSMutableAttributedString alloc]initWithAttributedString:attributeString]; + } else { + attribute = [[NSMutableAttributedString alloc]initWithAttributedString:self.attributedText]; + } + + + NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attribute]; + + NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; + [textStorage addLayoutManager:layoutManager]; + + NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:self.bounds.size]; + textContainer.lineFragmentPadding = 0; + textContainer.lineBreakMode = self.lineBreakMode; + + + [layoutManager addTextContainer:textContainer]; + + NSRange glyphRange; + + // Convert the range for glyphs. + [layoutManager characterRangeForGlyphRange:range actualGlyphRange:&glyphRange]; + + return [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; +} + +@end diff --git a/MVMCore/MVMCore/Constants/MVMCoreConstants.h b/MVMCore/MVMCore/Constants/MVMCoreConstants.h new file mode 100644 index 0000000..aa44293 --- /dev/null +++ b/MVMCore/MVMCore/Constants/MVMCoreConstants.h @@ -0,0 +1,47 @@ +// +// MVMCoreConstants.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/14/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import +#import + +typedef NS_ENUM(NSInteger, MVMAppContext) { + MVMAppContextNone = 0, + MVMAppContextMF, + MVMAppContextMVMLegacy, + MVMAppContextMFPrepay, + MVMAppContextHybrid, + MVMAppContextHybridPrepay, + MVMAppContextContentTransfer +}; + +typedef NS_ENUM(NSInteger, NavigationType) { + NavigationTypePush = 0, + NavigationTypeSet, + NavigationTypeReplaceTop, + NavigationTypeReplaceAfterRoot, + NavigationTypeReplaceElsePush, + NavigationTypePop, + NavigationTypePopSpecific, + NavigationTypePopTo, + NavigationTypePopToRoot +}; + +extern NSString * const ParameterMDN; + +#pragma mark - URL MFConstants + +// Server URLs +extern NSString * const URLProdPostpayBase; +extern NSString * const URLComponentNativeServer; +extern NSString * const URLComponentPrepayNativeServer; +extern NSString * const URLComponentKeepAlive; + +#pragma mark - Notification Names + +extern NSString * const NotificationResponseLoaded; +extern NSString * const MVMCoreNotificationGoingToServer; diff --git a/MVMCore/MVMCore/Constants/MVMCoreConstants.m b/MVMCore/MVMCore/Constants/MVMCoreConstants.m new file mode 100644 index 0000000..b0b9d17 --- /dev/null +++ b/MVMCore/MVMCore/Constants/MVMCoreConstants.m @@ -0,0 +1,31 @@ +// +// MVMCoreConstants.m +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/14/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import "MVMCoreConstants.h" + +// Time before Timeout in seconds. +NSTimeInterval const TimeOutTimeProd = 30.0; + +NSTimeInterval const TimeOutTimeTest = 60.0; // diff timeout since lower env backend can be real bad ex: CallFwd + +NSString * const ParameterMDN = @"mdn"; + +#pragma mark - URL MFConstants + +// Server URLs +NSString * const URLProdPostpayBase = @"https://mobile.vzw.com"; + +NSString * const URLComponentPrepayNativeServer = @"mfPrepaySS"; +NSString * const URLComponentNativeServer = @"mobileFirstSS"; + +NSString * const URLComponentKeepAlive = @"isAlive.jsp"; + +#pragma mark - Notification Names + +NSString * const NotificationResponseLoaded = @"responseLoaded"; +NSString * const MVMCoreNotificationGoingToServer = @"MVMCoreGoServer"; diff --git a/MVMCore/MVMCore/Constants/MVMCoreErrorConstants.h b/MVMCore/MVMCore/Constants/MVMCoreErrorConstants.h new file mode 100644 index 0000000..014fa3b --- /dev/null +++ b/MVMCore/MVMCore/Constants/MVMCoreErrorConstants.h @@ -0,0 +1,41 @@ +// +// MVMCoreErrorConstants.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/13/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import + +// Error Domains +extern NSString * const ErrorDomainSystem; +extern NSString * const ErrorDomainNative; +extern NSString * const ErrorDomainServer; + +// Native Error Codes (Add new ones to bottom, don't change order!) +typedef NS_ENUM(NSInteger, ErrorCode) { + ErrorCodeDefault = 1, + ErrorCodeParsingJSON, + ErrorCodeNoPageType, + ErrorCodeInitViewController, + ErrorCodeViewControllerProcessingJSON, + ErrorCodeRequiredModuleNotPresent, + ErrorCodeNativeTimeout, + ErrorCodeLinkawayFailed, + ErrorCodePopupFailed, + ErrorCodeUnknownActionType, + ErrorCodeEmptyField, + ErrorCodeInputValidationFailure, + ErrorCodeEmptyResponse, + ErrorCodeStaticCacheFail, + ErrorCodeServerFailSendTouchIDHash, + ErrorCodeJSONNotDictionary, + ErrorCodeNoStoryboardName, + ErrorCodeNoStoryBoardIdentifier, + ErrorCodeNoNibName, + ErrorCodeNoModule, + ErrorCodeInvalidSettingType, + ErrorCodeServerFailSendFaceIDHash, + ErrorCodeSSL +}; diff --git a/MVMCore/MVMCore/Constants/MVMCoreErrorConstants.m b/MVMCore/MVMCore/Constants/MVMCoreErrorConstants.m new file mode 100644 index 0000000..d626352 --- /dev/null +++ b/MVMCore/MVMCore/Constants/MVMCoreErrorConstants.m @@ -0,0 +1,14 @@ +// +// MVMCoreErrorConstants.m +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/13/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import "MVMCoreErrorConstants.h" + +// Error Domains +NSString * const ErrorDomainSystem = @"ErrorDomainSystem"; +NSString * const ErrorDomainNative = @"ErrorDomainNative"; +NSString * const ErrorDomainServer = @"ErrorDomainServer"; diff --git a/MVMCore/MVMCore/Constants/MVMCoreHardcodedStringsConstants.h b/MVMCore/MVMCore/Constants/MVMCoreHardcodedStringsConstants.h new file mode 100644 index 0000000..eadad96 --- /dev/null +++ b/MVMCore/MVMCore/Constants/MVMCoreHardcodedStringsConstants.h @@ -0,0 +1,25 @@ +// +// MVMCoreHardcodedStringsConstants.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/13/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import + +extern NSString * const HardcodedErrorTitle; +extern NSString * const HardcodedRestart; +extern NSString * const HardcodedOK; + +extern NSString * const HardcodedErrorCritical; +extern NSString * const HardcodedErrorUnableToProcess; + +// SSL Pinning error strings +extern NSString * const ERROR_SSL_TITLE; +extern NSString * const ERROR_SSL_MESSAGE_PREFIX_KEY; +extern NSString * const ERROR_SSL_MESSAGE_TITLE_KEY; +extern NSString * const ERROR_SSL_MESSAGE_POSTFIX_KEY; +extern NSString * const ERROR_SSL_UPDATE_TITLE_KEY; +extern NSString * const ERROR_SSL_UPDATE_MESSAGE_KEY; + diff --git a/MVMCore/MVMCore/Constants/MVMCoreHardcodedStringsConstants.m b/MVMCore/MVMCore/Constants/MVMCoreHardcodedStringsConstants.m new file mode 100644 index 0000000..a1caf5d --- /dev/null +++ b/MVMCore/MVMCore/Constants/MVMCoreHardcodedStringsConstants.m @@ -0,0 +1,25 @@ +// +// MVMCoreHardcodedStringsConstants.m +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/13/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import "MVMCoreHardcodedStringsConstants.h" + +NSString * const HardcodedErrorTitle = @"error"; +NSString * const HardcodedRestart = @"Restart Key"; +NSString * const HardcodedOK = @"okCaps"; + +NSString * const HardcodedErrorCritical = @"Error Message Critical Key"; +NSString * const HardcodedErrorUnableToProcess = @"Error Message Unable To Process Request Key"; + +// SSL Pinning error strings +NSString * const ERROR_SSL_TITLE = @"SSL Error Title"; +NSString * const ERROR_SSL_MESSAGE_PREFIX_KEY = @"SSL Error Message Prefix"; +NSString * const ERROR_SSL_MESSAGE_TITLE_KEY = @"SSL Error Message Title"; +NSString * const ERROR_SSL_MESSAGE_POSTFIX_KEY = @"SSL Error Message Postfix"; +NSString * const ERROR_SSL_UPDATE_TITLE_KEY = @"SSL Update Title"; +NSString * const ERROR_SSL_UPDATE_MESSAGE_KEY = @"SSL Update Message"; + diff --git a/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.h b/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.h new file mode 100644 index 0000000..244cd37 --- /dev/null +++ b/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.h @@ -0,0 +1,95 @@ +// +// MVMCoreJSONConstants.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/13/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import + +extern NSString * const KeyResponseInfo; +extern NSString * const KeyCode; +extern NSString * const KeyUserMessage; +extern NSString * const KeyTopMessage; +extern NSString * const KeyCloseButton; +extern NSString * const KeyMessageStyle; +extern NSString * const KeyErrorHeading; + +extern NSString * const KeyAppState; + +extern NSString * const KeyPage; +extern NSString * const KeyPageType; +extern NSString * const KeyModuleMap; +extern NSString * const KeyModuleList; +extern NSString * const KeyPageMap; +extern NSString * const KeySystemParameters; +extern NSString * const KeyButtonMap; +extern NSString * const KeyActionMap; +extern NSString * const KeyDisableButton; +extern NSString * const KeyOpenSupport; + +extern NSString * const KeyLinks; +extern NSString * const KeyTitle; +extern NSString * const KeyMessage; +extern NSString * const KeyActionTypeRestart; +extern NSString * const KeyActionTypeBack; +extern NSString * const KeyActionTypeCall; +extern NSString * const KeyActionTypePreviousSubmit; +extern NSString * const KeyActionTypePopup; +extern NSString * const KeyActionTypeCancel; +extern NSString * const KeyActionTypeRedirect; +extern NSString * const KeyActionTypeTopAlert; +extern NSString * const KeyActionTypeSettings; +extern NSString * const KeyActionTypeCollapseNotification; +extern NSString * const KeyActionInformation; +extern NSString * const KeyLinkAwayAppURL; +extern NSString * const KeyLinkAwayURL; +extern NSString * const KeyCallNumber; +extern NSString * const KeyPresentationStyle; +extern NSString * const KeyExtraParameters; +extern NSString * const KeyContextRoot; + +extern NSString * const KeyType; +extern NSString * const KeyMVMRC; + +extern NSString * const KeyRememberMe; +extern NSString * const KeySSOSection; +extern NSString * const KeySSOHashSection; +extern NSString * const KeyEnableAnalytics; +extern NSString * const KeyUserAuthenticateTokenHash; + +#pragma mark- SSN Json Keys + +extern NSString * const ParameterOSVersion; +extern NSString * const CLIENT_SSL_STATUS_KEY; + +#pragma mark - JSON Values + +// Server driven response type +extern NSString * const ValueTypeSuccess; +extern NSString * const ValueTypeError; +extern NSString * const ValueTypeFieldErrors; +extern NSString * const ValueTypeErrorScreen; +extern NSString * const ValueTypeRedirect; +#warning Scott, Drive these from protocol +extern NSString * const ValueTypeSurvivalMode; +extern NSString * const ValueTypeClearSpotMain; +extern NSString * const ValueTypeClearSpotEnded; +extern NSString * const ValueTypeClearSpotProcessing; +extern NSString * const ValueTypeLiveChat; +extern NSString * const ValueTypeFamilyBaseChild; +extern NSString * const ValueTypeRetailShop; +extern NSString * const ValueTypeRemoteIn; + +// Server driven message style. +extern NSString * const ValueMessageStylePopup; +extern NSString * const ValueMessageStyleTop; +extern NSString * const ValueMessageStyleTopPersistent; + +// Server driven presentation style +extern NSString * const ValuePresentationStyleModal; +extern NSString * const ValuePresentationStyleRoot; +extern NSString * const ValuePresentationStyleReplace; +extern NSString * const ValuePresentationStylePush; +extern NSString * const ValuePresentationStylePopToPage; diff --git a/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.m b/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.m new file mode 100644 index 0000000..1479f3f --- /dev/null +++ b/MVMCore/MVMCore/Constants/MVMCoreJSONConstants.m @@ -0,0 +1,95 @@ +// +// MVMCoreJSONConstants.m +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/13/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import "MVMCoreJSONConstants.h" + +NSString * const KeyResponseInfo = @"ResponseInfo"; +NSString * const KeyCode = @"code"; +NSString * const KeyUserMessage = @"userMessage"; +NSString * const KeyTopMessage = @"topMessage"; +NSString * const KeyCloseButton = @"closeButton"; +NSString * const KeyMessageStyle = @"messageStyle"; +NSString * const KeyErrorHeading = @"errorHdg"; + +NSString * const KeyAppState = @"appState"; + +NSString * const KeyPage = @"Page"; +NSString * const KeyPageType = @"pageType"; +NSString * const KeyModuleMap = @"ModuleMap"; +NSString * const KeyModuleList = @"modules"; +NSString * const KeyPageMap = @"PageMap"; +NSString * const KeySystemParameters = @"SystemParams"; +NSString * const KeyButtonMap = @"ButtonMap"; +NSString * const KeyActionMap = @"ActionMap"; +NSString * const KeyDisableButton = @"disableAction"; +NSString * const KeyOpenSupport = @"openSupport"; + +NSString * const KeyLinks = @"Links"; +NSString * const KeyTitle = @"title"; +NSString * const KeyMessage = @"message"; +NSString * const KeyActionTypeRestart = @"restart"; +NSString * const KeyActionTypeBack = @"back"; +NSString * const KeyActionTypeCall = @"call"; +NSString * const KeyActionTypePreviousSubmit = @"previousSubmit"; +NSString * const KeyActionTypePopup = @"popup"; +NSString * const KeyActionTypeCancel = @"cancel"; +NSString * const KeyActionTypeRedirect = @"switchApp"; +NSString * const KeyActionTypeTopAlert = @"topAlert"; +NSString * const KeyActionTypeSettings = @"openSettings"; +NSString * const KeyActionTypeCollapseNotification = @"collapseNotification"; +NSString * const KeyActionInformation = @"actionInformation"; +NSString * const KeyLinkAwayAppURL = @"appURL"; +NSString * const KeyLinkAwayURL = @"browserUrl"; +NSString * const KeyCallNumber = @"callNumber"; +NSString * const KeyPresentationStyle = @"presentationStyle"; +NSString * const KeyExtraParameters = @"extraParameters"; +NSString * const KeyContextRoot = @"appContext"; + +NSString * const KeyType = @"type"; +NSString * const KeyMVMRC = @"LaunchMVMRC"; + +NSString * const KeyRememberMe = @"Store_Info"; +NSString * const KeySSOSection = @"SSO"; +NSString * const KeySSOHashSection = @"deviceMdnHashMap"; +NSString * const KeyEnableAnalytics = @"enableAnalytics"; +NSString * const KeyUserAuthenticateTokenHash = @"UserAuthenticateTokenHash"; + +NSString * const ParameterOSVersion = @"osVersion"; + +#pragma mark- SSL Json Keys +NSString * const CLIENT_SSL_STATUS_KEY = @"clientSSLStatus"; + +#pragma mark - JSON Values + +// Server driven response type +NSString * const ValueTypeSuccess = @"Success"; +NSString * const ValueTypeError = @"GlobalError"; +NSString * const ValueTypeFieldErrors = @"FieldErrors"; +NSString * const ValueTypeErrorScreen = @"ErrorScreen"; +NSString * const ValueTypeRedirect = @"Redirect"; +#warning Scott, drive these from protocol. +NSString * const ValueTypeSurvivalMode = @"SurvivalMode"; +NSString * const ValueTypeClearSpotMain = @"ClearSpotTopAlertMain"; +NSString * const ValueTypeClearSpotEnded = @"ClearSpotTopAlertEnded"; +NSString * const ValueTypeClearSpotProcessing = @"ClearSpotProcessing"; +NSString * const ValueTypeLiveChat = @"ChatTop"; +NSString * const ValueTypeFamilyBaseChild = @"FamilyBaseChild"; +NSString * const ValueTypeRetailShop = @"RetailShop"; +NSString * const ValueTypeRemoteIn = @"RemoteIn"; + +// Server driven message style. +NSString * const ValueMessageStylePopup = @"Popup"; +NSString * const ValueMessageStyleTop = @"Top"; +NSString * const ValueMessageStyleTopPersistent = @"TopPersistent"; + +// Server driven presentation style +NSString * const ValuePresentationStyleModal = @"modal"; +NSString * const ValuePresentationStyleRoot = @"root"; +NSString * const ValuePresentationStyleReplace = @"replace"; +NSString * const ValuePresentationStylePush = @"push"; +NSString * const ValuePresentationStylePopToPage = @"popBackToPage"; diff --git a/MVMCore/MVMCore/EmbeddedLibraries/Reachability/Reachability.h b/MVMCore/MVMCore/EmbeddedLibraries/Reachability/Reachability.h new file mode 100644 index 0000000..36cc034 --- /dev/null +++ b/MVMCore/MVMCore/EmbeddedLibraries/Reachability/Reachability.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2016 Apple Inc. All Rights Reserved. + See LICENSE.txt for this sample’s licensing information + + Abstract: + Basic demonstration of how to use the SystemConfiguration Reachablity APIs. + */ + +#import +#import +#import + + +typedef enum : NSInteger { + NotReachable = 0, + ReachableViaWiFi, + ReachableViaWWAN +} NetworkStatus; + +#pragma mark IPv6 Support +//Reachability fully support IPv6. For full details, see ReadMe.md. + + +extern NSString *kReachabilityChangedNotification; + + +@interface Reachability : NSObject + +/*! + * Use to check the reachability of a given host name. + */ ++ (instancetype)reachabilityWithHostName:(NSString *)hostName; + +/*! + * Use to check the reachability of a given IP address. + */ ++ (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress; + +/*! + * Checks whether the default route is available. Should be used by applications that do not connect to a particular host. + */ ++ (instancetype)reachabilityForInternetConnection; + + +#pragma mark reachabilityForLocalWiFi +//reachabilityForLocalWiFi has been removed from the sample. See ReadMe.md for more information. +//+ (instancetype)reachabilityForLocalWiFi; + +/*! + * Start listening for reachability notifications on the current run loop. + */ +- (BOOL)startNotifier; +- (void)stopNotifier; + +- (NetworkStatus)currentReachabilityStatus; + +/*! + * WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand. + */ +- (BOOL)connectionRequired; + +@end diff --git a/MVMCore/MVMCore/EmbeddedLibraries/Reachability/Reachability.m b/MVMCore/MVMCore/EmbeddedLibraries/Reachability/Reachability.m new file mode 100644 index 0000000..6892baa --- /dev/null +++ b/MVMCore/MVMCore/EmbeddedLibraries/Reachability/Reachability.m @@ -0,0 +1,242 @@ +/* + Copyright (C) 2016 Apple Inc. All Rights Reserved. + See LICENSE.txt for this sample’s licensing information + + Abstract: + Basic demonstration of how to use the SystemConfiguration Reachablity APIs. + */ + +#import +#import +#import +#import +#import + +#import + +#import "Reachability.h" + +#pragma mark IPv6 Support +//Reachability fully support IPv6. For full details, see ReadMe.md. + + +NSString *kReachabilityChangedNotification = @"kNetworkReachabilityChangedNotification"; + + +#pragma mark - Supporting functions + +#define kShouldPrintReachabilityFlags 1 + +static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment) +{ +#if kShouldPrintReachabilityFlags + + NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n", + (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', + (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', + + (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', + (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', + (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', + (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-', + comment + ); +#endif +} + + +static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) +{ +#pragma unused (target, flags) + NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); + NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback"); + + Reachability* noteObject = (__bridge Reachability *)info; + // Post a notification to notify the client that the network reachability changed. + [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject]; +} + + +#pragma mark - Reachability implementation + +@implementation Reachability +{ + SCNetworkReachabilityRef _reachabilityRef; +} + ++ (instancetype)reachabilityWithHostName:(NSString *)hostName +{ + Reachability* returnValue = NULL; + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); + if (reachability != NULL) + { + returnValue= [[self alloc] init]; + if (returnValue != NULL) + { + returnValue->_reachabilityRef = reachability; + } + else { + CFRelease(reachability); + } + } + return returnValue; +} + + ++ (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress +{ + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, hostAddress); + + Reachability* returnValue = NULL; + + if (reachability != NULL) + { + returnValue = [[self alloc] init]; + if (returnValue != NULL) + { + returnValue->_reachabilityRef = reachability; + } + else { + CFRelease(reachability); + } + } + return returnValue; +} + + ++ (instancetype)reachabilityForInternetConnection +{ + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + + return [self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress]; +} + +#pragma mark reachabilityForLocalWiFi +//reachabilityForLocalWiFi has been removed from the sample. See ReadMe.md for more information. +//+ (instancetype)reachabilityForLocalWiFi + + + +#pragma mark - Start and stop notifier + +- (BOOL)startNotifier +{ + BOOL returnValue = NO; + SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; + + if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context)) + { + if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) + { + returnValue = YES; + } + } + + return returnValue; +} + + +- (void)stopNotifier +{ + if (_reachabilityRef != NULL) + { + SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + } +} + + +- (void)dealloc +{ + [self stopNotifier]; + if (_reachabilityRef != NULL) + { + CFRelease(_reachabilityRef); + } +} + + +#pragma mark - Network Flag Handling + +- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags +{ + PrintReachabilityFlags(flags, "networkStatusForFlags"); + if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) + { + // The target host is not reachable. + return NotReachable; + } + + NetworkStatus returnValue = NotReachable; + + if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) + { + /* + If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi... + */ + returnValue = ReachableViaWiFi; + } + + if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) + { + /* + ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs... + */ + + if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) + { + /* + ... and no [user] intervention is needed... + */ + returnValue = ReachableViaWiFi; + } + } + + if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) + { + /* + ... but WWAN connections are OK if the calling application is using the CFNetwork APIs. + */ + returnValue = ReachableViaWWAN; + } + + return returnValue; +} + + +- (BOOL)connectionRequired +{ + NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef"); + SCNetworkReachabilityFlags flags; + + if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) + { + return (flags & kSCNetworkReachabilityFlagsConnectionRequired); + } + + return NO; +} + + +- (NetworkStatus)currentReachabilityStatus +{ + NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef"); + NetworkStatus returnValue = NotReachable; + SCNetworkReachabilityFlags flags; + + if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) + { + returnValue = [self networkStatusForFlags:flags]; + } + + return returnValue; +} + + +@end diff --git a/MVMCore/MVMCore/Info.plist b/MVMCore/MVMCore/Info.plist new file mode 100644 index 0000000..1007fd9 --- /dev/null +++ b/MVMCore/MVMCore/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeAuthObject.h b/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeAuthObject.h new file mode 100644 index 0000000..1b5d757 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeAuthObject.h @@ -0,0 +1,23 @@ +// +// FreeBeeAuthObject.h +// mobilefirst +// +// Created by Bansal, Tapan on 20/01/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import + +@class FreeBeeUrlObject; + +@interface FreeBeeAuthObject : NSObject + +- (BOOL)isValidFreeBeeSessionAuthObject; + +- (void)updateWithDictionary:(nullable NSDictionary*)inDict; +- (nullable NSDictionary*)proxyDictionaryForUrl:(nullable NSURL*)url; +- (nullable NSDictionary*)campaignHeaderForUrl:(nullable NSURL*)url; + +- (nullable FreeBeeUrlObject*)freebeeUrlObjectForUrl:(nullable NSURL *)url; +- (BOOL)isExpired ; +@end diff --git a/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeAuthObject.m b/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeAuthObject.m new file mode 100644 index 0000000..248de88 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeAuthObject.m @@ -0,0 +1,117 @@ +// +// FreeBeeAuthObject.m +// mobilefirst +// +// Created by Bansal, Tapan on 20/01/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import "FreeBeeAuthObject.h" +#import +#import "MFFreebeeHandler.h" +#import "FreeBeeUrlObject.h" + +@interface FreeBeeAuthObject () + +@property(nullable,strong,nonatomic) NSString *vidToken; +@property(nullable,strong,nonatomic) NSMutableArray *freeBeeUrlObjects; + +- (FreeBeeUrlObject*)freebeeUrlObjectForUrl:(NSURL*)url; + +@end + +@implementation FreeBeeAuthObject + +#pragma mark - +#pragma mark Public Methods +- (BOOL)isValidFreeBeeSessionAuthObject { + + if (self.vidToken.length && self.freeBeeUrlObjects.count) + return YES; + return NO; + +} +- (void)updateWithDictionary:(nullable NSDictionary*)inDict { + + //Remove Previously Existing Expired Ones + [self.freeBeeUrlObjects removeAllObjects]; + + if (inDict && [inDict isKindOfClass:[NSDictionary class]]) { + + self.vidToken = [inDict objectForKey:@"vidToken"]; + if (self.vidToken && self.vidToken.length) { + + NSArray* sdAuthResp = [inDict objectChainOfKeysOrIndexes:@[@"sdAuthResp"] ofType:[NSArray class]]; + __block FreeBeeUrlObject* freeBeeUrlObject = nil; + for (NSDictionary* inDict in sdAuthResp) { + + freeBeeUrlObject = [[FreeBeeUrlObject alloc] init]; + [freeBeeUrlObject updateWithDictionary:inDict]; + + if (!self.freeBeeUrlObjects) + self.freeBeeUrlObjects = [NSMutableArray array]; + + //Assigning the requested url to have a mapping b/w sdurl and request url + freeBeeUrlObject.requestUrl = [[MFFreebeeHandler sharedHandler] urlForidFromConfigDict:freeBeeUrlObject.urlId]; + + //Adding Only Valid Objects to Array + if (freeBeeUrlObject.sdUrl.length && NO == [freeBeeUrlObject isExpired]) + [self.freeBeeUrlObjects addObject:freeBeeUrlObject]; + freeBeeUrlObject = nil; + } + } + } +} + +- (nullable NSDictionary*)proxyDictionaryForUrl:(nullable NSURL*)url; { + + FreeBeeUrlObject* urlObject = [self freebeeUrlObjectForUrl:url]; + if (urlObject) + + return [urlObject proxyDictionary]; + return nil; + +} + +- (NSDictionary*)campaignHeaderForUrl:(nullable NSURL*)url { + + if (self.vidToken.length) { + + FreeBeeUrlObject* urlObject = [self freebeeUrlObjectForUrl:url]; + if (urlObject) + return [urlObject campaignHeader]; + } + + return nil; +} + +//http://mobile.vzw.com/abc.png +- (nullable FreeBeeUrlObject*)freebeeUrlObjectForUrl:(NSURL*)url { + + if (url) { + + NSString *host = [url host]; + //if freebeeobjects has url as part of + + if (host) { + + NSPredicate* predicate = [NSPredicate predicateWithFormat:@"requestUrl contains %@", host]; + NSArray* freeBeeUrlObject = [self.freeBeeUrlObjects filteredArrayUsingPredicate:predicate]; + + if (freeBeeUrlObject.count) + return freeBeeUrlObject[0]; + } + } + + return nil; +} + +- (BOOL)isExpired { + + if (self.freeBeeUrlObjects.count) { + + return [(( FreeBeeUrlObject*)self.freeBeeUrlObjects[0]) isExpired]; + } + return YES; +} +@end diff --git a/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeUrlObject.h b/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeUrlObject.h new file mode 100644 index 0000000..b2eb2fa --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeUrlObject.h @@ -0,0 +1,28 @@ +// +// FreeBeeUrlObject.h +// mobilefirst +// +// Created by Bansal, Tapan on 08/02/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import + +@interface FreeBeeUrlObject : NSObject + + +@property(nullable,strong,nonatomic) NSString *urlId; +@property(nullable,strong,nonatomic) NSNumber *httpsProxyIP; +@property(nullable,strong,nonatomic) NSString *sdUrl; +@property(nullable,strong,nonatomic) NSNumber *sdConfExpiry; +@property(nullable,strong,nonatomic) NSNumber *sdPort; +@property(nullable,strong,nonatomic) NSString *requestUrl; +@property(assign,nonatomic) NSInteger authStatus; + +- (void)updateWithDictionary:(nullable NSDictionary*)inDict; + +- (nullable NSDictionary*)proxyDictionary; +- (nullable NSDictionary*)campaignHeader; +- (BOOL)isValidFreeBeeObj; +- (BOOL)isExpired; +@end diff --git a/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeUrlObject.m b/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeUrlObject.m new file mode 100644 index 0000000..6b800bb --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/FreeBee/FreeBeeUrlObject.m @@ -0,0 +1,100 @@ +// +// FreeBeeUrlObject.m +// mobilefirst +// +// Created by Bansal, Tapan on 08/02/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import "FreeBeeUrlObject.h" + +static NSUInteger const kHTTPSProxyAuthorized = 1200; + +@interface FreeBeeUrlObject() + +@property(nonatomic) NSDictionary* proxyDictionary; +@property(nonatomic) NSDictionary* campaignHeaderDictionary; + +// The time that we started the last session timer. +@property(nullable,strong,nonatomic) NSDate* expirationDate; +@end + +@implementation FreeBeeUrlObject + +- (void)updateWithDictionary:(nullable NSDictionary*)inDict { + + if (inDict && [inDict isKindOfClass:[NSDictionary class]]) { + + NSNumber* authCode = [inDict objectForKey:@"authRespCode"]; + if ([authCode intValue] == kHTTPSProxyAuthorized) { + + self.urlId = [inDict objectForKey:@"urlId"]; + self.httpsProxyIP = [inDict objectForKey:@"httpsProxyIP"]; + self.sdUrl = [inDict objectForKey:@"sdUrl"]; + + self.sdPort = [inDict objectForKey:@"sdPort"]; + self.authStatus = kHTTPSProxyAuthorized; + + self.sdConfExpiry = [inDict objectForKey:@"sdConfExpiry"]; + self.expirationDate = [NSDate dateWithTimeIntervalSince1970:[self.sdConfExpiry doubleValue]]; + } + else { + + self.authStatus = [authCode intValue]; + self.expirationDate = [NSDate distantPast]; + } + } +} + +- (nullable NSDictionary*)proxyDictionary { + + if (nil != _proxyDictionary) + return _proxyDictionary; + + NSMutableDictionary *proxyDict = [[NSMutableDictionary alloc]init]; + + // depricated constants with HTTPS + [proxyDict setObject:@1 forKey:@"HTTPSEnable"]; + [proxyDict setObject:self.httpsProxyIP forKey:(NSString *)kCFStreamPropertyHTTPSProxyHost]; + [proxyDict setObject:self.sdPort forKey:(NSString *)kCFStreamPropertyHTTPSProxyPort]; + + self.proxyDictionary = [proxyDict copy]; + return self.proxyDictionary; +} + +- (nullable NSDictionary*)campaignHeader { + + if (YES == [self isValidFreeBeeObj]) { + + if (self.campaignHeaderDictionary) + return self.campaignHeaderDictionary; + + NSArray *urlPieces = [self.sdUrl componentsSeparatedByString:@"?"]; + NSMutableString *pass = [[NSMutableString alloc]init]; + pass = urlPieces[1]; + + NSData *plainData = [[NSString stringWithFormat:@"vzServices:%@", pass] dataUsingEncoding:NSUTF8StringEncoding]; + NSString *base64string = [@"Basic " stringByAppendingString:[plainData base64EncodedStringWithOptions:0]]; + + if (base64string.length) { + + self.campaignHeaderDictionary = @{@"Proxy-Authorization":base64string}; + return self.campaignHeaderDictionary; + } + } + return nil; +} + +- (BOOL)isValidFreeBeeObj { + + if (self.sdUrl) + return YES; + + return NO; +} + +- (BOOL)isExpired { + + return [[NSDate date] compare:self.expirationDate] == NSOrderedDescending ? YES : NO; +} +@end diff --git a/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeHandler.h b/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeHandler.h new file mode 100644 index 0000000..c2245f4 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeHandler.h @@ -0,0 +1,49 @@ +// +// MFFreebeeHandler.h +// mobilefirst +// +// Created by Patrick Sommer on 1/3/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import + +@class FreeBeeAuthObject; +@class MVMCoreOperation; +@class MFFreebeeOperation; + +typedef void(^FreebeeLoadFinishedHandler)(MVMCoreOperation* _Nullable freebeeOperation, BOOL isValid); + +@interface MFFreebeeHandler : NSObject + +@property(nullable, nonatomic, strong, readonly) FreeBeeAuthObject* freeBeeAuthObj; + +@property(nonatomic, readonly) BOOL isValidCampaign; +@property(nonatomic, readonly) MFFreebeeOperation* _Nullable freebeeOperation; + ++ (nullable instancetype)sharedHandler; + +- (void)activateFreeBeeIfServerEnabled; +- (void)invalidateFreeBeeState; + +- (void)processFreeBeeAuthRequestWithCompletionHandler:(nullable FreebeeLoadFinishedHandler)completionHandler; +- (BOOL)canProceedWithFreebeeAuthRequest; +- (BOOL)isFreeBeeEnabled; +- (BOOL)isExpired; + +- (void)enableFreeBeeForCurrentModule:(BOOL)enable; +- (BOOL)isFreeBeeEnabledForCurrentModule; +- (BOOL)isExcludedModule:(nullable NSString*)parentPageType; + +- (void)printStatusDescription; + +- (nullable NSDictionary*)proxyDictionaryforUrl:(nullable NSURL*)url; +- (nullable NSDictionary*)campaignHeaderforUrl:(nullable NSURL*)url; + +- (BOOL)isFreeBeeAuthorizedValidUrl:(nullable NSURL*)url; +- (nullable NSString*)urlForidFromConfigDict:(nonnull NSString*)urlId; + +- (nullable NSData*)freebee_dataWithContentsOfURL:(NSURL *_Nullable)url; + +- (void)configureFreeBeeWithDict:(nullable NSDictionary*)configDict withSessionReset:(BOOL)isReset; +@end diff --git a/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeHandler.m b/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeHandler.m new file mode 100644 index 0000000..bace2f7 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeHandler.m @@ -0,0 +1,671 @@ +// +// MFFreebeeHandler.m +// mobilefirst +// +// Created by Patrick Sommer on 1/3/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import "MFFreebeeHandler.h" +#import +#import "FreeBeeAuthObject.h" +#import "FreeBeeUrlObject.h" +#import "Reachability.h" +#import "MFFreebeeOperation.h" +#import "MVMCoreConstants.h" +#import "MVMCoreSessionObject.h" +#import "MVMCoreJSONConstants.h" +#import "MVMCoreLoadHandler.h" +#import "MVMCoreLoggingHandler.h" + +typedef NS_ENUM(NSUInteger, FreeBeeCampaignState) { + + FreeBeeCampaignStateNotDetermined, + FreeBeeCampaignStateBusy, + FreeBeeCampaignStateInvalid, + FreeBeeCampaignStateValid, + FreeBeeCampaignStateExpired, +}; + +@interface MFFreebeeHandler() + +@property(nonatomic, strong) NSString* authString; + +@property(nonatomic, strong) FreeBeeAuthObject* freeBeeAuthObj; +@property(nonatomic, assign) NSTimeInterval requestTimeoutInterval; +@property(nonatomic, assign) FreeBeeCampaignState currentFreeBeeCampaignState; +@property(nonatomic, strong) NSMutableArray* freeBeeUrls; +@property(nonatomic, strong) NSArray* excludedModulesForFreeBee; + +@property(nonatomic, strong) NSMutableDictionary* configDictionary; +@property(nonatomic) BOOL freeBeeEnabled; +@property(nonatomic) BOOL freeBeeEnabledForCurrentModule; +@property(nonatomic) BOOL isFreeBeeServerEnabled; + +@property(nonatomic) Reachability* reachability; + +@property(nonatomic) MFFreebeeOperation* freebeeOperation; +@property(nonatomic) FreebeeLoadFinishedHandler completionHandler; + +// An operation queue for top alerts +@property (nonnull, strong, nonatomic) NSOperationQueue *freebeeOperationQueue; + +- (void)processFreeBeeState:(nullable FreebeeLoadFinishedHandler)completionHandler; +- (void)processAuthRequest:(nullable FreebeeLoadFinishedHandler)completionHandler; + +- (void)updateWithConfigDictionary:(nullable NSDictionary*)configDict; +- (NSURLRequest*)authRequestWithConfigDict:(NSDictionary*)configDict; + +- (BOOL)isCampaignStateNotDetermined; + +- (FreeBeeCampaignState)processResponse:(NSData*)data withExtraParams:(NSDictionary*)extraParams; +- (void)configureMFFreeBeeSession; ++ (NSData *)sendSynchronousRequest:(NSURL *)url + returningResponse:(__autoreleasing NSURLResponse **)response + error:(__autoreleasing NSError **)error; +@end + +@implementation MFFreebeeHandler + ++ (nullable instancetype)sharedHandler { + static dispatch_once_t once; + static MFFreebeeHandler* sharedHandler; + + dispatch_once(&once, ^ { + + sharedHandler = [[self alloc] init]; + sharedHandler.authString = nil; + sharedHandler.freeBeeAuthObj = nil; + sharedHandler.currentFreeBeeCampaignState = FreeBeeCampaignStateNotDetermined; + sharedHandler.excludedModulesForFreeBee = nil; + sharedHandler.freeBeeEnabledForCurrentModule = YES; + sharedHandler.requestTimeoutInterval = 1; + sharedHandler.isFreeBeeServerEnabled = NO; + + [[NSNotificationCenter defaultCenter] addObserver:sharedHandler selector:@selector(responseJSONUpdated:) name:NotificationResponseLoaded object:nil]; + + sharedHandler.freebeeOperationQueue = [[NSOperationQueue alloc] init]; + sharedHandler.freebeeOperationQueue.maxConcurrentOperationCount = 1; + }); + + return sharedHandler; +} + +#pragma mark - +#pragma mark Public Methods + +//Activate FreeBee Feature at certain point +- (void)activateFreeBeeIfServerEnabled { + + if (self.isFreeBeeServerEnabled) + self.freeBeeEnabled = YES; + else + self.freeBeeEnabled = NO; + + MVMCoreLog(@"FreeBee status is %d", [[MFFreebeeHandler sharedHandler] isFreeBeeEnabled]); +} + +- (void)invalidateFreeBeeState { + + MVMCoreLog(@"FreeBee State Invalidated"); + if (self.currentFreeBeeCampaignState != FreeBeeCampaignStateBusy) { + + self.currentFreeBeeCampaignState = FreeBeeCampaignStateNotDetermined; + [[MVMCoreSessionObject sharedGlobal].freeBeeSession invalidateAndCancel]; + [MVMCoreSessionObject sharedGlobal].freeBeeSession = nil; + } +} + +- (void)processFreeBeeAuthRequestWithCompletionHandler:(nullable FreebeeLoadFinishedHandler)completionHandler { + + if ([self canProceedWithFreebeeAuthRequest]) { + + // check if self timer based mechanism can be used + if([self isExpired]) { + + [self invalidateFreeBeeState]; + } + } + + [self processFreeBeeState:completionHandler]; +} + +- (BOOL)canProceedWithFreebeeAuthRequest { + + if ([self isFreeBeeEnabled] && ([self isCampaignStateNotDetermined] || [self isExpired])) + return YES; + + return NO; +} + +- (BOOL)isFreeBeeEnabled { + + return self.freeBeeEnabled; +} + +- (BOOL)isValidCampaign { + + if (self.currentFreeBeeCampaignState == FreeBeeCampaignStateValid ) { + + return YES; + } + + return NO; +} + +- (BOOL)isExpired { + + return [self.freeBeeAuthObj isExpired]; +} + +- (void)enableFreeBeeForCurrentModule:(BOOL)enable { + + if (self.isFreeBeeEnabled) + self.freeBeeEnabledForCurrentModule = enable; +} + +- (BOOL)isFreeBeeEnabledForCurrentModule { + + return self.freeBeeEnabledForCurrentModule; +} + +- (BOOL)isExcludedModule:(nullable NSString*)parentPageType { + + if (self.excludedModulesForFreeBee.count == 0) + return NO; + + if (parentPageType.length) { + + NSPredicate* predicate = [NSPredicate predicateWithFormat:@"%@ =[cd] SELF", parentPageType]; + NSArray* subArray = [self.excludedModulesForFreeBee filteredArrayUsingPredicate:predicate]; + if (subArray.count) + return YES; + else + return NO; + } + return YES; +} + +- (void)printStatusDescription { + + if ([[MFFreebeeHandler sharedHandler] isValidCampaign]) { + + MVMCoreLog(@"FreeBee Valid Campaign"); + } + else { + + MVMCoreLog(@"FreeBee Invalid Campaign"); + } +} + +- (nullable NSDictionary*)proxyDictionaryforUrl:(nullable NSURL*)url { + + return [self.freeBeeAuthObj proxyDictionaryForUrl:url]; +} + +- (nullable NSDictionary*)campaignHeaderforUrl:(nullable NSURL*)url { + + return [self.freeBeeAuthObj campaignHeaderForUrl:url]; +} + +//make it protocol dependent +- (BOOL)isFreeBeeAuthorizedValidUrl:(nullable NSURL*)url { + + //if validated url has prefix, then return YES + FreeBeeUrlObject* urlObject = [self.freeBeeAuthObj freebeeUrlObjectForUrl:url]; + + if (urlObject) + return YES; + + return NO; +} + +- (nullable NSString*)urlForidFromConfigDict:(nonnull NSString*)urlId { + + NSArray* authUrls = [self.configDictionary arrayForKey:@"authUrls"]; + NSPredicate* predicate = [NSPredicate predicateWithFormat:@"urlId = %@",urlId]; + + NSArray* matchedUrl = [authUrls filteredArrayUsingPredicate:predicate]; + if (matchedUrl.count) { + + NSDictionary* authUrl = matchedUrl[0]; + return authUrl[@"authUrl"]; + } + return nil; +} + +#pragma mark FreeBee Helper for NSData +- (nullable NSData*)freebee_dataWithContentsOfURL:(NSURL *)url { + + NSData* data = nil; + NSDictionary* proxyDict = [self proxyDictionaryforUrl:url]; + + if (YES == [self isFreeBeeEnabled] && + YES == [self isValidCampaign] && proxyDict && + YES == [self isFreeBeeEnabledForCurrentModule]) { + + MVMCoreLog(@"Free Data Url, %@", url); + + NSURLResponse* response = nil; + NSError* error = nil; + data = [self sendSynchronousRequest:url returningResponse:&response error:&error]; + + if(error) + data = nil; + + MVMCoreLog(@"freebee_dataWithContentsOfURL:Free Data, %lu", (unsigned long)data.length); + } + else { + + data = [NSData dataWithContentsOfURL:url]; + } + return data; + +} + +#pragma mark - FreeBee Registration +- (void)responseJSONUpdated:(nonnull NSNotification *)notification { + +#if !ENABLE_FREEBEE_LOCAL_TEST + NSDictionary *module = [notification.userInfo dictWithChainOfKeysOrIndexes:@[KeyModuleMap, @"FreeBeeRollout"]]; +#else + NSDictionary *module = [notification.userInfo dictWithChainOfKeysOrIndexes:@[KeyModuleMap, @"DeepLinkMapping"]]; +#endif + + if (module) { + + // Enable for testing hardcoded response +#if ENABLE_FREEBEE_LOCAL_TEST + NSString *plistPath = [[MVMCoreUtility bundleForMVMCore] pathForResource:@"freebeelaunchApp" ofType:@"json"]; + NSData* data = [NSData dataWithContentsOfFile:plistPath]; + NSDictionary* freeBeeConfigDictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; + module = [freeBeeConfigDictionary dictionaryForKey:@"FreeBeeRollout"]; +#endif + + MVMCoreLog(@"*********************************FreeBee Logs Start**********************************"); + MVMCoreLog(@"FreeBee launch App Json is %@", module); + MVMCoreLog(@"*********************************FreeBee Logs End**********************************"); + NSString* freeBeeEnabledFlag = [module stringForKey:@"freeBeeEnabled"]; + [MVMCoreLoggingHandler logWithDelegateWithObject:nil withName:@"freeBeeData" withExtraInfo:module]; + + if ([freeBeeEnabledFlag isEqualToString:@"Y"]) { + + self.isFreeBeeServerEnabled = YES; + self.authString = [module stringForKey:@"proxyServerUrl"]; + if (self.authString) { + + NSDictionary* configDict = [module dictionaryForKey:@"freeBeeData"]; + [self configureFreeBeeWithDict:configDict withSessionReset:NO]; + self.excludedModulesForFreeBee = [module arrayForKey:@"disabledModules"]; + + NSString* timeOut = [module stringForKey:@"authRequestTimeOut"]; + NSTimeInterval time = [timeOut doubleValue]; + if (time > 0.f) + self.requestTimeoutInterval = time; + } + else { + + [self configureFreeBeeWithDict:nil withSessionReset:NO]; + } + } + else { + + [self configureFreeBeeWithDict:nil withSessionReset:NO]; + } + } +} + +- (void)configureFreeBeeWithDict:(nullable NSDictionary*)configDict withSessionReset:(BOOL)isReset { + + if (configDict) { + + [self updateWithConfigDictionary:configDict]; + [self enableReachability:YES]; + } + else { + + [self invalidateFreeBeeState]; + [self updateWithConfigDictionary:nil]; + if (isReset) { + + [self enableReachability:NO]; + [[MVMCoreSessionObject sharedGlobal].freeBeeSession invalidateAndCancel]; + [MVMCoreSessionObject sharedGlobal].freeBeeSession = nil; + } + } +} + +#pragma mark - FreeBee State Machine SetUp +- (void)processFreeBeeState:(nullable FreebeeLoadFinishedHandler)completionHandler { + + switch (self.currentFreeBeeCampaignState) { + + case FreeBeeCampaignStateBusy: + [self processBusyState]; + break; + + case FreeBeeCampaignStateNotDetermined: + [self processAuthRequest:completionHandler]; + break; + + case FreeBeeCampaignStateValid: + self.completionHandler(nil, YES); + self.completionHandler = nil; + //start a timer request + break; + + case FreeBeeCampaignStateInvalid: + [self enableReachability:YES]; + self.completionHandler(nil, YES); + self.completionHandler = nil; + break; + + default: + break; + } +} + +- (void)processBusyState { + + if (nil == self.configDictionary) { + + MVMCoreLog(@"FreeBee Response failed with error configDictionary:Empty"); + self.freeBeeAuthObj = nil; + self.currentFreeBeeCampaignState = FreeBeeCampaignStateInvalid; + } + self.completionHandler(self.freebeeOperation, YES); +} + +#pragma mark - Auth Request Handler +- (void)processAuthRequest:(nullable FreebeeLoadFinishedHandler)completionHandler { + + if (self.freebeeOperation) { + + self.currentFreeBeeCampaignState = FreeBeeCampaignStateBusy; + [self processBusyState]; + return; + } + + [self enableReachability:NO]; + + [[MVMCoreLoadHandler sharedGlobal] registerLoadQueue:self.freebeeOperationQueue]; + + self.completionHandler = completionHandler; + + MVMCoreLog(@"Initializing Freebee Auth Session Call"); + + __weak MFFreebeeHandler* wSelf = self; + self.currentFreeBeeCampaignState = FreeBeeCampaignStateBusy; + + NSURLRequest* request = [self authRequestWithConfigDict:self.configDictionary]; + + if (request == nil) { + + [MVMCoreLoggingHandler logWithDelegateWithObject:nil withName:@"freeBeeError" withExtraInfo:nil]; + wSelf.currentFreeBeeCampaignState = FreeBeeCampaignStateInvalid; + [self processFreeBeeState:nil]; + return; + } + + //add operation to queue + self.freebeeOperation = [[MFFreebeeOperation alloc] initWithConfigDict:request Delegate:wSelf]; + [self.freebeeOperationQueue addOperation:self.freebeeOperation]; +} + +- (void)updateWithConfigDictionary:(nullable NSDictionary*)configDict { + + if (configDict && configDict.count) + self.configDictionary = [NSMutableDictionary dictionaryWithDictionary:configDict]; + else { + + [self.configDictionary removeAllObjects]; + self.isFreeBeeServerEnabled = NO; + self.freeBeeEnabled = NO; + self.freeBeeEnabledForCurrentModule = NO; + } +} + +//warning:this needs to be modified for actual dictionary +- (NSURLRequest*)authRequestWithConfigDict:(NSDictionary*)configDict { + + //Need to modify this code with config dictionary + if (self.authString.length == 0) + return nil; + + NSURL *url = [NSURL URLWithString:self.authString]; + + //warning: For Initial testing we are putting + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:self.requestTimeoutInterval]; + + //Update headers + [request setHTTPMethod:@"POST"]; + [request setValue:@"no-cache, no-store" forHTTPHeaderField:@"Cache-Control"]; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; + + NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; + [parameters setObject:@[configDict] forKey:@"sdAuthReq"]; + + MVMCoreLog(@"Freebee AuthRequest Params = %@",parameters); + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:&error]; + [request setHTTPBody:data]; + return [request copy]; +} + +#pragma mark - Helper Functions +- (BOOL)isCampaignStateNotDetermined { + + if (self.currentFreeBeeCampaignState == FreeBeeCampaignStateNotDetermined) + return YES; + + return NO; +} + +#pragma mark - FreeBeeTaskFinished Protocol +- (void)updateWithResponse:(NSData*)data Error:(NSError*)error ExtraParams:(NSDictionary*) extraParams { + + if (data.length > 0) { + + self.currentFreeBeeCampaignState = [self processResponse:data withExtraParams:extraParams]; + [self processFreeBeeState:nil]; + + } else { + + // Empty response. + MVMCoreLog(@"FreeBee Auth Request failed with empty response"); + self.currentFreeBeeCampaignState = FreeBeeCampaignStateInvalid; + [MVMCoreLoggingHandler logWithDelegateWithObject:nil withName:@"freeBeeError" withExtraInfo:extraParams]; + [self processFreeBeeState:nil]; + } + self.freebeeOperation = nil; +} + +- (FreeBeeCampaignState)processResponse:(NSData*)data withExtraParams:(NSDictionary*)extraParams { + + __weak MFFreebeeHandler* wSelf = self; + + NSDictionary *jsonDictionary = nil; + NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + MVMCoreLog(@"FreeBee Response is %@",responseString); + // Serialize the downloaded data to json object. + NSError *error = nil; + jsonDictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; + + MVMCoreLog(@"FreeBee init success with response %@", jsonDictionary); + if ([jsonDictionary isKindOfClass:[NSDictionary class]]) { + + if (!self.freeBeeAuthObj) { + + //warning:this needs to be modified for multiple urls + wSelf.freeBeeAuthObj = [[FreeBeeAuthObject alloc] init]; + [wSelf.freeBeeAuthObj updateWithDictionary:jsonDictionary]; + } + else { + + [wSelf.freeBeeAuthObj updateWithDictionary:jsonDictionary]; + } + + NSMutableDictionary* logDict = [NSMutableDictionary dictionary]; + + if (jsonDictionary) + [logDict addEntriesFromDictionary:jsonDictionary]; + + if (extraParams) + [logDict addEntriesFromDictionary:extraParams]; + + if ([self.freeBeeAuthObj isValidFreeBeeSessionAuthObject]) { + + [MVMCoreLoggingHandler logWithDelegateWithObject:nil withName:@"freeBeeSuccess" withExtraInfo:logDict]; + + MVMCoreLog(@"FreeBee Response Validated"); + [self configureMFFreeBeeSession]; + return FreeBeeCampaignStateValid; + } + + [MVMCoreLoggingHandler logWithDelegateWithObject:nil withName:@"freeBeeError" withExtraInfo:logDict]; + + MVMCoreLog(@"FreeBee Response failed with error"); + wSelf.freeBeeAuthObj = nil; + return FreeBeeCampaignStateInvalid; + } + + if (error.description) { + + [MVMCoreLoggingHandler logWithDelegateWithObject:nil withName:@"freeBeeError" withExtraInfo:@{@"description":error.description}]; + } + else { + + [MVMCoreLoggingHandler logWithDelegateWithObject:nil withName:@"freeBeeError" withExtraInfo:nil]; + } + + return FreeBeeCampaignStateInvalid; +} + +- (void)configureMFFreeBeeSession { + + MVMCoreLog(@"FreeBee Configure Session"); + + //Assign The new Session Configuration sice its not possible to modify the existing session, we need to + MVMCoreSessionObject *sessionSingleton = [MVMCoreSessionObject sharedGlobal]; + NSURLSession* session = sessionSingleton.session; + NSURLSessionConfiguration *configuration = session.configuration; + + NSURL* baseUrl = sessionSingleton.baseURL; + configuration.connectionProxyDictionary = [[MFFreebeeHandler sharedHandler] proxyDictionaryforUrl:baseUrl]; + + if (configuration.connectionProxyDictionary) { + + MVMCoreLog("Creating New MFSSeeion object with configuration dict %@",configuration.connectionProxyDictionary); + MVMCoreLog("Creating New MFSSeeion object with Delegate %@, Op Queue %@",session.delegate, session.delegateQueue); + sessionSingleton.freeBeeSession = [NSURLSession sessionWithConfiguration:configuration delegate:session.delegate delegateQueue:session.delegateQueue]; + } +} + +// A Syncchronous request to fetch NSData +- (NSData *)sendSynchronousRequest:(NSURL *)url + returningResponse:(__autoreleasing NSURLResponse **)response + error:(__autoreleasing NSError **)error { + + dispatch_group_t group = dispatch_group_create(); + dispatch_group_enter(group); + + NSError __block *err = NULL; + NSData __block *data; + NSURLResponse __block *resp; + + NSURLSession *session = nil; + NSOperationQueue* queue = [NSOperationQueue currentQueue]; + + NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; + + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + configuration.connectionProxyDictionary = [[MFFreebeeHandler sharedHandler] proxyDictionaryforUrl:url]; + + session = [NSURLSession sessionWithConfiguration:configuration delegate:session.delegate delegateQueue:queue]; + + NSDictionary* campaignDictionary = [[MFFreebeeHandler sharedHandler] campaignHeaderforUrl:url]; + if (campaignDictionary) { + + [request addValue:campaignDictionary[@"Proxy-Authorization"] forHTTPHeaderField:@"Proxy-Authorization"]; + } + + + [[session dataTaskWithRequest:request + completionHandler:^(NSData* _data, NSURLResponse* _response, NSError* _error) { + + resp = _response; + err = _error; + data = _data; + dispatch_group_leave(group); + + }] resume]; + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + + if (response) { + + *response = resp; + } + + if (error) { + + *error = err; + } + + return data; +} +#pragma mark - Reachibility Integration +- (void)enableReachability:(BOOL)enable { + + dispatch_async(dispatch_get_main_queue(), ^{ + if (enable) { + + _reachability = [Reachability reachabilityForInternetConnection]; + + // Observe the kNetworkReachabilityChangedNotification. When that notification is posted, the method reachabilityChanged will be called. + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityDidChange:) name:kReachabilityChangedNotification object:nil]; + [_reachability startNotifier]; + } + else { + + if (_reachability) { + + [[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil]; + [_reachability stopNotifier]; + _reachability = nil; + } + } + }); +} + +- (void)reachabilityDidChange:(NSNotification *)notification { + + MVMCoreLog(@"Device Reachability Changed"); + Reachability *reachability = (Reachability *)[notification object]; + + switch (reachability.currentReachabilityStatus) { + case NotReachable: { + MVMCoreLog(@"Unreachable"); + [self invalidateFreeBeeState]; + break; + } + case ReachableViaWWAN: { + MVMCoreLog(@"Reachable via wlan"); + [self invalidateFreeBeeState]; + break; + } + case ReachableViaWiFi: { + MVMCoreLog(@"Reachable via wifi"); + [self invalidateFreeBeeState]; + break; + } + } +} + +#pragma mark - NSObject Methods +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self name:NotificationResponseLoaded object:nil]; +} +@end diff --git a/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeOperation.h b/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeOperation.h new file mode 100644 index 0000000..8ff1613 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeOperation.h @@ -0,0 +1,22 @@ +// +// MFFreebeeOperation.h +// mobilefirst +// +// Created by Bansal, Tapan on 07/09/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import +#import "MVMCoreOperation.h" + +@protocol FreeBeeTaskFinished + +-(void)updateWithResponse:(NSData*)data Error:(NSError*)error ExtraParams:(NSDictionary*) extraParams; + +@end + +@interface MFFreebeeOperation : MVMCoreOperation + +-(instancetype)initWithConfigDict:(NSURLRequest*)freeBeeAuthRequest Delegate:(id)delegate; + +@end diff --git a/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeOperation.m b/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeOperation.m new file mode 100644 index 0000000..f84e23d --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/FreeBee/MFFreebeeOperation.m @@ -0,0 +1,107 @@ +// +// MFFreebeeOperation.m +// mobilefirst +// +// Created by Bansal, Tapan on 07/09/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import "MFFreebeeOperation.h" +#import "MVMCoreLoggingHandler.h" + +@interface MFFreebeeOperation() + +@property(nonatomic, strong) NSURLRequest* freeBeeAuthRequest; +@property(weak) id delegate; +@property(nonatomic) dispatch_semaphore_t semaphore; + +@end + +@implementation MFFreebeeOperation + +-(instancetype)initWithConfigDict:(NSURLRequest*)freeBeeAuthRequest Delegate:(id)delegate { + + if(self = [super init]) { + + self.freeBeeAuthRequest = freeBeeAuthRequest; + self.delegate = delegate; + _semaphore = dispatch_semaphore_create(0); + } + return self; +} + +-(void)main { + + [super main]; + + if ([self checkAndHandleForCancellation]){ + [self markAsFinished]; + return; + } + [self processAuthRequest]; + [self markAsFinished]; +} + +-(void)unlock { + + dispatch_semaphore_signal(_semaphore); +} + +-(void)processAuthRequest { + + NSURLRequest *request = self.freeBeeAuthRequest; + + if (request == nil) { + + [self.delegate updateWithResponse:nil Error:nil ExtraParams:nil]; + return; + } + + MVMCoreLog(@"*********************************FreeBee Auth Call Start**********************************"); + NSDate* date = [NSDate date]; + NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:date]; + + NSURLResponse *response = nil; + NSError *error = nil; + NSData *data = [self sendSynchronousRequest:request returningResponse:&response error:&error]; + MVMCoreLog(@"Freebee Auth Call Request Time response for freebee is %f", interval); + NSDictionary *timeForRequest = @{@"ResponseTime":@(interval)}; + [self.delegate updateWithResponse:[data copy] Error:error ExtraParams:timeForRequest]; +} + +- (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response + error:(NSError **)error { + + NSError __block *err = NULL; + NSData __block *data; + NSURLResponse __block *resp; + + [[[NSURLSession sharedSession] dataTaskWithRequest:request + completionHandler:^(NSData* _data, NSURLResponse* _response, NSError* _error) { + resp = _response; + err = _error; + data = _data; + // this is for local testing +#if 0 + NSString *plistPath = [[MVMCoreUtility bundleForMVMCore] pathForResource:@"freebee" ofType:@"json"]; + data = [NSData dataWithContentsOfFile:plistPath]; + err = nil; +#endif + [self unlock]; + }] resume]; + + dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); + + if (response) { + + *response = resp; + } + + if (error) { + + *error = err; + } + + return data; +} +@end diff --git a/MVMCore/MVMCore/LoadHandling/FreeBee/freebee.json b/MVMCore/MVMCore/LoadHandling/FreeBee/freebee.json new file mode 100644 index 0000000..4b1203e --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/FreeBee/freebee.json @@ -0,0 +1,14 @@ +{ + "statusCode": 200, + "vidToken": "xlO5qbqZLp68J7OBHqumyN693FfDAdY6rC3T51haBxM=", + "sdAuthResp": [ + { + "urlId": "1", + "authRespCode": 1200, + "httpsProxyIP": "68.128.201.128", + "sdUrl": "mobile.vzw.com:443?vzSvc=xlO5qbqZLp68J7OBHqumyN693FfDAdY6rC3T51haBxM=&vispVzKey=70421425&vispIconFg=1&vispUsr=70421425:A4WgVNAcwIdx8EVgt4QjyQ==&vispAuthSid=CIAAACAKJwE&vispExpTime=1492803669&vispAuthKey=46700746&vispAuthSign=0.19.sC5AhLOaDdDXZWz3gYx0MoxjNJLxb6IcV-e5ZBxNS8s", + "sdConfExpiry": 1697567906, + "sdPort": 22779 + } + ] +} diff --git a/MVMCore/MVMCore/LoadHandling/FreeBee/freebeelaunchApp.json b/MVMCore/MVMCore/LoadHandling/FreeBee/freebeelaunchApp.json new file mode 100644 index 0000000..0ac015a --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/FreeBee/freebeelaunchApp.json @@ -0,0 +1,25 @@ +{ + "FreeBeeRollout": { + "ResponseInfo": { + "locale": "EN", + "type": "Success", + "code": "00000", + "message": "0", + "userMessage": "0" + }, + "authRequestTimeOut": "1", + "freeBeeEnabled": "Y", + "freeBeeData": { + "campaignToken": "CIAAIYAKqAE", + "authUrls": [ + { + "urlId": "1", + "authUrl": "mobile.vzw.com:443" + } + ] + }, + "disabledModules": [ + ], + "proxyServerUrl": "https://auth.svcs.verizon.com:22790/sd/v2/sdAuthorization/httpsProxy" + } +} diff --git a/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayDelegateProtocol.h b/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayDelegateProtocol.h new file mode 100644 index 0000000..faf199d --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayDelegateProtocol.h @@ -0,0 +1,25 @@ +// +// MVMCoreLoadingOverlayDelegateProtocol.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/27/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import +#import + +@protocol MVMCoreLoadingOverlayDelegateProtocol + +@optional + +// Loading will be beginning +- (void)beginningLoading; + +// Loading is finished +- (void)finishedLoading; + +// Provides the screen to use for loading. +- (nonnull UIViewController *)getLoadingViewController; + +@end diff --git a/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayHandler.h b/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayHandler.h new file mode 100644 index 0000000..64e5388 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayHandler.h @@ -0,0 +1,29 @@ +// +// MVMCoreLoadingOverlayHandler.h +// myverizon +// +// Created by Scott Pfeil on 3/10/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// +// A singleton for handling a loading screen. Ensures there is only one on the screen at any given time. + +#import + +@interface MVMCoreLoadingOverlayHandler : NSObject + +// Returns the shared instance of this singleton ++ (nullable instancetype)sharedLoadingOverlay; + +// Starts Loading. Every start loading call must be terminated with an end loading call. +- (void)startLoading; + +// Returns if it is showing. +- (BOOL)isShowing; + +// One of the loads stopped loading. +- (void)stopLoading:(BOOL)animate; + +// Forces to stop loading even if other items are still loading. +- (void)forceStopLoading:(BOOL)animate; + +@end diff --git a/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayHandler.m b/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayHandler.m new file mode 100644 index 0000000..8c97181 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingOverlayHandler.m @@ -0,0 +1,172 @@ +// +// MVMCoreLoadingOverlayHandler.m +// myverizon +// +// Created by Scott Pfeil on 3/10/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreLoadingOverlayHandler.h" +#import "MVMCoreSessionObject.h" +#import +#import "MVMCoreLoadingViewControllerProtocol.h" +#import "MVMCoreObject.h" +@interface MVMCoreLoadingOverlayHandler () + +// The loading ui elements +@property (nullable, weak, nonatomic, readwrite) UIViewController *loadingViewController; + +// A reference to the animation timer. Used to delay animation. +@property (nullable, strong, nonatomic) NSTimer *animationTimer; + +// Animation Flags +@property (nonatomic) BOOL animatingIn; +@property (nonatomic) BOOL animatingOut; + +// The number of start loads called. +@property (nonatomic) NSInteger loadCount; + +// Animates in the overlay after the delay. +- (void)animateAfterDelay:(nonnull NSTimer *)timer; + +// Animates in the overlay. +- (void)animateLoadingViewController; + +@end + +@implementation MVMCoreLoadingOverlayHandler + ++ (instancetype)sharedLoadingOverlay { + static dispatch_once_t once; + static id sharedInstance; + + dispatch_once(&once, ^ + { + sharedInstance = [[self alloc] init]; + }); + + return sharedInstance; +} + +#pragma mark - Overlay Functions + +- (void)startLoading { + + [MVMCoreDispatchUtility performBlockOnMainThread:^{ + + // If loading view hasn't been made yet, create it. + if (!self.loadingViewController && [[MVMCoreObject sharedInstance].loadingProtocol respondsToSelector:@selector(getLoadingViewController)]) { + self.loadingViewController = [[MVMCoreObject sharedInstance].loadingProtocol getLoadingViewController]; + } + + // Does nothing if no loading view controller + if (self.loadingViewController) { + self.loadCount++; + + // Notify we are beginning the laod + if ([[MVMCoreObject sharedInstance].loadingProtocol respondsToSelector:@selector(beginningLoading)]) { + [[MVMCoreObject sharedInstance].loadingProtocol beginningLoading]; + } + + // If we are already waiting to animate or animating in, do nothing. + if (!self.animationTimer && !self.animatingIn) { + + // Restarts the loading animation. + [self.loadingViewController startLoading]; + + if (self.animatingOut) { + + // If we are animating out, just start animating back in. + [self animateLoadingViewController]; + } else if (self.loadingViewController.view.isHidden) { + + self.loadingViewController.view.hidden = NO; + self.loadingViewController.view.alpha = 0; + + // Animate in after a small delay in case the response comes back very quickly. + self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(animateAfterDelay:) userInfo:nil repeats:NO]; + } + } + } + }]; +} + +- (void)animateLoadingViewController { + + self.animatingIn = YES; + [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ + self.loadingViewController.view.alpha = 1.0; + } completion:^(BOOL finished) { + self.animatingIn = NO; + }]; +} + +- (void)animateAfterDelay:(nonnull NSTimer *)timer { + + // Only animates in if the timer hasn't been nilled out yet. + if (self.animationTimer == timer) { + [self animateLoadingViewController]; + } + self.animationTimer = nil; +} + +- (BOOL)isShowing { + return !self.loadingViewController.view.hidden; +} + +- (void)stopLoading:(BOOL)animate { + + [MVMCoreDispatchUtility performBlockOnMainThread:^{ + if (--self.loadCount <= 0) { + [self forceStopLoading:animate]; + } + }]; +} + +- (void)forceStopLoading:(BOOL)animate { + + [MVMCoreDispatchUtility performBlockOnMainThread:^{ + + self.loadCount = 0; + + // Kills the timer if it is going. + if (self.animationTimer) { + [self.animationTimer invalidate]; + self.animationTimer = nil; + } + + if (self.loadingViewController.view && [self isShowing] && !self.animatingOut) { + + if (animate) { + self.animatingOut = YES; + + [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ + self.loadingViewController.view.alpha = 0; + } completion:^(BOOL finished) { + self.animatingOut = NO; + + // May have been cancelled to animate back in. + if (!self.animatingIn) { + self.loadingViewController.view.alpha = 0; + self.loadingViewController.view.hidden = YES; + [self.loadingViewController stopLoading]; + if ([[MVMCoreObject sharedInstance].loadingProtocol respondsToSelector:@selector(finishedLoading)]) { + [[MVMCoreObject sharedInstance].loadingProtocol finishedLoading]; + } + } + }]; + } else { + + // No animation, reset state. + self.loadingViewController.view.alpha = 0; + self.loadingViewController.view.hidden = YES; + [self.loadingViewController stopLoading]; + if ([[MVMCoreObject sharedInstance].loadingProtocol respondsToSelector:@selector(finishedLoading)]) { + [[MVMCoreObject sharedInstance].loadingProtocol finishedLoading]; + } + } + } + }]; +} + +@end diff --git a/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingViewControllerProtocol.h b/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingViewControllerProtocol.h new file mode 100644 index 0000000..9a880d7 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/LoadingOverlay/MVMCoreLoadingViewControllerProtocol.h @@ -0,0 +1,17 @@ +// +// MVMCoreLoadingViewControllerProtocol.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/21/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +@protocol MVMCoreLoadingViewControllerProtocol + +// Called when the view controller should animate loading +- (void)startLoading; + +// Called when the view controller should stop animating loading. +- (void)stopLoading; + +@end diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadDelegateProtocol.h b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadDelegateProtocol.h new file mode 100644 index 0000000..c62d0b2 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadDelegateProtocol.h @@ -0,0 +1,64 @@ +// +// MVMCoreLoadDelegateProtocol.h +// myverizon +// +// Created by Scott Pfeil on 11/26/13. +// Copyright (c) 2013 Verizon Wireless. All rights reserved. +// +// The standard post request protocol + +#import +#import +@class MVMCoreRequestParameters; +@class MVMCoreErrorObject; +@class MVMCoreAlertObject; +@class MVMCoreLoadObject; + +@protocol MVMCoreLoadDelegateProtocol + +@optional + +/** Use this function if you want to handle any specific errors in a special manner. You can perform any logic needed here. Default behavior is to present the error. + * @param errorObject The error object + * @param loadObject Contains load data. + * @return If we should continue the loading process or end it. + * Details: Should handle any view controller specific loading errors. Easy to subclass. Called in checkForErrors */ +- (BOOL)checkForDelegateSpecificErrors:(nullable MVMCoreErrorObject *)errorObject loadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(BOOL shouldContinueLoad, MVMCoreLoadObject * _Nullable loadObject, MVMCoreErrorObject * _Nullable error))completionHandler; + +/** Notifies the delegate that an error page will be loaded and asks if we should load it. + * @param loadObject Contains load data. + * @param error Contains any error. + * @return TRUE if the calling process should continue. */ +- (BOOL)shouldContinueToErrorPage:(nonnull MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error; + +/** Notifies the delegate that a module failed to load. + * @param module The name of the module. + * @param loadObject The load object containing all of the information. + * @param error The error object passed in will be set in the case of an error. + * @return True if the calling process should continue. */ +- (BOOL)handleModuleError:(nonnull NSString *)module loadObject:(nonnull MVMCoreLoadObject *)loadObject error:(nonnull MVMCoreErrorObject *)error; + +/** Get the alert object whose data will be presented. Overwrite this to alter how you want the alert to show. + * @param loadObject The load object. + * @param errorObject An error object if there was an error. + * @return Returns the alert object. + * Details: Gets the alert that will display to the screen. Easier to subclass here to avoid subclassing the displaying logic. */ +- (nullable MVMCoreAlertObject *)alertObjectToShow:(nonnull MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)errorObject; + +/** Allows the delegate to handle any field errors. + * @param fieldErrors The field errors. + * @param loadObject The load object.*/ +- (void)handleFieldErrors:(nullable NSArray *)fieldErrors loadObject:(nonnull MVMCoreLoadObject *)loadObject; + +/** Called when the load is all finished. Use this to perform any cleanup. + * @param loadObject The load object. + * @param loadedViewController If a view controller was loaded or not. + * @param error The error object. + * Details: Only sends an errorObject if there is an error. */ +- (void)loadFinished:(nullable MVMCoreLoadObject *)loadObject loadedViewController:(nullable UIViewController *)loadedViewController error:(nullable MVMCoreErrorObject *)error; + +// Called when the load is cancelled. +- (void)loadCancelled:(nullable MVMCoreLoadObject *)loadObject; + +@end + diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.h b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.h new file mode 100644 index 0000000..7ccc0ed --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.h @@ -0,0 +1,68 @@ +// +// MVMCoreLoadHandler.h +// myverizon +// +// Created by ScottMVMCoreErrorObject.h Pfeil on 11/12/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// +// Handles loading data + +#import +#import +#import +#import +#import + +@class MVMCoreRequestParameters; +@class MVMCoreErrorObject; +@class MVMCoreLoadObject; +@class MVMCoreLoadRequestOperation; + +@interface MVMCoreLoadHandler : NSObject + +// Returns the shared instance of this singleton ++ (nullable instancetype)sharedGlobal; + +// Adds an additional externally managed queue to the list of all loading queues. This allows for cross queue dependency management. +- (void)registerLoadQueue:(NSOperationQueue *_Nonnull)queue; + +// Returns the error location for the given load object. Important for proper logging. +- (nonnull NSString *)errorLocationForRequest:(nonnull MVMCoreLoadObject *)loadObject; + +// 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; + +#pragma mark - Request Functions. + +// Creates a request object with the given parameters. +- (nonnull NSURLRequest *)requestWithParameters:(nonnull MVMCoreRequestParameters *)requestParameters; + +// 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. +- (nonnull NSURLSessionTask *)sendRequest:(nonnull MVMCoreRequestParameters *)requestParameters locationForError:(nonnull NSString *)locationForError requestFinished:(nullable void (^)(id _Nullable jsonObject, MVMCoreErrorObject * _Nullable error))requestFinished; + +#pragma mark - Loading Functions + +// Loads a blocking request with the passed in parameters and data for the next page. Pass in the data to handle certain functions. +- (nonnull MVMCoreLoadRequestOperation *)loadRequest:(nonnull MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject*)delegate; + +// Loads a background request with the passed in parameters and data for the next page. Pass in the data to handle certain functions. +- (nonnull MVMCoreLoadRequestOperation *)loadBackgroundRequest:(nonnull MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject*)delegate; + +// Loads a blocking request with the passed in load object and data for the next page. +- (nonnull MVMCoreLoadRequestOperation *)loadObject:(nonnull MVMCoreLoadObject *)loadObject; + +// Cancels all of the current loads. +- (void)cancelAllLoads; + +#pragma mark - Standard Delegate Functions + +// By default, if error code is not zero, returns not to continue. ++ (BOOL)defaultCheckForSpecificErrors:(nullable MVMCoreErrorObject *)errorObject loadObject:(nonnull MVMCoreLoadObject *)loadObject; + +// By default, returns continue loading and decides not to throw an error. ++ (BOOL)defaultHandleModuleError:(nonnull NSString *)module loadObject:(nonnull MVMCoreLoadObject *)loadObject error:(nonnull MVMCoreErrorObject *)error; + +// Tracks that it has finished.. ++ (void)defaultLoadFinished:(nullable MVMCoreLoadObject *)loadObject loadedViewController:(nullable UIViewController *)loadedViewController error:(nullable MVMCoreErrorObject *)error; + +@end diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.m b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.m new file mode 100644 index 0000000..bcd112b --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadHandler.m @@ -0,0 +1,364 @@ +// +// MVMCoreLoadHandler.m +// myverizon +// +// Created by Scott Pfeil on 11/12/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreLoadHandler.h" +#import "MVMCoreGetterUtility.h" +#import "MVMCoreLoggingHandler.h" +#import "MVMCoreLoadRequestOperation.h" +#import "MVMCoreCache.h" +#import "MFHardCodedServerResponse.h" +#import "MFFreebeeHandler.h" +#import "MVMCoreNavigationHandler.h" +#import "MFSSLPinningHandler.h" +#import "MVMCoreLoadObject.h" +#import "MVMCoreSessionObject.h" +#import "MVMCoreJSONConstants.h" +#import "MVMCoreErrorObject.h" +#import "MVMCoreErrorConstants.h" +#import "MVMCoreHardcodedStringsConstants.h" +#import "MVMCoreObject.h" + +@interface MVMCoreLoadHandler () + +@property (strong, nonatomic) NSOperationQueue *blockingLoadQueue; +@property (strong, nonatomic) NSOperationQueue *backgroundLoadQueue; +@property (strong, nonatomic) NSMutableArray *registeredLoadQueues; + +@end + +@implementation MVMCoreLoadHandler + ++ (nullable instancetype)sharedGlobal { + static dispatch_once_t once; + static id sharedInstance; + + dispatch_once(&once, ^ + { + sharedInstance = [[self alloc] init]; + }); + + return sharedInstance; +} + +- (instancetype)init { + if (self = [super init]) { + self.blockingLoadQueue = [[NSOperationQueue alloc] init]; + self.blockingLoadQueue.maxConcurrentOperationCount = 1; + + self.backgroundLoadQueue = [[NSOperationQueue alloc] init]; + + self.registeredLoadQueues = [[NSMutableArray alloc] initWithCapacity:3]; + [self registerLoadQueue:self.blockingLoadQueue]; + [self registerLoadQueue:self.backgroundLoadQueue]; + } + return self; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if ([keyPath isEqualToString:@"operations"] && [self.registeredLoadQueues containsObject:object] && ([change[NSKeyValueChangeNewKey] count] > [change[NSKeyValueChangeOldKey] count])) { + // An operation was added to a registered queue. + for (NSOperation *op in change[NSKeyValueChangeNewKey]) { + if ([op isKindOfClass:[MVMCoreLoadRequestOperation class]] && ![(MVMCoreLoadRequestOperation*)op areDependenciesAdded]) { + [self assignPageTypeDependencies:(MVMCoreLoadRequestOperation*)op]; + } + } + } +} + +- (void)registerLoadQueue:(NSOperationQueue *)queue { + [self.registeredLoadQueues addObject:queue]; + [queue addObserver:self forKeyPath:@"operations" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil]; +} + +- (void)assignPageTypeDependencies:(MVMCoreLoadRequestOperation *)operation { + @synchronized (self) { + if (operation.dependenciesAdded) return; // Prevents dependencies from being added twice. + operation.dependenciesAdded = YES; + NSString *addingPageType = operation.requestParameters.pageType; + NSArray* successivePageTypes = operation.requestParameters.successivePageTypes; + // For each registered queue... + for (NSOperationQueue *queue in self.registeredLoadQueues) { + // Iterate thru each existing operation... + for (NSOperation *existingOperation in queue.operations) { + // Ensure its a MVMCoreLoadRequestOperation... + if ([existingOperation isKindOfClass:[MVMCoreLoadRequestOperation class]] && existingOperation != operation) { + MVMCoreRequestParameters *existingOpParams = ((MVMCoreLoadRequestOperation *)existingOperation).requestParameters; + // If existing operation page type is one of the new operation registered conflicting page types, + // or the new operation page type is one of the existing operation's conflicting page types... + if ([successivePageTypes containsObject:existingOpParams.pageType] || + [existingOpParams.successivePageTypes containsObject:addingPageType]) { + // Add a dependency on the new page type of the existing page type. Note dependencies are only added to new operations, so the method would never push an operation ahead of an exisitng operation, avoiding causing circular dependencies. + MVMCoreLog(@"Adding %@ call dependency on %@", existingOpParams.pageType, operation.requestParameters.pageType); + [operation addDependency:existingOperation]; + } + } + } + } + } +} + +- (nonnull NSString *)errorLocationForRequest:(nonnull MVMCoreLoadObject *)loadObject { + + return [self errorLocationForRequest:loadObject.delegate pageType:loadObject.requestParameters.pageType modules:[NSString stringWithFormat:@"%@",loadObject.requestParameters.modules]]; +} + +- (nonnull NSString *)errorLocationForRequest:(nonnull id)requestingObject pageType:(nonnull NSString *)pageType modules:(nonnull NSString *)modules { + + // By default, sends as ClassNameRequests_RequestedPageType. + return [NSString stringWithFormat:@"%@Requests_pageType:%@_modules:%@",NSStringFromClass([requestingObject class]),pageType,modules]; +} + +#pragma mark - Request Functions. + +- (nonnull NSURLRequest *)requestWithParameters:(nonnull MVMCoreRequestParameters *)requestParameters { + + NSURL *url = nil; + + if (requestParameters.alternateBaseURL) { + url = requestParameters.alternateBaseURL; + } else { + url = [MVMCoreSessionObject sharedGlobal].baseURL; + } + + if (!url) { + url = [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) { + [requestParameters addRequestParameters:@{KeyModuleList:requestParameters.modules}]; + } + + NSTimeInterval timeOutInterval = 60; + if (requestParameters.customTimeoutTime) { + timeOutInterval = requestParameters.customTimeoutTime.floatValue; + } else if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(timeOutIntervalForRequest:)]) { + timeOutInterval = [[MVMCoreObject sharedInstance].globalLoadDelegate timeOutIntervalForRequest:requestParameters]; + } + + // Create and send post request, adding params as http body. + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:timeOutInterval]; + + [request setHTTPMethod:@"POST"]; + [request setValue:@"no-cache, no-store" forHTTPHeaderField:@"Cache-Control"]; + + // Either adds the params to the body or the header. + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; + + + NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; + + // Sets up the Initial parameters. + if (requestParameters.addInitialRequestParameters) { + NSDictionary *initialParameters = [[MVMCoreSessionObject sharedGlobal] getInitialParameters]; + if (initialParameters) { + [parameters setObject:initialParameters forKey:@"InitialParams"]; + } + } + + // Adds request specific parameters + if (requestParameters.parameters.count > 0) { + [parameters setObject:requestParameters.parameters forKey:@"RequestParams"]; + } + + if ([[MVMCoreObject sharedInstance].mobileFirstDelegate shouldLogRequestsResponse]) { + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:parameters options:NSJSONWritingPrettyPrinted error:&error]; + NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + MVMCoreLog(@"Request Parameters for URL %@:\n%@",[request.URL absoluteString], jsonString); + } + + //adding Free Bee header + if(YES == [[MFFreebeeHandler sharedHandler] isFreeBeeEnabled] + && YES == [[MFFreebeeHandler sharedHandler] isValidCampaign]) { + + BOOL isExcluded = [[MFFreebeeHandler sharedHandler] isExcludedModule:requestParameters.parentPageType]; + [[MFFreebeeHandler sharedHandler] enableFreeBeeForCurrentModule:!isExcluded]; + + // to enable only for self server module + if(NO == isExcluded){ + + NSDictionary* campaignDictionary = [[MFFreebeeHandler sharedHandler] campaignHeaderforUrl:request.URL]; + if(campaignDictionary) { + + MVMCoreLog(@"Freebee Request Campaign Dictionary : %@", campaignDictionary); + [request addValue:campaignDictionary[@"Proxy-Authorization"] forHTTPHeaderField:@"Proxy-Authorization"]; + } + } + } + + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:&error]; + if (data) { + [request setHTTPBody:data]; + } else { + // Log the json error + MVMCoreErrorObject *errorObject = [[MVMCoreErrorObject alloc] initWithTitle:nil message:nil code:ErrorCodeParsingJSON domain:ErrorDomainNative location:[NSString stringWithFormat:@"requestWithParameters_%@",requestParameters.pageType]]; + [MVMCoreLoggingHandler addErrorToLog:errorObject]; + } + + return request; +} + +- (nonnull 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 (requestFinished) { + requestFinished(response, nil); + } + return nil; + } +#endif + + NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate]; + NSURLRequest *request = [self requestWithParameters:requestParameters]; + NSURLSession *session = [MVMCoreSessionObject sharedGlobal].session; + + + //In case of error for time out, we need to revert to normal session object + if(YES == [[MFFreebeeHandler sharedHandler] isFreeBeeEnabled] && + YES == [[MFFreebeeHandler sharedHandler] isValidCampaign] && + YES == [[MFFreebeeHandler sharedHandler] isFreeBeeEnabledForCurrentModule]) { + + // to enable only for self server module + if([MVMCoreSessionObject sharedGlobal].freeBeeSession != nil) { + session = [MVMCoreSessionObject sharedGlobal].freeBeeSession; + MVMCoreLog(@"*********************************FreeBee Sponsored Request Start**********************************"); + } + } + + [[NSNotificationCenter defaultCenter] postNotificationName:MVMCoreNotificationGoingToServer object:nil]; + + NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + + MVMCoreLog(@"Request Time %f",[NSDate timeIntervalSinceReferenceDate] - startTime); + + + NSMutableDictionary *trackInfo = [[NSMutableDictionary alloc]initWithDictionary:@{ + @"pageType":requestParameters.pageType ?: @"", + @"requestTime":@([NSDate timeIntervalSinceReferenceDate] - startTime), + @"url":request.URL.description ?: @""}]; + if (error) { + [trackInfo setObject:error.localizedDescription forKey:@"error"]; + } + + [MVMCoreLoggingHandler logWithDelegateWithObject:nil withName:@"httpRequestStatus" withExtraInfo:trackInfo]; + + id jsonObject = nil; + MVMCoreErrorObject *errorObject = nil; + if (!error) { + + if (data.length > 0) { + + // Serialize the downloaded data to json object. + NSError *error = nil; + jsonObject = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; + + 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:ErrorDomainNative location:locationForError]; + } else if ([[MVMCoreObject sharedInstance].mobileFirstDelegate shouldLogRequestsResponse]) { + + // 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.) + // MVMCoreLog(@"Raw Response:\n%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); + + // Log the response pretty. + NSData *prettyData = [NSJSONSerialization dataWithJSONObject:jsonObject options:NSJSONWritingPrettyPrinted error:&error]; + NSString *responseString = [[NSString alloc] initWithData:prettyData encoding:NSUTF8StringEncoding]; + MVMCoreLog(@"Response for Request Page Type %@:\n%@",requestParameters.pageType, responseString); + } + } else { + // Empty response. + errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeEmptyResponse domain:ErrorDomainNative location:locationForError]; + } + } else { + // If the request was cancelled due to SSL error or other issues. + // Error with the request. + errorObject = [MFSSLPinningHandler createErrorObjectForNSErrorAndVerifySSLErrors:error location:locationForError]; + } + + if (requestFinished) { + requestFinished(jsonObject,errorObject); + } + }]; + [task resume]; + return task; +} + +#pragma mark - Loading Functions + +- (MVMCoreLoadRequestOperation *)loadRequest:(nonnull MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject*)delegate { + + MVMCoreLoadRequestOperation *loadOperation = [[MVMCoreLoadRequestOperation alloc] initWithRequestParameters:requestParameters dataForPage:dataForPage delegate:delegate backgroundLoad:NO]; + [self.blockingLoadQueue addOperation:loadOperation]; + return loadOperation; +} + +- (MVMCoreLoadRequestOperation *)loadBackgroundRequest:(nonnull MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject*)delegate { + + MVMCoreLoadRequestOperation *loadOperation = [[MVMCoreLoadRequestOperation alloc] initWithRequestParameters:requestParameters dataForPage:dataForPage delegate:delegate backgroundLoad:YES]; + [self.backgroundLoadQueue addOperation:loadOperation]; + return loadOperation; +} + +- (MVMCoreLoadRequestOperation *)loadObject:(nonnull MVMCoreLoadObject *)loadObject { + + MVMCoreLoadRequestOperation *loadOperation = [[MVMCoreLoadRequestOperation alloc] initWithLoadObject:loadObject backgroundLoad:NO]; + [self.blockingLoadQueue addOperation:loadOperation]; + return loadOperation; +} + +- (void)cancelAllLoads { + [self.blockingLoadQueue cancelAllOperations]; + [self.backgroundLoadQueue cancelAllOperations]; +} + +#pragma mark - Standard Delegate Functions + ++ (BOOL)defaultCheckForSpecificErrors:(nullable MVMCoreErrorObject *)errorObject loadObject:(nonnull MVMCoreLoadObject *)loadObject { + + // Continues, despite error, if we have a page type. + if (errorObject && !loadObject.pageType) { + return NO; + } + return YES; +} + ++ (BOOL)defaultHandleModuleError:(nonnull NSString *)module loadObject:(nonnull MVMCoreLoadObject *)loadObject error:(nonnull MVMCoreErrorObject *)error { + + // Currently decides not to throw an error and returns continue + error.silentError = YES; + + return YES; +} + ++ (void)defaultLoadFinished:(nullable MVMCoreLoadObject *)loadObject loadedViewController:(nullable UIViewController *)loadedViewController error:(nullable MVMCoreErrorObject *)error { +// [[MFAdobeTracker sharedTracker] trackResponseInfo:loadObject.responseInfoMap forPagetype:loadObject.pageType]; +} + +@end diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadObject.h b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadObject.h new file mode 100644 index 0000000..daa038f --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadObject.h @@ -0,0 +1,60 @@ +// +// MVMCoreLoadObject.h +// myverizon +// +// Created by Scott Pfeil on 11/16/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// +// An object that holds load data + +#import +#import +#import +#import + + +@class MVMCoreLoadRequestOperation; +@class MVMCoreRequestParameters; + +@interface MVMCoreLoadObject : NSObject + +// The page that was loaded. +@property (nullable, strong, nonatomic) NSString *pageType; + +// The data for hte page that was loaded +@property (nullable, strong, nonatomic) NSDictionary *pageJSON; + +// the modules that are needed +@property (nullable, strong, nonatomic) NSDictionary *modulesJSON; + +// the response info +@property (nullable, strong, nonatomic) NSDictionary *responseInfoMap; + +// any additional parameters +@property (nullable, strong, nonatomic) NSDictionary *systemParametersJSON; + +// The request +@property (nullable, strong, nonatomic) MVMCoreRequestParameters *requestParameters; + +// any additional data for the page +@property (nullable, strong, nonatomic) NSDictionary *dataForPage; + +// The load delegate +@property (nullable, weak, nonatomic) NSObject *delegate; + +// The operation that is loading. +@property (nullable, weak, nonatomic) MVMCoreLoadRequestOperation *operation; + +// Flags for if we loaded from the cache. +@property (nonatomic) BOOL pageDataFromCache; +@property (nonatomic) BOOL moduleDataFromCache; + +- (nullable instancetype)initWithPageJSON:(nullable NSDictionary *)pageJSON modulesJSON:(nullable NSDictionary *)modulesJSON requestParameters:(nullable MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject*)delegate; + +- (nullable instancetype)initWithRequestParameters:(nullable MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject*)delegate; + +- (nullable instancetype)initWithDelegate:(nullable NSObject*)delegate; + +- (nullable instancetype)initWithPageJSON:(nullable NSDictionary *)pageJSON errorObject:(nullable MVMCoreErrorObject *)errorObject; + +@end diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadObject.m b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadObject.m new file mode 100644 index 0000000..3064012 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadObject.m @@ -0,0 +1,57 @@ +// +// MVMCoreLoadObject.m +// myverizon +// +// Created by Scott Pfeil on 11/16/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreLoadObject.h" +#import "MVMCoreErrorObject.h" +#import "MVMCoreJSONConstants.h" + +@implementation MVMCoreLoadObject + +- (nullable instancetype)initWithPageJSON:(nullable NSDictionary *)pageJSON modulesJSON:(nullable NSDictionary *)modulesJSON requestParameters:(nullable MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject*)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*)delegate { + if (self = [self initWithDelegate:delegate]) { + self.requestParameters = requestParameters; + self.dataForPage = dataForPage; + } + return self; +} + +- (nullable instancetype)initWithDelegate:(nullable NSObject*)delegate { + if (self = [super init]) { + self.delegate = delegate; + } + return self; +} + +- (nullable instancetype)initWithPageJSON:(nullable NSDictionary *)pageJSON errorObject:(nullable MVMCoreErrorObject *)errorObject { + if (self = [self initWithPageJSON:pageJSON modulesJSON:nil requestParameters:nil dataForPage:nil delegate:nil]) { + + NSMutableDictionary *responseInfo = [NSMutableDictionary dictionary]; + if (errorObject.code) { + [responseInfo setObject:[errorObject stringErrorCode] forKey:KeyCode]; + } + if (errorObject.messageToLog) { + [responseInfo setObject:errorObject.messageToLog forKey:KeyMessage]; + } + if (errorObject.messageToDisplay) { + [responseInfo setObject:errorObject.messageToDisplay forKey:KeyUserMessage]; + } + self.responseInfoMap = responseInfo; + } + return self; +} + +@end diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.h b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.h new file mode 100644 index 0000000..ef5fd17 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.h @@ -0,0 +1,106 @@ +// +// MVMCoreLoadRequestOperation.h +// myverizon +// +// Created by Scott Pfeil on 11/11/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// +// The meat and potatos. Handles the load protocol. Should NOT be called on the main queue. + +#import +#import +#import +#import + +@class MVMCoreRequestParameters; +@class MVMCoreLoadObject; + +@interface MVMCoreLoadRequestOperation : MVMCoreOperation + +@property (nullable, strong, nonatomic) MVMCoreRequestParameters *requestParameters; +@property (nullable, strong, nonatomic) MVMCoreLoadObject *loadObject; +@property (nullable, strong, nonatomic) NSDictionary *dataForPage; +@property (nullable, strong, nonatomic) NSObject *delegate; +@property (nonatomic) BOOL backgroundLoad; +@property (nonatomic, getter=areDependenciesAdded) BOOL dependenciesAdded; + +// 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 delegate:(nullable NSObject*)delegate 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; + +/* 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; + +/** Sends the request to the server with the given parameters. + * @param loadObject The load object for this operation. Should contain any data loaded from the cache. + * @param completionHandler gets called with the json assuming there were no errors. Any errors will be handled and completion handler will not be called. */ ++ (void)sendRequest:(nonnull MVMCoreRequestParameters *)requestParameters loadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(NSDictionary * _Nullable json))completionHandler; + +/* A helper function. If we shouldn't continue, finish or abort with error. If we should continue, call the continue block. Error is not handled if we continue. + * @param errorBlock The block that gets called when we shouldn't continue for any reason. + * @param completionHandler The block that gets when we should continue. */ ++ (void)handleShouldContinue:(BOOL)shouldContinue error:(nullable MVMCoreErrorObject *)error loadObject:(nullable MVMCoreLoadObject *)loadObject errorBlock:(nullable void (^)(void))errorBlock continueBlock:(nonnull void (^)(void))continueBlock; + +// Processes the json retrieved from the server. Will cache the json and process it. ++ (void)processJSONFromServer:(nonnull NSDictionary *)jsonDictionary loadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(MVMCoreLoadObject * _Nonnull loadObject, MVMCoreErrorObject * _Nullable error))completionHandler; + +/** Handles the load object now that the json is retrieved. Loads a page when asked and finishes up the load. + * @param loadObject The load data from the cache or server. + * @param error A possible error that was caught and needs to be passed to load finished. It has already been handled.*/ ++ (void)handleLoadObject:(nonnull MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error; + +/** Checks the json for any errors. First, checks the response info for standard errors. Then, allows the core delegate to handle global errors. Then, allows the delegate to handle controller specific errors. + * @param loadObject The load data from the cache or server. + * @param completionHandler The completion handler to load once finished. Returns if we should continue loading or not. */ ++ (void)checkForErrorsInJSONForLoadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(BOOL shouldContinueLoad, MVMCoreLoadObject * _Nullable loadObject, MVMCoreErrorObject * _Nullable error))completionHandler; + +/** Caches the modules. + * @param modules a dictionary of modules. + * @param loadObject The load data from the cache or server. + * @param error The error object passed in will be set in the case of an error. + * @return True if the calling process should continue. */ ++ (BOOL)cacheModules:(nullable NSDictionary *)modules loadObject:(nonnull MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error; + +/** Caches the additional pages. + * @param pages a dictionary of pages. + * @param loadObject The load data from the cache or server. + * @param error The error object passed in will be set in the case of an error. + * @return True if the calling process should continue. */ ++ (BOOL)cachePages:(nullable NSDictionary *)pages loadObject:(nonnull MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error; + +/** Verifies that all needed modules are loaded + * @param viewController The view controller going to be loaded. + * @param loadObject The load data from the cache or server. + * @param error The error object passed in will be set in the case of an error. + * @return True if the calling process should continue. */ ++ (BOOL)verifyRequiredModulesLoaded:(nonnull UIViewController *)viewController loadObject:(nullable MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error; + +/** 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.*/ ++ (void)createViewControllerWithLoadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(UIViewController * _Nullable viewController, MVMCoreLoadObject *_Nonnull loadObject))completionHandler; + +// Displays the view controller to the screen. ++ (void)displayViewController:(nonnull UIViewController *)viewController loadObject:(nullable MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error; + +// Handles the error based on the error object passed in. May log and/or display the error. ++ (void)handleError:(nonnull MVMCoreErrorObject *)error loadObject:(nonnull MVMCoreLoadObject *)loadObject showAlertForErrorIfApplicable:(BOOL)showAlertForErrorIfApplicable; + +// Shows the appropriate alert tyle for the given response info and/or error. ++ (void)createAndShowAlertForLoadObject:(nullable MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error actionDelegate:(nonnull id)actionDelegate; + +/** Called when the load was finished due to an error. Handles the error and ends the load. + * @param error The error that occured. May be logged depending. + * @param loadObject The load data for the error. **/ ++ (void)loadAbortedWithError:(nonnull MVMCoreErrorObject *)error loadObject:(nonnull MVMCoreLoadObject *)loadObject; + +/** Called when the load is finished. Ends the load. Lets the delegate know that the load finished and passes along any error that may have caused an abort. + * @param errorObject An error if one caused an abort. + * @param loadedViewController If a view controller was loaded or not, it will be passed here. + * @param loadObject The load data **/ ++ (void)loadFinished:(nonnull MVMCoreLoadObject *)loadObject loadedViewController:(nullable UIViewController *)loadedViewController errorObject:(nullable MVMCoreErrorObject *)errorObject; + +@end diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.m b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.m new file mode 100644 index 0000000..2f466a9 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreLoadRequestOperation.m @@ -0,0 +1,908 @@ +// +// MVMCoreLoadRequestOperation.m +// myverizon +// +// Created by Scott Pfeil on 11/11/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// + +#import +#import "MVMCoreLoadHandler.h" +#import "MVMCoreLoadingOverlayHandler.h" +#import "MVMCoreCache.h" +#import "MVMCoreSessionTimeHandler.h" +#import "MVMCoreLoggingHandler.h" +#import "MVMCoreSessionObject.h" +#import "MVMCoreAlertHandler.h" +#import "MVMCoreViewControllerMappingObject.h" +#import "MVMCoreNavigationHandler.h" +#import "MVMCoreAlertObject.h" +#import "MFFreebeeHandler.h" +#import "FreeBeeAuthObject.h" +#import "MFSSLPinningHandler.h" +#import +#import "MVMCoreLoadObject.h" +#import "MVMCoreRequestParameters.h" +#import "MVMCoreGetterUtility.h" +#import "NSDictionary+MFConvenience.h" +#import "MVMCoreJSONConstants.h" +#import "MVMCoreHardcodedStringsConstants.h" +#import "MVMCoreErrorConstants.h" +#import "MVMCoreActionUtility.h" +#import +#import "MVMCoreObject.h" + +@interface MVMCoreLoadRequestOperation () + +@property (weak, nonatomic) NSURLSessionTask *sessionTask; + +// For temporarily storing any alert to show until we determine it's delegate. +@property (nonatomic) BOOL alertToShow; +@property (strong, nonatomic, nullable) MVMCoreErrorObject *errorForAlertToShow; + +@end + +@implementation MVMCoreLoadRequestOperation + +#pragma mark - Initializers + +- (nullable instancetype)initWithRequestParameters:(nullable MVMCoreRequestParameters *)requestParameters dataForPage:(nullable NSDictionary *)dataForPage delegate:(nullable NSObject*)delegate backgroundLoad:(BOOL)backgroundLoad { + if (self = [super init]) { + self.requestParameters = requestParameters; + self.dataForPage = dataForPage; + self.delegate = delegate; + self.backgroundLoad = backgroundLoad; + } + return self; +} + +- (nullable instancetype)initWithLoadObject:(nullable MVMCoreLoadObject *)loadObject backgroundLoad:(BOOL)backgroundLoad { + + if (self = [self initWithRequestParameters:loadObject.requestParameters dataForPage:loadObject.dataForPage delegate:loadObject.delegate backgroundLoad:backgroundLoad]) { + self.loadObject = loadObject; + } + return self; +} + +#pragma mark - Operation + +- (void)cancel { + [super cancel]; + [self.sessionTask cancel]; +} + +- (void)start { + + // Adds a loading overlay if necessary. + if (!self.backgroundLoad && !self.requestParameters.noloadingOverlay) { + [[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] startLoading]; + } + + [super start]; +} + +- (void)markAsFinished { + + // stop any loading animation we may have started + if (!self.backgroundLoad && !self.requestParameters.noloadingOverlay) { + [[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:NO]; + } + + MVMCoreLog(@"Load Operation finished for page type %@, background load %@", self.requestParameters.pageType, @(self.backgroundLoad)); + [super markAsFinished]; +} + +- (BOOL)checkAndHandleForCancellation { + + if ([self isCancelled] && [self.delegate respondsToSelector:@selector(loadCancelled:)]) { + + MVMCoreLoadObject *loadObject = self.loadObject; + if (!loadObject) { + loadObject = [[MVMCoreLoadObject alloc] initWithPageJSON:nil modulesJSON:nil requestParameters:self.requestParameters dataForPage:self.dataForPage delegate:self.delegate]; + loadObject.operation = self; + } + + // Must let the delegate know if cancelled. + [loadObject.delegate loadCancelled:loadObject]; + } + return [super checkAndHandleForCancellation]; +} + +- (void)main { + MVMCoreLog(@"Load Operation begun for page type %@, background load %@, delegate %@", self.requestParameters.pageType, @(self.backgroundLoad),self.delegate); + + // Always check for cancellation before launching the task. + if ([self checkAndHandleForCancellation]) { + return; + } + + if (!self.requestParameters) { + + // No load requested, finish. + MVMCoreLoadObject *loadObject = [[MVMCoreLoadObject alloc] initWithRequestParameters:nil dataForPage:self.dataForPage delegate:self.delegate]; + loadObject.operation = self; + [MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:nil errorObject:nil]; + } else if (self.loadObject) { + + // Directly handle the passed in load object. + self.loadObject.operation = self; + [MVMCoreLoadRequestOperation handleLoadObject:self.loadObject error:nil]; + } else { + + // No provided load object, check the cache for data first.. + [MVMCoreLoadRequestOperation checkCacheForDataForRequest:self.requestParameters completionHandler:^(NSDictionary *pageFromCache, NSDictionary *modulesFromCache) { + + if ([self checkAndHandleForCancellation]) { + return; + } + + // Log if needed. + if ([[MVMCoreObject sharedInstance].mobileFirstDelegate shouldLogRequestsResponse]) { + if (pageFromCache) { + MVMCoreLog(@"loaded from cache page %@",[MVMCoreActionUtility formatDictionaryAsJSONString:pageFromCache]); + } + if (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]; + + // Check if we need to go to server for missing data. + MVMCoreRequestParameters *requestForMissingData = [MVMCoreLoadRequestOperation createRequestForDataWithLoadObject:loadObject]; + if (!requestForMissingData) { + + // We have all the needed data, continue with the load. + [[MVMCoreSessionTimeHandler sharedSessionHandler] sendKeepAliveToServer:NO]; + [MVMCoreLoadRequestOperation handleLoadObject:loadObject error:nil]; + } else { + + // Send a new request to the server. + [MVMCoreLoadRequestOperation sendRequest:requestForMissingData loadObject:loadObject completionHandler:^(NSDictionary * _Nullable json) { + + // Process the data retrieved from the server. + [MVMCoreLoadRequestOperation processJSONFromServer:json loadObject:loadObject completionHandler:^(MVMCoreLoadObject * _Nonnull loadObject, MVMCoreErrorObject * _Nullable error) { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + if (loadObject.pageDataFromCache || loadObject.pageType) { + + // Can continue loading with the page. + [MVMCoreLoadRequestOperation handleLoadObject:loadObject error:error]; + } else if (loadObject.operation.alertToShow || loadObject.operation.backgroundLoad || loadObject.requestParameters.noViewControllerToLoad) { + + // Alert to show and finish, or nothing to show and asked to show and finish. + [MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:nil errorObject:error]; + } else { + + // Error, no message and no page but we wanted to show something. + MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeNoPageType domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]; + [MVMCoreLoadRequestOperation loadAbortedWithError:error loadObject:loadObject]; + } + }]; + }]; + } + }]; + } +} + +#pragma mark - Load Functions + ++ (void)checkCacheForDataForRequest:(nonnull MVMCoreRequestParameters *)requestParameters completionHandler:(nonnull void (^)(NSDictionary * _Nullable pageFromCache, NSDictionary * _Nullable modulesFromCache))completionHandler { + + if (requestParameters.neverLoadFromCache) { + + // Never use the cache. + completionHandler(nil,nil); + } else if (requestParameters.pageType.length != 0 && requestParameters.modules.count > 0) { + + // Check cache function for page json is already in the cache. + [[MVMCoreCache sharedCache] fetchJSONForPageType:requestParameters.pageType completionHandler:^(NSDictionary * _Nullable jsonDictionary) { + + // Check cache function for modules already in the cache. + NSDictionary *pageDictionary = jsonDictionary; + [[MVMCoreCache sharedCache] fetchJSONForModules:requestParameters.modules completionHandler:^(NSDictionary * _Nullable jsonDictionary) { + completionHandler(pageDictionary,jsonDictionary); + }]; + }]; + } else if (requestParameters.pageType.length != 0) { + + // Check cache function if page json is already in the cache. + [[MVMCoreCache sharedCache] fetchJSONForPageType:requestParameters.pageType completionHandler:^(NSDictionary * _Nullable jsonDictionary) { + completionHandler(jsonDictionary,nil); + }]; + } else if (requestParameters.modules.count > 0) { + + // Check cache function if modules already in the cache. + [[MVMCoreCache sharedCache] fetchJSONForModules:requestParameters.modules completionHandler:^(NSDictionary * _Nullable jsonDictionary) { + completionHandler(nil,jsonDictionary); + }]; + } +} + +- (nonnull MVMCoreLoadObject *)createLoadObjectWithPageFromCache:(nullable NSDictionary *)pageFromCache modulesFromCache:(nullable NSDictionary *)modulesFromCache { + + MVMCoreLoadObject *loadObject = [[MVMCoreLoadObject alloc] initWithPageJSON:pageFromCache modulesJSON:modulesFromCache requestParameters:self.requestParameters dataForPage:self.dataForPage delegate:self.delegate]; + loadObject.operation = self; + + // If we successfully grab from the cache, then the response page type is the requesting page type. + if (pageFromCache) { + loadObject.pageType = self.requestParameters.pageType; + } + + // Store if we loaded from the cache or not. + loadObject.pageDataFromCache = (pageFromCache != nil); + loadObject.moduleDataFromCache = (modulesFromCache != nil); + + return loadObject; +} + ++ (nullable MVMCoreRequestParameters *)createRequestForDataWithLoadObject:(nonnull MVMCoreLoadObject *)loadObject { + + // Check if the page was found in the cache. + BOOL pageNotInCache = loadObject.operation.requestParameters.pageType && !loadObject.pageDataFromCache; + + // Check all modules were found in the cache + NSMutableArray *modulesNotInCache = [NSMutableArray array]; + for (NSString *module in loadObject.operation.requestParameters.modules) { + if (![loadObject.modulesJSON objectForKey:module]) { + [modulesNotInCache addObject:module]; + } + } + + MVMCoreRequestParameters *requestParametersForServer = nil; + if (pageNotInCache || modulesNotInCache.count > 0) { + + // We are missing data, will need to go to server. + requestParametersForServer = [loadObject.operation.requestParameters copy]; + + // Page was in the cache, remove it from the server request. + // new server needs page type always it seems + /*if (!pageNotInCache) { + requestParametersForServer.pageType = nil; + }*/ + + requestParametersForServer.modules = modulesNotInCache.count > 0 ? modulesNotInCache : nil; + } + + return requestParametersForServer; +} + ++ (void)sendRequest:(nonnull MVMCoreRequestParameters *)requestParameters loadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(NSDictionary * _Nullable json))completionHandler { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + loadObject.operation.sessionTask = [[MVMCoreLoadHandler sharedGlobal] sendRequest:requestParameters locationForError:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject] requestFinished:^(id jsonObject, MVMCoreErrorObject *error) { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + if (jsonObject) { + + if ([jsonObject isKindOfClass:[NSDictionary class]]) { + + // Update the session timer on a good response. + [[MVMCoreSessionTimeHandler sharedSessionHandler] startSessionTimer]; + + completionHandler(jsonObject); + } else { + + // 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]; + } + }]; +} + ++ (void)handleShouldContinue:(BOOL)shouldContinue error:(nullable MVMCoreErrorObject *)error loadObject:(nullable MVMCoreLoadObject *)loadObject errorBlock:(nullable void (^)(void))errorBlock continueBlock:(nonnull void (^)(void))continueBlock { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + if (error && !shouldContinue) { + + // There's an error and we shouldn't continue, handle and end load. + if (errorBlock) { + errorBlock(); + } + [MVMCoreLoadRequestOperation loadAbortedWithError:error loadObject:loadObject]; + } else if (shouldContinue) { + continueBlock(); + } else { + // End load + if (errorBlock) { + errorBlock(); + } + [MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:nil errorObject:error]; + } +} + ++ (void)notifyListenersOfNewResponse:(NSDictionary *)pages modules:(NSDictionary *)modules systemParameters:(NSDictionary *)systemParameters { + + NSMutableDictionary *userInfo = nil; + if (pages.count > 0 || modules.count > 0 || systemParameters.count > 0) { + userInfo = [NSMutableDictionary dictionary]; + if (pages.count > 0) { + [userInfo setObject:pages forKey:KeyPageMap]; + } + if (modules.count > 0) { + [userInfo setObject:modules forKey:KeyModuleMap]; + } + if (systemParameters.count > 0) { + [userInfo setObject:systemParameters forKey:KeySystemParameters]; + } + } + + if (userInfo) { + [[NSNotificationCenter defaultCenter] postNotificationName:NotificationResponseLoaded object:nil userInfo:userInfo]; + } +} + ++ (void)processJSONFromServer:(nonnull NSDictionary *)jsonDictionary loadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(MVMCoreLoadObject * _Nonnull loadObject, MVMCoreErrorObject * _Nullable error))completionHandler { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + // Store the new page data if we didn't load page data from the cache. + NSDictionary *pageJSON = [jsonDictionary objectForKey:KeyPage ofType:[NSDictionary class]]; + if (!loadObject.pageDataFromCache) { + loadObject.pageJSON = pageJSON; + loadObject.pageType = [pageJSON string:KeyPageType]; + } + + // Sets the response info map + loadObject.responseInfoMap = [jsonDictionary dict:KeyResponseInfo]; + + // Dismiss any top alerts that server wants us to dismiss + [[MVMCoreAlertHandler sharedAlertHandler] hidePersistentTopAlertViewOfTypeString:[loadObject.responseInfoMap string:@"disableType"]]; + + // Adds the modules received from server to any modules we grabbed from the cache. + NSDictionary *modules = [jsonDictionary dict:KeyModuleMap]; + if (loadObject.modulesJSON && modules) { + NSMutableDictionary *mutableModules = [NSMutableDictionary dictionaryWithDictionary:loadObject.modulesJSON]; + [mutableModules addEntriesFromDictionary:modules]; + loadObject.modulesJSON = mutableModules; + } else if (!loadObject.modulesJSON && modules) { + loadObject.modulesJSON = modules; + } + + // Stores any system parameters + NSDictionary *systemParameters = [jsonDictionary dict:KeySystemParameters]; + loadObject.systemParametersJSON = systemParameters.count > 0 ? systemParameters : nil; + + // module items are cached. + MVMCoreErrorObject *moduleCachingError = nil; + BOOL shouldContinue = [MVMCoreLoadRequestOperation cacheModules:modules loadObject:loadObject error:&moduleCachingError]; + [MVMCoreLoadRequestOperation handleShouldContinue:shouldContinue error:moduleCachingError loadObject:loadObject errorBlock:^{ + + // Notify of system parameters if error. + [MVMCoreLoadRequestOperation notifyListenersOfNewResponse:nil modules:nil systemParameters:systemParameters]; + } continueBlock:^{ + + // Handle any module caching error and continue load. + if (moduleCachingError) { + [MVMCoreLoadRequestOperation handleError:moduleCachingError loadObject:loadObject showAlertForErrorIfApplicable:YES]; + } + + // additional pages are cached. + MVMCoreErrorObject *pagesCachingError = nil; + NSDictionary *pages = [jsonDictionary dict:KeyPageMap]; + BOOL shouldContinue = [MVMCoreLoadRequestOperation cachePages:pages loadObject:loadObject error:&pagesCachingError]; + [MVMCoreLoadRequestOperation handleShouldContinue:shouldContinue error:pagesCachingError loadObject:loadObject errorBlock:^{ + + // Notify of module updates if we are erroring out. + [MVMCoreLoadRequestOperation notifyListenersOfNewResponse:nil modules:modules systemParameters:systemParameters]; + } continueBlock:^{ + + // Handle any page caching error and continue load. + if (pagesCachingError) { + [MVMCoreLoadRequestOperation handleError:pagesCachingError loadObject:loadObject showAlertForErrorIfApplicable:YES]; + } + + // Checks for errors. + [MVMCoreLoadRequestOperation checkForErrorsInJSONForLoadObject:loadObject completionHandler:^(BOOL shouldContinueLoad, MVMCoreLoadObject * _Nullable loadObject, MVMCoreErrorObject * _Nullable error) { + [MVMCoreLoadRequestOperation handleShouldContinue:shouldContinueLoad error:error loadObject:loadObject errorBlock:^{ + + // Notify of module/pages updates if we are erroring out. + [MVMCoreLoadRequestOperation notifyListenersOfNewResponse:pages modules:modules systemParameters:systemParameters]; + } continueBlock:^{ + if (error) { + // Hold onto any error until we establish a delegate + [MVMCoreLoadRequestOperation handleError:error loadObject:loadObject showAlertForErrorIfApplicable:NO]; + loadObject.operation.alertToShow = YES; + loadObject.operation.errorForAlertToShow = error; + } else { + // Hold onto any success messages the server may have sent. + NSString *messageStyle = [loadObject.responseInfoMap string:KeyMessageStyle]; + if (messageStyle && (!loadObject.operation.backgroundLoad || loadObject.requestParameters.allowAlertsIfBackgroundRequest)) { + loadObject.operation.alertToShow = YES; + } + } + + if (loadObject.pageDataFromCache) { + + // Notify listeners of module/pages loaded. + [MVMCoreLoadRequestOperation notifyListenersOfNewResponse:pages modules:modules systemParameters:systemParameters]; + + // Just continue if coming from cache. + completionHandler(loadObject,error); + } 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. + [[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]; + } else { + + NSDictionary *pagesLoaded = pages; + if (loadObject.pageType) { + + // We have a new page, cache the data and then continue loading. + [[MVMCoreCache sharedCache] addPageToCache:loadObject.pageJSON pageType:loadObject.pageType queue:nil waitUntilFinished:YES completionBlock:NULL]; + + // Add this page to the pages loaded and notify. + NSMutableDictionary *mutablePages = [NSMutableDictionary dictionaryWithObject:loadObject.pageJSON forKey:loadObject.pageType]; + if (pages) { + [mutablePages addEntriesFromDictionary:pages]; + } + pagesLoaded = mutablePages; + } + + // Notify listeners of things loaded. + [MVMCoreLoadRequestOperation notifyListenersOfNewResponse:pagesLoaded modules:modules systemParameters:systemParameters]; + + completionHandler(loadObject,error); + } + }]; + }]; + }]; + }]; +} + ++ (void)handleLoadObject:(nonnull MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + if (loadObject.operation.backgroundLoad || (!error.errorScreenError && loadObject.requestParameters.noViewControllerToLoad) || (!error.nativeDrivenErrorScreen && loadObject.pageType.length == 0)) { + + // No screen to load, finish the load. + [MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:nil errorObject:error]; + } else { + + // We have a screen to show. + void (^completionHandler)(UIViewController * _Nullable, MVMCoreLoadObject * _Nonnull) = ^(UIViewController * _Nullable viewController, MVMCoreLoadObject * _Nonnull loadObject) { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + if (viewController) { + + // Display or finish + if (error.errorScreenError || !loadObject.requestParameters.dontDisplayViewController) { + [MVMCoreLoadRequestOperation displayViewController:viewController loadObject:loadObject error:error]; + } else { + [MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:viewController errorObject:error]; + } + } else if (!loadObject.requestParameters.shouldNotGoToServerOnCacheFail && (loadObject.pageDataFromCache || loadObject.moduleDataFromCache)) { + + // Loading from the cache failed, remove from the cache and try the load again using the server. + [[MVMCoreCache sharedCache] removeJSONForPageType:loadObject.pageType queue:nil waitUntilFinished:YES completionBlock:NULL]; + [[MVMCoreCache sharedCache] removeJSONForModules:loadObject.requestParameters.modules queue:nil waitUntilFinished:YES completionBlock:NULL]; + loadObject.operation.alertToShow = NO; + loadObject.operation.errorForAlertToShow = nil; + [loadObject.operation main]; + } else { + + // Otherwise the request is finished. + [MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:nil errorObject:error]; + } + }; + + if (!error.nativeDrivenErrorScreen) { + + // Server driven screen, create normally + [MVMCoreLoadRequestOperation createViewControllerWithLoadObject:loadObject completionHandler:completionHandler]; + } else { + // Get the proper native error screen from the delegate + [MVMCoreDispatchUtility performBlockOnMainThread:^{ + UIViewController *nativeErrorViewController = [[MVMCoreObject sharedInstance].globalLoadDelegate getNativeScreenForRequestError:error requestObject:loadObject.requestParameters]; + [MVMCoreDispatchUtility performBlockInBackground:^{ + completionHandler(nativeErrorViewController,loadObject); + }]; + }]; + } + } +} + ++ (void)checkForErrorsInJSONForLoadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(BOOL shouldContinueLoad, MVMCoreLoadObject * _Nullable loadObject, MVMCoreErrorObject * _Nullable error))completionHandler { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + NSString *type = [loadObject.responseInfoMap string:KeyType]; + if ([ValueTypeSuccess isEqualToString:type]) { + // Success, no error. + completionHandler(YES,loadObject,nil); + } else if ([ValueTypeRedirect isEqualToString:type]) { + + // Server is telling us to redirect. + completionHandler(NO, loadObject, nil); + [[MVMCoreSessionObject sharedGlobal] redirectWithInfo:[loadObject.pageJSON dictWithChainOfKeysOrIndexes:@[KeyButtonMap,KeyMVMRC]]]; + } else if ([ValueTypeErrorScreen isEqualToString:type]) { + + // Error Screen, abort the load and handle the screen if necessary + MVMCoreErrorObject *error = [MVMCoreErrorObject createErrorObjectForErrorInfo:loadObject.responseInfoMap location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]; + [MVMCoreLoadRequestOperation loadAbortedWithError:error loadObject:loadObject]; + } else { + + void (^continueCheckingErrors)(BOOL shouldContinueLoad, MVMCoreErrorObject * _Nullable error) = ^(BOOL shouldContinueLoad, MVMCoreErrorObject * _Nullable error) { + if (!shouldContinueLoad || error) { + // Call completion if provided error or we should not continue load + completionHandler(shouldContinueLoad, loadObject, error); + } else { + // Check for controller specific errors. + BOOL shouldContinue; + MVMCoreErrorObject *error = [MVMCoreErrorObject createErrorObjectForErrorInfo:loadObject.responseInfoMap location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]; + if ([loadObject.delegate respondsToSelector:@selector(checkForDelegateSpecificErrors:loadObject:completionHandler:)]) { + shouldContinue = [loadObject.delegate checkForDelegateSpecificErrors:error loadObject:loadObject completionHandler:completionHandler]; + } else { + shouldContinue = [MVMCoreLoadHandler defaultCheckForSpecificErrors:error loadObject:loadObject]; + } + completionHandler(shouldContinue, loadObject, error); + } + }; + + if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(handleGlobalErrorsForLoadObject:completionHandler:)]) { + // Handle any general cases with the delegate. + [[MVMCoreObject sharedInstance].globalLoadDelegate handleGlobalErrorsForLoadObject:loadObject completionHandler:continueCheckingErrors]; + } else { + continueCheckingErrors(YES,nil); + } + } +} + ++ (BOOL)cacheModules:(nullable NSDictionary *)modules loadObject:(nonnull MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error { + + __block BOOL shouldContinue = YES; + + // Enumerates the modules and caches each dictionary that is cacheable. + __block MVMCoreErrorObject *errorObject = nil; + [modules enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + + if ([loadObject.operation checkAndHandleForCancellation] && stop) { + *stop = YES; + } + + if (obj && [obj isKindOfClass:[NSDictionary class]]) { + + NSDictionary *responseInfo = [obj dict:KeyResponseInfo]; + 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 = [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeJSONNotDictionary domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]; + } + + if (errorObject) { + + // Check with the delegate if we should continue in the case of the module error. + if ([loadObject.delegate respondsToSelector:@selector(handleModuleError:loadObject:error:)]) { + shouldContinue = [loadObject.delegate handleModuleError:key loadObject:loadObject error:errorObject]; + } else { + shouldContinue = [MVMCoreLoadHandler defaultHandleModuleError:KeyCode loadObject:loadObject error:errorObject]; + } + + if (!shouldContinue && stop) { + *stop = YES; + } + } + }]; + + if (error && errorObject) { + *error = errorObject; + } + return shouldContinue; +} + + ++ (BOOL)cachePages:(nullable NSDictionary *)pages loadObject:(nonnull MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error { + + __block BOOL shouldContinue = YES; + + // Enumerates the pages and caches each dictionary that is cacheable. + __block MVMCoreErrorObject *errorObject = nil; + [pages enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + + if ([loadObject.operation checkAndHandleForCancellation] && stop) { + *stop = YES; + } + + if (obj && [obj isKindOfClass:[NSDictionary class]]) { + + NSString *pageType = [obj string:KeyPageType]; + if (pageType) { + [[MVMCoreCache sharedCache] addPageToCache:obj pageType:pageType queue:nil waitUntilFinished:YES completionBlock:NULL]; + } else { + errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeNoPageType domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]; + } + } else { + errorObject = [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeJSONNotDictionary domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]; + } + + // Logs the error. + if (errorObject) { + [MVMCoreLoggingHandler addErrorToLog:errorObject]; + } + }]; + + return shouldContinue; +} + ++ (BOOL)verifyRequiredModulesLoaded:(nonnull UIViewController *)viewController loadObject:(nullable MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error { + + // Check if all needed modules are loaded. + __block NSMutableArray *modulesRequired = [NSMutableArray arrayWithArray:[[MVMCoreViewControllerMappingObject sharedViewControllerMappingObject] modulesRequiredForPageType:loadObject.pageType]]; + if (modulesRequired.count > 0) { + + [[loadObject.modulesJSON allKeys] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + + if (modulesRequired.count == 0) { + *stop = YES; + } else { + NSUInteger index = [modulesRequired indexOfObject:obj]; + if (index != NSNotFound) { + [modulesRequired removeObjectAtIndex:index]; + } + } + }]; + + if (modulesRequired.count == 0) { + return YES; + } else { + + // Error, not all needed modules are loaded. + if (error) { + *error = [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] messageToLog:[modulesRequired description] code:ErrorCodeRequiredModuleNotPresent domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]]; + } + return NO; + } + } else { + return YES; + } +} + ++ (void)createViewControllerWithLoadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(UIViewController * _Nullable viewController, MVMCoreLoadObject *_Nonnull loadObject))completionHandler { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + // Check if we can load an already created screen. + UIViewController *viewController = nil; + if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(getCachedViewControllerForLoadObject:)]) { + viewController = [[MVMCoreObject sharedInstance].globalLoadDelegate getCachedViewControllerForLoadObject:loadObject]; + } + if (viewController) { + completionHandler(viewController,loadObject); + } else { + + // Initialize the view controller using the pageType-ViewController mapping. (on the main thread) + [MVMCoreDispatchUtility performBlockOnMainThread:^{ + + __block MVMCoreErrorObject *error = nil; + __block UIViewController *viewController = [[MVMCoreViewControllerMappingObject sharedViewControllerMappingObject] createMFViewControllerOfPageType:loadObject.pageType error:&error]; + [MVMCoreDispatchUtility performBlockInBackground:^{ + + BOOL shouldContinue = NO; + if (viewController) { + + // Verifies all modules needed are loaded. + shouldContinue = [MVMCoreLoadRequestOperation verifyRequiredModulesLoaded:viewController loadObject:loadObject error:&error]; + if (!shouldContinue) { + viewController = nil; + } else { + + // Allows the view controller to handle specific errors (such as if certain data that it needs isn't loaded). + shouldContinue = [viewController shouldFinishProcessingLoad:loadObject error:&error]; + if (!shouldContinue) { + viewController = nil; + } + } + } else if (error) { + + // Sets the location for the error. + error.location = [[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]; + } else { + // Couldn't initialize view controller, serious error. + 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:^{ + completionHandler(viewController,loadObject); + }]; + }]; + }]; + } +} + ++ (void)displayViewController:(nonnull UIViewController *)viewController loadObject:(nullable MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + // Once displyed, we are finished. + void (^completionBlock)(void) = ^{ + [MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:viewController errorObject:error]; + }; + + // Lets the server determine the presentation style if not explicitely set by developer. + if (loadObject.requestParameters.loadStyle == MFLoadStyleDefault) { + + // Sets it first based on the action map. + NSString *presentationStyle = [loadObject.requestParameters.actionMap stringForKey:KeyPresentationStyle]; + [loadObject.requestParameters setMFLoadStyleBasedOnPresentationStyle:presentationStyle]; + + // Let's the response override if needed. + presentationStyle = [loadObject.pageJSON stringForKey:KeyPresentationStyle]; + [loadObject.requestParameters setMFLoadStyleBasedOnPresentationStyle:presentationStyle]; + } + + // Check if we need to load on another control + __block UIViewController *viewControllerToLoad = nil; + if ([[MVMCoreObject sharedInstance].globalLoadDelegate respondsToSelector:@selector(getViewManagerWithViewController:loadObject:)]) { + [MVMCoreDispatchUtility performSyncBlockOnMainThread:^{ + viewControllerToLoad = [[MVMCoreObject sharedInstance].globalLoadDelegate getViewManagerWithViewController:viewController loadObject:loadObject]; + }]; + } + + // Navigates + [[MVMCoreNavigationHandler sharedNavigationHandler] navigateWithLoadObject:loadObject viewController:(viewControllerToLoad ?: viewController) delegate:loadObject.operation completionHandler:completionBlock]; +} + ++ (void)handleError:(nonnull MVMCoreErrorObject *)error loadObject:(nonnull MVMCoreLoadObject *)loadObject showAlertForErrorIfApplicable:(BOOL)showAlertForErrorIfApplicable { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + // 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 actionDelegate:loadObject.operation.delegate]; + } +} + ++ (void)createAndShowAlertForLoadObject:(nullable MVMCoreLoadObject *)loadObject error:(nullable MVMCoreErrorObject *)error actionDelegate:(nonnull id)actionDelegate { + + // Check delegate for alert object to show. + MVMCoreAlertObject *alertObject; + if ([actionDelegate respondsToSelector:@selector(alertObjectToShow:error:)]) { + alertObject = [actionDelegate alertObjectToShow:loadObject error:error]; + } else { + alertObject = [MVMCoreAlertObject alertObjectForLoadObject:loadObject error:error actionDelegate:actionDelegate]; + } + + // Set how we handle text field errors. + [alertObject setTextFieldErrorHandler:^(NSArray * _Nonnull fieldErrors) { + if ([loadObject.delegate respondsToSelector:@selector(handleFieldErrors:loadObject:)]) { + [actionDelegate handleFieldErrors:fieldErrors loadObject:loadObject]; + } + }]; + + [alertObject showAlert]; +} + ++ (void)loadAbortedWithError:(nonnull MVMCoreErrorObject *)error loadObject:(nonnull MVMCoreLoadObject *)loadObject { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + // Native driven error screens still need to be loaded. (as long as the delegate desires) + if (!loadObject.requestParameters.handleErrorsSilently && !error.silentError && error.errorScreenError && (![loadObject.delegate respondsToSelector:@selector(shouldContinueToErrorPage:error:)] || [loadObject.delegate shouldContinueToErrorPage:loadObject error:error])) { + [MVMCoreLoadRequestOperation handleError:error loadObject:loadObject showAlertForErrorIfApplicable:YES]; + [MVMCoreLoadRequestOperation handleLoadObject:loadObject error:error]; + } else { + [MVMCoreLoadRequestOperation handleError:error loadObject:loadObject showAlertForErrorIfApplicable:YES]; + [MVMCoreLoadRequestOperation loadFinished:loadObject loadedViewController:nil errorObject:error]; + } +} + ++ (void)loadFinished:(nonnull MVMCoreLoadObject *)loadObject loadedViewController:(nullable UIViewController *)loadedViewController errorObject:(nullable MVMCoreErrorObject *)errorObject { + + if ([loadObject.operation checkAndHandleForCancellation]) { + return; + } + + // Show any alerts that we have been saving. + if (loadObject.operation.alertToShow) { + + id actionDelegate = loadedViewController ?: loadObject.operation.delegate; + [MVMCoreLoadRequestOperation createAndShowAlertForLoadObject:loadObject error:loadObject.operation.errorForAlertToShow actionDelegate:actionDelegate]; + } + + // Notify delegate that the load has finished + if ([loadObject.delegate respondsToSelector:@selector(loadFinished:loadedViewController:error:)]) { + [loadObject.delegate loadFinished:loadObject loadedViewController:loadedViewController error:errorObject]; + } else { + [MVMCoreLoadHandler defaultLoadFinished:loadObject loadedViewController:loadedViewController error:errorObject]; + } + [loadObject.operation markAsFinished]; +} + +#pragma mark - Presentation Delegate + +- (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.delegate respondsToSelector:@selector(navigationController:willDisplayViewController:)]) { + [self.delegate navigationController:navigationController willDisplayViewController:viewController]; + } +} + +- (void)navigationController:(UINavigationController *)navigationController displayedViewController:(UIViewController *)viewController { + + if ([self.delegate respondsToSelector:@selector(navigationController:displayedViewController:)]) { + [self.delegate navigationController:navigationController displayedViewController:viewController]; + } +} + +- (nullable id )navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { + if (self.delegate && [self.delegate respondsToSelector:@selector(navigationController:animationControllerForOperation:fromViewController:toViewController:)]) { + + return [self.delegate navigationController:navigationController animationControllerForOperation:operation fromViewController:fromVC toViewController:toVC]; + } else { + return nil; + } +} + +- (void)viewController:(UIViewController *)presentingViewController willPresentViewController:(UIViewController *)presentedViewController { + + if ([self.delegate respondsToSelector:@selector(viewController:willPresentViewController:)]) { + [self.delegate viewController:presentingViewController willPresentViewController:presentedViewController]; + } +} + +- (void)viewController:(UIViewController *)presentingViewController didPresentViewController:(UIViewController *)presentedViewController { + if ([self.delegate respondsToSelector:@selector(viewController:didPresentViewController:)]) { + [self.delegate viewController:presentingViewController didPresentViewController:presentedViewController]; + } +} + + +@end diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreRequestParameters.h b/MVMCore/MVMCore/LoadHandling/MVMCoreRequestParameters.h new file mode 100644 index 0000000..157ab5d --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreRequestParameters.h @@ -0,0 +1,113 @@ +// +// MVMCoreRequestParameters.h +// myverizon +// +// Created by Scott Pfeil on 3/4/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// +// An object for the request parameters + +#import +#import + +// The loading style. +// MFLoadStyleDefault: This means it has not been explicitely set by the developer. Standard push. +// MFLoadStylePush: Set explicitely to push. +// MFLoadStyleReplaceCurrent: Replaces the current view controller if possible. +// MFLoadStyleOnTopOfRoot: Loads ontop of the root controller. +// MFLoadStyleBecomeRoot: Load as the root controller. +typedef NS_ENUM(NSInteger, MFLoadStyle) { + MFLoadStyleDefault = 0, + MFLoadStylePush, + MFLoadStyleReplaceCurrent, + MFLoadStyleOnTopOfRoot, + MFLoadStyleBecomeRoot, + MFLoadStylePresent +}; + +@interface MVMCoreRequestParameters : NSObject + +// request parameters +@property (nullable, strong, nonatomic) NSString *pageType; +@property (nullable, strong, nonatomic) NSArray *modules; +@property (nullable, strong, nonatomic) NSDictionary *parameters; + +// adding parent pageType for freebee +@property (nullable, strong, nonatomic) NSString *parentPageType; + +// Context root for server +@property (nullable, strong, nonatomic) NSString *contextRoot; + +// A flag for if you do not want to try loading any actual view controller. (Unless there is an error screen) +@property (assign, nonatomic) BOOL noViewControllerToLoad; + +// A flag for if you want a view controller to be created but not displayed. (Unless there is an error screen) +@property (assign, nonatomic) BOOL dontDisplayViewController; + +// A flag for if you don't want loading overlay to display. +@property (nonatomic) BOOL noloadingOverlay; + +// A flag for if we should hide any errors from the user or not. +@property (assign, nonatomic) BOOL handleErrorsSilently; + +// A flag for if you want to override pushing view animated or not. +@property (assign, nonatomic) BOOL shouldNotAnimatePush; + +// A flag for if we want to override making a request to server if fail to load from cache +@property (assign, nonatomic) BOOL shouldNotGoToServerOnCacheFail; + +// A flag for if we never want to load from the cache +@property (assign, nonatomic) BOOL neverLoadFromCache; + +// A flag for if we should be adding the initial request parameters. +@property (assign, nonatomic) BOOL addInitialRequestParameters; + +// A flag for if we should always show alerts, even during a background load. +@property (assign) BOOL allowAlertsIfBackgroundRequest; + +// Determines how it is loaded. +@property (nonatomic) MFLoadStyle loadStyle; +@property (nonatomic) BOOL replaceViewIfOnStackElseLoadWithStyle; + +// 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; + +// Can provide a specific navigation controller to use for loading. Will use the default other wise. +@property (nullable, weak, nonatomic) UINavigationController *navigationController; + +// If the request was created with an action map. +@property (nullable, strong, nonatomic) NSDictionary *actionMap; + +// only used when the base url is not the same as mf +@property (nullable, strong, nonatomic) NSURL *alternateBaseURL; + +@property (nullable, strong, nonatomic) NSNumber *customTimeoutTime; + +// Will open support panel at the end of the load. +@property (nonatomic) BOOL openSupportPanel; + +// A list of page types that this operation should NOT be loaded in parallel with. +@property (nonatomic) NSArray* _Nullable successivePageTypes; + +// 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; + +// Creates an object with the given page type, modules, and extra parameters. Adds the extra parameters to the standard request parameters. +- (nullable instancetype)initWithPageType:(nonnull NSString *)pageType additionalModules:(nonnull NSArray *)additionalModules extraParameters:(nullable NSDictionary *)extraParameters; + +// Creates an object with the given modules. Adds the extra parameters to the standard request parameters. +- (nullable instancetype)initWithModules:(nonnull NSArray *)modules extraParameters:(nullable NSDictionary *)extraParameters; + +// Initializes the object with an action map. +- (nullable instancetype)initWithActionMap:(nonnull NSDictionary *)actionMap; + +// Adds the passed in parameters. +- (void)addRequestParameters:(nonnull NSDictionary *)parameters; + +// Removes the passed in parameters. +- (void)removeRequestParameterWithKey:(nonnull NSString *)key; + +// Sets the load style based on the server sent presentationStyle +- (void)setMFLoadStyleBasedOnPresentationStyle:(nonnull NSString *)presentationStyle; + +@end diff --git a/MVMCore/MVMCore/LoadHandling/MVMCoreRequestParameters.m b/MVMCore/MVMCore/LoadHandling/MVMCoreRequestParameters.m new file mode 100644 index 0000000..b0d4bfe --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/MVMCoreRequestParameters.m @@ -0,0 +1,136 @@ +// +// MVMCoreRequestParameters.m +// myverizon +// +// Created by Scott Pfeil on 3/4/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreRequestParameters.h" +#import "NSDictionary+MFConvenience.h" +#import "MVMCoreJSONConstants.h" +#import "MVMCoreViewControllerMappingObject.h" + +@interface MVMCoreRequestParameters () + +- (nullable instancetype)initWithExtraParameters:(nullable NSDictionary *)extraParameters; + +@end + +@implementation MVMCoreRequestParameters + +- (nullable instancetype)initWithExtraParameters:(nullable NSDictionary *)extraParameters { + if (self = [super init]) { + self.parameters = extraParameters; + + // Default load style. + self.loadStyle = MFLoadStyleDefault; + self.replaceViewIfOnStackElseLoadWithStyle = YES; + } + return self; +} + +- (nullable instancetype)initWithPageType:(nonnull NSString *)pageType extraParameters:(nullable NSDictionary *)extraParameters { + if (self = [self initWithExtraParameters:extraParameters]) { + self.pageType = pageType; + self.modules = [[MVMCoreViewControllerMappingObject sharedViewControllerMappingObject] allModulesForPageType:pageType]; + } + return self; +} + +- (nullable instancetype)initWithPageType:(nonnull NSString *)pageType additionalModules:(nonnull NSArray *)additionalModules extraParameters:(nullable NSDictionary *)extraParameters { + if (self = [self initWithPageType:pageType extraParameters:extraParameters]) { + + if (additionalModules.count > 0) { + + if (self.modules) { + NSMutableArray *modules = [NSMutableArray arrayWithArray:self.modules]; + [modules addObjectsFromArray:additionalModules]; + self.modules = [NSArray arrayWithArray:modules]; + } else { + self.modules = additionalModules; + } + } + } + return self; +} + +- (nullable instancetype)initWithModules:(nonnull NSArray *)modules extraParameters:(nullable NSDictionary *)extraParameters { + if (self = [self initWithExtraParameters:extraParameters]) { + self.modules = modules; + } + return self; +} + +- (nullable instancetype)initWithActionMap:(nonnull NSDictionary *)actionMap { + + if (self = [self initWithPageType:[actionMap stringForKey:KeyPageType] additionalModules:[actionMap array:KeyModuleList] extraParameters:[actionMap dict:KeyExtraParameters]]) { + self.contextRoot = [actionMap string:KeyContextRoot]; + self.actionMap = actionMap; + self.openSupportPanel = [actionMap boolForKey:KeyOpenSupport]; + + // Right now server is sending default.... can't uncomment this until they remove default + //self.replaceViewIfOnStackElseLoadWithStyle = [actionMap boolForKey:@"tryToReplaceFirst"]; + } + return self; +} + +- (void)addRequestParameters:(nonnull NSDictionary *)parameters { + + if ([parameters count] > 0) { + NSMutableDictionary *currentParameters = [NSMutableDictionary dictionaryWithDictionary:self.parameters]; + [currentParameters addEntriesFromDictionary:parameters]; + self.parameters = [NSDictionary dictionaryWithDictionary:currentParameters]; + } +} + +- (void)removeRequestParameterWithKey:(nonnull NSString *)key { + + NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithDictionary:self.parameters]; + [dictionary removeObjectForKey:key]; + self.parameters = [NSDictionary dictionaryWithDictionary:dictionary]; +} + +- (void)setMFLoadStyleBasedOnPresentationStyle:(nonnull NSString *)presentationStyle { + + if ([ValuePresentationStyleModal isEqualToString:presentationStyle]) { + + // Server says to load as modal + self.loadStyle = MFLoadStylePresent; + } else if ([ValuePresentationStyleRoot isEqualToString:presentationStyle]) { + self.loadStyle = MFLoadStyleBecomeRoot; + } else if ([ValuePresentationStyleReplace isEqualToString:presentationStyle]) { + self.loadStyle = MFLoadStyleReplaceCurrent; + } else if ([ValuePresentationStylePush isEqualToString:presentationStyle]) { + self.loadStyle = MFLoadStylePush; + } +} + +- (id)copyWithZone:(nullable NSZone *)zone { + + MVMCoreRequestParameters *copyObject = [[MVMCoreRequestParameters alloc] init]; + copyObject.pageType = [self.pageType copy]; + copyObject.parentPageType = self.parentPageType; + + copyObject.modules = [self.modules copy]; + copyObject.parameters = [self.parameters copy]; + copyObject.contextRoot = [self.contextRoot copy]; + copyObject.loadStyle = self.loadStyle; + copyObject.replaceViewIfOnStackElseLoadWithStyle = self.replaceViewIfOnStackElseLoadWithStyle; + copyObject.noViewControllerToLoad = self.noViewControllerToLoad; + copyObject.dontDisplayViewController = self.dontDisplayViewController; + copyObject.noloadingOverlay = self.noloadingOverlay; + copyObject.handleErrorsSilently = self.handleErrorsSilently; + copyObject.shouldNotAnimatePush = self.shouldNotAnimatePush; + copyObject.shouldNotGoToServerOnCacheFail = self.shouldNotGoToServerOnCacheFail; + copyObject.neverLoadFromCache = self.neverLoadFromCache; + copyObject.addInitialRequestParameters = self.addInitialRequestParameters; + copyObject.tabWasPressed = self.tabWasPressed; + copyObject.navigationController = self.navigationController; + copyObject.allowAlertsIfBackgroundRequest = self.allowAlertsIfBackgroundRequest; + copyObject.alternateBaseURL = self.alternateBaseURL; + copyObject.openSupportPanel = self.openSupportPanel; + return copyObject; +} + +@end diff --git a/MVMCore/MVMCore/LoadHandling/SSLPinning/Certificates/DigiCertGlobalRootCA.cer b/MVMCore/MVMCore/LoadHandling/SSLPinning/Certificates/DigiCertGlobalRootCA.cer new file mode 100644 index 0000000000000000000000000000000000000000..2f1e5523af47d44e35ea27be89e25a663ea7f99f GIT binary patch literal 947 zcmXqLVqS01#58>YGZP~dlK_YHgRlusZW|YtW}S?jc-+f?myJ`a&7D}zCz zA-4f18*?ZNn=n&ou%W1dFo?q?%;S=op6Q%gRHERSmux6$APy4d78WQkFV{-}N+koO z^pf*)4HXRJKvK-Y;&2`AIr&M6ISN7f`6UX@js|k#yar~5hK2?Z5GBrQ4CKMN^l(BG zqY|>m8Ce;an;7{S44N3Zn3@dd`IJs!)JE3#aq8T|A z&M;11?q{$)_`CCwOos5ek9w8vwzD}fG;*FhC*8dN{#olnw@25~jV;t*_#-WJ|60ik06Z{$usNcN*7A>Z>YdMz&_;#V8wj@Ao_KA}aTB!TxfA z{eKw0wccFeA`oG;vQ*;g#YOwvAE{@3nPvRLlOv(~O8ge9mi-G?dHK}Zo%fH=DT`a+ zZ9Z|{9i?Zv=R~CXvQGayyICYf{CKqHJfr=e-u=4F?Q~`Jc1NRIJXhZTXuP_+H^P3a z^b1doMFqDES?+${B=*UN%a47$hjQ?3! zfC;G0fFHyc2Ju-9n1PgmEJ%QlMT|v+`Cfo&*hQb+X&)9pl9oO^eUt6QPy>08v@(l? zfmj1}1*qu{7!8aJr{|cj@(>o{`gPZ3j(^aGFZb86e66hHSga!P$6LGQfbPz#$Nzmf zC?9cpNA8@elIJrtR|{@^T(Dr_S)Y!XlY1r>JX*~!vv2as|H)UMuqRwzynk*q=f|sk zkAuJTCI7NtetS#W{iL*O7xNr@Kk06LRqi!^Zp0()$O8__Z*?`AT67HFks3>ujWo?Cxj#WeU z^4IG{MPAb-4oIsket75QefOHJTU0n$4c_SP<=*eQKVav| c&#z}N3avfSacqW3d@q;Hv6ok;PR+ds0Q5RD}zDf zDMM}pPB!LH7B*p~&|pJx15pr%OPDV#wJ0+ZAHEh*10$~Lqzuz)Bq)JWD;FfuSOQ-G<27@?r6;Fe#ckXTxhkzbTqm71bZTAZqo zpO;f<=xX2$(#tGt1Jk15oRe5wtYEAVP@0sJnXC{{l$o1YRH@*cT2zvmmYJMbl9`{U z;0Q6Rq!MVhyQzVkIIn@3p`n3=0T@Jy^BS8Om>ZfI8Jk*~T1E{o&o(hCAx9=7D+6;A zBR_*d6C)Q>6C)$TdKC^1%_`G*2|TNwdt3Y6so7)WDzjVr!9Pu=pWz&fQ(8sUzkXbM z!mu+|>4ny+Wlo#>f9wq1zHz(v=70Tl?BQ?TJKs)A5=}gxz`Nwey&n$_Cvn(@2*36` z6&cd1_EfuHdj9LL3nv~j_PpVooE7AKssBYmpzp=b%-!|V7D!E-9>m7D^+J4dU zg&V#sQvI}EW>UJu(=Ti+?OuN3=ynE37+PwSCm+Najefwa${%+|+ zV@X*Vhtnl0l8TdWiu_xn6?DA+DvRfn-emUZfO>87Laa8?dvn zYV$EONwJ9ZKfNr~wx+i)`|$aOtScT8I@OYT2C8Dp86_nJR{Hum`RVz3WxzaC49r7% z$@#hZWyN4IkV&AdB+JJl#v)RGId%D=yKi=XFyQ>`Z2606y|JM&aE$6CEUW{y6yd{%yxq52h)cJewkF(RgyoyG1RU z+`{>Nw|VZWGTmO%E$MQ}v5Ga*E5bkFR9#aRH`6Lram%f@>z}uzYEA9h+jcLmQLI^4 z;LN?hE1I-&HRO4@k34-Yr6&3Ci(tOM)~SD+*A#(ax4;xJwkvQ5H1Bj%HnSF1%xbNs~@I-k}E KUR&`tIST-5H^c1! literal 0 HcmV?d00001 diff --git a/MVMCore/MVMCore/LoadHandling/SSLPinning/MFSSLPinningHandler.h b/MVMCore/MVMCore/LoadHandling/SSLPinning/MFSSLPinningHandler.h new file mode 100644 index 0000000..041bdb8 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/SSLPinning/MFSSLPinningHandler.h @@ -0,0 +1,44 @@ +// +// MFSSLPinningHandler.h +// mobilefirst +// +// Created by Pfeil, Scott Robert on 8/3/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import + +@class MVMCoreErrorObject; +@class MVMCoreRequestParameters; +@class MFURLSessionChallengeResult; + +@interface MFSSLPinningHandler : NSObject + +#pragma mark - SSL Data + +// To setup SSL pinning status. ++ (void)setupSSLData:(NSString *_Nullable)status; + +#pragma mark NSURLSessionDelegate methods + +// Handles like the delegate function for the URLSession ++ (MFURLSessionChallengeResult * _Nonnull)getResultForURLSession:(NSURLSession *_Nonnull)session didReceiveChallenge:(NSURLAuthenticationChallenge *_Nonnull)challenge; + +#pragma mark - Error Handling + +// Creates and returns SSL error object for the NSError. ++ (nonnull MVMCoreErrorObject *)errorObjectForSSLErrors:(nullable NSString *)locationForError; + +// Creates and returns either an SSL error object or normal error object for the NSError. ++ (nonnull MVMCoreErrorObject *)createErrorObjectForNSErrorAndVerifySSLErrors:(nonnull NSError *)error location:(nullable NSString *)locationForError; + +// To invoke SSL Error Handling flow, when error code:NSURLErrorCancelled is thrown while connecting to server API's for each session. +// it is used for redirection flows ++ (void)handleSSLErrors:(nullable MVMCoreErrorObject *)errorObject requestParameters:(nonnull MVMCoreRequestParameters *)requestParameters; + +#pragma mark - SSL Logging + +// To log SSL error on MF Server ++ (void)logSSLError:(NSString *_Nonnull)tag; + +@end diff --git a/MVMCore/MVMCore/LoadHandling/SSLPinning/MFSSLPinningHandler.m b/MVMCore/MVMCore/LoadHandling/SSLPinning/MFSSLPinningHandler.m new file mode 100644 index 0000000..8b40a39 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/SSLPinning/MFSSLPinningHandler.m @@ -0,0 +1,585 @@ +// +// MFSSLPinningHandler.m +// mobilefirst +// +// To handle SSL pinning: +// It will be pinning client and server root certificate/public key to secure app +// +// Created by Pfeil, Scott Robert on 8/3/17. +// Updated by Khan, Arshad on 8/23/17 +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import "MFSSLPinningHandler.h" +#import "MVMCoreErrorObject.h" +#import "MVMCoreRequestParameters.h" +#import "MVMCoreNavigationHandler.h" +#import "MVMCoreSessionObject.h" +#import "MVMCoreLoadHandler.h" +#import "MVMCoreLoggingHandler.h" +#import "MFURLSessionChallengeResult.h" +#import +#import "MVMCoreJSONConstants.h" +#import "MVMCoreActionUtility.h" +#import "MVMCoreGetterUtility.h" +#import "MVMCoreLoadObject.h" +#import "MVMCoreErrorConstants.h" +#import "MVMCoreHardcodedStringsConstants.h" +#import "MVMCoreObject.h" + +NSString * const MF_IOS_SSL_CERT_PATH_NOT_FOUND = @"MF_IOS_SSL_CERT_PATH_NOT_FOUND"; +NSString * const MF_IOS_SSL_CERT_PINNED = @"MF_IOS_SSL_CERT_PINNED"; +NSString * const MF_IOS_SSL_PUBLIC_KEY_PINNED = @"MF_IOS_SSL_PUBLIC_KEY_PINNED"; +NSString * const MF_IOS_SSL_CERT_MISMATCH = @"MF_IOS_SSL_CERT_MISMATCH"; +NSString * const MF_IOS_SSL_SERVER_CERT_DATA_NULL = @"MF_IOS_SSL_SERVER_CERT_DATA_NULL"; +NSString * const MF_IOS_SSL_CLIENT_CERT_NULL = @"MF_IOS_SSL_CLIENT_CERT_NULL"; +NSString * const MF_IOS_SSL_PINNING_SUCCESS = @"MF_IOS_SSL_PINNING_SUCCESS"; +NSString * const MF_IOS_SSL_PINNING_FAILED = @"MF_IOS_SSL_PINNING_FAILED"; +NSString * const MF_IOS_SSL_SERVER_CERT_NULL = @"MF_IOS_SSL_SERVER_CERT_NULL"; +NSString * const MF_IOS_SSL_PINNING_SWITCHED_OFF = @"MF_IOS_SSL_PINNING_SWITCHED_OFF"; +NSString * const MF_IOS_SSL_MANDATORY_UPGRADE_PAGE = @"MF_IOS_SSL_MANDATORY_UPGRADE_PAGE"; +NSString * const MF_IOS_SSL_ERROR_PAGE = @"MF_IOS_SSL_ERROR_PAGE"; + +@interface MFSSLPinningHandler () + +// Stores the ssl dictionary in the user defaults or removes. ++ (void)setupSSLData:(NSString *_Nullable)status; + +// The challenge on the request is allowed, get the result. ++ (MFURLSessionChallengeResult *)allowChallenge:(NSURLAuthenticationChallenge *)challenge; + +// Returns the path of the certificate. ++ (NSString *)getCertificatePath:(NSString *_Nullable)commonName; + +// To check expiry of pinned certificate +// we can use SecTrustSetVerifyDate as well, if below function gives any problem in future ++ (BOOL)isCertificateExpiredAtPath:(NSString*)path; + +@end + +@implementation MFSSLPinningHandler + +#pragma mark - SSL Data setup + ++ (BOOL)shouldBeSSLPinned { + NSString *hashedSslStatus = [[NSUserDefaults standardUserDefaults] stringForKey:CLIENT_SSL_STATUS_KEY]; + if (hashedSslStatus && [hashedSslStatus isEqualToString:[MVMCoreActionUtility hashStringWithSHA256:@"true"]]) { + return YES; + } else { + return NO; + } +} + ++ (void)storeSSLStatus:(NSString *_Nullable)sslStatus { + [MVMCoreDispatchUtility performBlockOnMainThread:^{ + if (sslStatus.length > 0) { + // Explicitly turning OFF SSL Pinning + [[NSUserDefaults standardUserDefaults] setObject:[MVMCoreActionUtility hashStringWithSHA256:@"false"] forKey:CLIENT_SSL_STATUS_KEY]; + } else { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:CLIENT_SSL_STATUS_KEY]; + } + }]; +} + +// Updates the ssl pinning status. ++ (void)updateSSLData:(NSString *_Nullable)sslStatus { + if (sslStatus.length>0) { + [MFSSLPinningHandler storeSSLStatus:sslStatus]; + } +} + +// Gets the ssl pinning status (if we are using or not) ++ (void)setupSSLData:(NSString *_Nullable)status { + + NSString *sslStatus = [[NSUserDefaults standardUserDefaults] stringForKey:CLIENT_SSL_STATUS_KEY]; + if (sslStatus==nil || sslStatus.length==0) { + + // None previously stored... + // By default SSL status would be true + // can be switched off from test screen + // using 'true' string for hashing and stroing this SSL status securely + sslStatus = [MVMCoreActionUtility hashStringWithSHA256:@"false"]; + // store this status for future use + [MFSSLPinningHandler storeSSLStatus:sslStatus]; + + } else if (status) { + + // Found previously stored, update. + [MFSSLPinningHandler updateSSLData:status]; + } +} + +#pragma mark- SSL Pinning functions + +BOOL evaluateCertificate(SecTrustRef secTrust) { + SecTrustResultType trustResult; + SecTrustEvaluate(secTrust, &trustResult); + BOOL isCertificateTrusted = NO; + switch (trustResult) { + case kSecTrustResultUnspecified: + case kSecTrustResultProceed: + { + isCertificateTrusted = YES; + MVMCoreLog(@"Pass: certificate is trusted"); + } + break; + case kSecTrustResultRecoverableTrustFailure: + MVMCoreLog(@"Failure: certificate is not trusted or kSecTrustResultRecoverableTrustFailure"); + default: + MVMCoreLog(@"Failure: certificate is invalid or not trustable:%u",trustResult); + break; + } + return isCertificateTrusted; +} + +SecKeyRef getPublicKeyForCertificate(SecCertificateRef clientProdCert) { + SecKeyRef aPublicKeyRef = NULL; + if (clientProdCert != NULL) { + + SecTrustRef aTrustRef = NULL; + SecPolicyRef aPolicyRef = SecPolicyCreateBasicX509(); + + if (aPolicyRef) { + if (SecTrustCreateWithCertificates((CFTypeRef)clientProdCert, aPolicyRef, &aTrustRef) == noErr) { + SecTrustResultType result; + if (aTrustRef) { + if (SecTrustEvaluate(aTrustRef, &result) == noErr) { + aPublicKeyRef = (SecKeyRef)CFBridgingRetain(CFBridgingRelease(SecTrustCopyPublicKey(aTrustRef))); + MVMCoreLog(@"Public key fetching success."); + } else { + MVMCoreLog(@"Public key fetching failed."); + } + } + } + } + if (aPolicyRef) CFRelease(aPolicyRef); + if (aTrustRef) CFRelease(aTrustRef); + } + return aPublicKeyRef; +} + +#pragma mark- SSL Pinning methods + ++ (MFURLSessionChallengeResult *)allowChallenge:(NSURLAuthenticationChallenge *)challenge { + SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust]; + NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; + return [MFURLSessionChallengeResult createChallengeResultWithDisposition:NSURLSessionAuthChallengeUseCredential credential:credential]; +} + ++ (NSString *)getCertificatePath:(NSString *_Nullable)commonName { + NSString *resourcePath = nil; + if (commonName.length>0) { + resourcePath = [commonName stringByReplacingOccurrencesOfString:@" " withString:@""]; + MVMCoreLog(@"Server Certificate Common Name: %@",commonName); + } + // prod root certificate + NSString *pathToProdCert = nil; + if (resourcePath.length>0) { + MVMCoreLog(@"Name of Certificate Resource: %@",resourcePath); + // To fetch exact certificate stored in MF bundle + // like, if digi is root certificate from server, + // then get respective certificate from local + pathToProdCert = [[MVMCoreGetterUtility bundleForMVMCore] pathForResource:resourcePath ofType:@"cer"]; + } else { + // Default is verisign + pathToProdCert = [[MVMCoreGetterUtility bundleForMVMCore] pathForResource:@"VeriSignClass3PublicPrimaryCertificationAuthority-G5" ofType:@"cer"]; + } + MVMCoreLog(@"Client Certificate Path Prod Cert: %@",pathToProdCert); + return pathToProdCert; +} + ++ (BOOL)isCertificateExpiredAtPath:(NSString *)path { + if (path) { + + // Gets the certificate and creates an object for it. + NSData *certificateData = [[NSFileManager defaultManager] contentsAtPath:path]; + SecCertificateRef certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData); + if (certificateRef != NULL) { + + // Creates a policy. + SecTrustRef trustRef = NULL; + SecPolicyRef policyRef = SecPolicyCreateBasicX509(); + if (policyRef) { + + // Creates a trust + if (SecTrustCreateWithCertificates((CFTypeRef)certificateRef, policyRef, &trustRef) == noErr) { + + // Set SSL policies for domain name check + NSMutableArray *policies = [NSMutableArray array]; + CFDataRef clientCertDataRef = (__bridge_retained CFDataRef)certificateData; + SecCertificateRef clientProdCert = SecCertificateCreateWithData(NULL, clientCertDataRef); + + CFStringRef clientHostName = SecCertificateCopySubjectSummary(clientProdCert); + + [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, clientHostName)]; + SecTrustSetPolicies(trustRef, (__bridge CFArrayRef)policies); + + // release clientHostName, because it is no logner needed + if (clientHostName) { + CFRelease(clientHostName); + } + if (clientCertDataRef) { + CFRelease(clientCertDataRef); + } + + OSStatus status = SecTrustSetVerifyDate(trustRef, (CFDateRef)[NSDate date]); + MVMCoreLog(@"Client certificate status:%d",(int)status); + if (status == errSecSuccess) { + MVMCoreLog(@"Client Certificate is not Expired"); + return NO; + } else { + MVMCoreLog(@"Client Certificate Expired"); + return YES; + } + } + } + + if (policyRef) CFRelease(policyRef); + if (trustRef) CFRelease(trustRef); + if (certificateRef) CFRelease(certificateRef); + } + return NO; + } else { + return NO; + } +} + +#pragma mark- helpers + ++ (BOOL)checkCertificatePinning:(BOOL)certificatesPinned serverPublicKey:(SecKeyRef)serverPublicKey clientPublicKey:(SecKeyRef)clientPublicKey clientProdRootCertificateData:(NSData *_Nullable)clientProdRootCertificateData serverRootCertificateData:(NSData *_Nullable)serverRootCertificateData extraInfo:(NSMutableDictionary *_Nonnull)extraInfo { + // Check equality of root certificates + if ([serverRootCertificateData isEqualToData:clientProdRootCertificateData]) { + MVMCoreLog(@"Root Certificates are equal."); + certificatesPinned = YES; +// [MFSSLPinningHandler logSSLError:MF_IOS_SSL_CERT_PINNED]; + [MVMCoreLoggingHandler logWithDelegateWithObject:(id)self withName:MF_IOS_SSL_CERT_PINNED withExtraInfo:extraInfo]; + MVMCoreLog(@"Certificate is successfully pinned"); + } else if ( (clientPublicKey != NULL) && (serverPublicKey != NULL) && [(__bridge id)clientPublicKey isEqual:(__bridge id)serverPublicKey] ) { + // Publick key fallback flow to extend certificate pinning, even if root certificate expired. + // certificate expire fall back flow-1 + MVMCoreLog(@"public key fall back flow-1"); + certificatesPinned = YES; +// [MFSSLPinningHandler logSSLError:MF_IOS_SSL_PUBLIC_KEY_PINNED]; + [MVMCoreLoggingHandler logWithDelegateWithObject:(id)self withName:MF_IOS_SSL_PUBLIC_KEY_PINNED withExtraInfo:extraInfo]; + MVMCoreLog(@"Certificate public key is successfully pinned"); + } else { + // Genuine failure case + certificatesPinned = NO; + [MFSSLPinningHandler logSSLError:MF_IOS_SSL_CERT_MISMATCH]; + [MVMCoreLoggingHandler logWithDelegateWithObject:(id)self withName:MF_IOS_SSL_CERT_MISMATCH withExtraInfo:extraInfo]; + MVMCoreLog(@"Failure Case:Client certificates data is nil or not matching with server certificate"); + } + return certificatesPinned; +} + ++ (BOOL)processClientCertificate:(BOOL)certificatesPinned extraInfo:(NSMutableDictionary *_Nonnull)extraInfo serverRootCertificateData:(NSData *_Nullable)serverRootCertificateData clientProdRootCertificateData:(NSData *_Nullable)clientProdRootCertificateData serverRootCertificate:(SecCertificateRef)serverRootCertificate serverHostNameStr:(NSString *)serverHostNameStr { + // Check, client root certificate data should not be nil + if (clientProdRootCertificateData != nil) { + // Get client certificate ref + CFDataRef clientCertDataRef = (__bridge_retained CFDataRef)clientProdRootCertificateData; + SecCertificateRef clientProdRootCert = SecCertificateCreateWithData(NULL, clientCertDataRef); + + CFStringRef clientHostName = SecCertificateCopySubjectSummary(clientProdRootCert); + NSString *clientHostNameStr = (__bridge NSString *)clientHostName; + if (clientHostNameStr) { + [extraInfo setObject:clientHostNameStr forKey:@"clientHostNameStr"]; + MVMCoreLog(@"client certificate Distinguished common Name: %@",clientHostNameStr); + } else { + MVMCoreLog(@"Failure Case:client common name is not found."); + [extraInfo setObject:@"client common name is not found" forKey:@"clientHostNameStr"]; + } + + // To log, client and server certificate's common name mismatch case + if ([serverHostNameStr isEqualToString:clientHostNameStr]) { + MVMCoreLog(@"Server Certificate is valid"); + } else { + MVMCoreLog(@"Failure Case:certificate Common name mismatch."); + [extraInfo setObject:@"certificate Common name mismatch" forKey:@"commonNameMismatch"]; + } + + // release clientHostName, because it is no logner needed + if (clientHostName) { + CFRelease(clientHostName); + } + // release clientCertDataRef, because it is no logner needed + if (clientCertDataRef) { + CFRelease(clientCertDataRef); + } + + // Get client root certificate public key. + // To extend certificate pinning, even if root certificate expired. + SecKeyRef clientPublicKey = (__bridge_retained SecKeyRef)(CFBridgingRelease(getPublicKeyForCertificate(clientProdRootCert))); + // To log, if public key is null + if (!clientPublicKey) { + MVMCoreLog(@"Failure Case:client public key is null."); + [extraInfo setObject:@"client public key is null" forKey:@"clientPublicKey"]; + } + // Get server root certificate public key. + SecKeyRef serverPublicKey = (__bridge_retained SecKeyRef)(CFBridgingRelease(getPublicKeyForCertificate(serverRootCertificate))); + // To log, if public key is null + if (!serverPublicKey) { + MVMCoreLog(@"Failure Case:server public key is null."); + [extraInfo setObject:@"server public key is null" forKey:@"serverPublicKey"]; + } + + // Check, server root certificate data should not be nil + if (serverRootCertificateData) { + certificatesPinned = [MFSSLPinningHandler checkCertificatePinning:certificatesPinned serverPublicKey:serverPublicKey clientPublicKey:clientPublicKey clientProdRootCertificateData:clientProdRootCertificateData serverRootCertificateData:serverRootCertificateData extraInfo:extraInfo]; + } else { + // Server Certificate Data is nil, this would not happen. + // But if happens then it is client side issue. So, bypass SSL Pinning + certificatesPinned = YES; + MVMCoreLog(@"Failure Case:Server Certificate Data is nil."); + [extraInfo setObject:@"serverCertificateData null" forKey:@"serverCertificateData"]; + [MFSSLPinningHandler logSSLError:MF_IOS_SSL_SERVER_CERT_DATA_NULL]; + [MVMCoreLoggingHandler logWithDelegateWithObject:(id)self withName:MF_IOS_SSL_SERVER_CERT_DATA_NULL withExtraInfo:extraInfo]; + } + // Release clientPublicKey, because it is no logner needed + if (clientPublicKey) { + CFRelease(clientPublicKey); + } + // Release serverPublicKey, because it is no logner needed + if (serverPublicKey) { + CFRelease(serverPublicKey); + } + } else { + // Client certificate data is nil, this would not happen + MVMCoreLog(@"Failure Case:Client certificate data is nil."); + [extraInfo setObject:@"clientProdCertificateData null" forKey:@"clientProdCertificateData"]; + // But if happens then it is client side issue. + // So, bypass SSL Pinning. Developer need to fix this. + // It should not block user. + certificatesPinned = YES; + [MFSSLPinningHandler logSSLError:MF_IOS_SSL_CLIENT_CERT_NULL]; + [MVMCoreLoggingHandler logWithDelegateWithObject:(id)self withName:MF_IOS_SSL_CLIENT_CERT_NULL withExtraInfo:extraInfo]; + } + return certificatesPinned; +} + ++ (void)checkSSLTesting:(BOOL *)isServerCertificateTrusted withCertificatesPinned:(BOOL *)isCertificatesPinned { + // For testing, SSL error scenario + if ([[MVMCoreSessionObject sharedGlobal] testingSSLError] || [[MVMCoreSessionObject sharedGlobal] sslCertificateExpired]) { + MVMCoreLog(@"SSL Pinning Testing Error: certificate is not pinned explicitly, for testing SSL error scenarios."); + NSString *testCase = [[MVMCoreSessionObject sharedGlobal] testingSSLError] ? @"Test Case: SSL Error validated successfully." : @"Test Case: SSL Certificate expired validated successfully."; + MVMCoreLog(@"%@",testCase); + *isCertificatesPinned = NO; + *isServerCertificateTrusted = NO; + } +} + ++ (MFURLSessionChallengeResult * _Nonnull)processPinningStatus:(SecTrustRef)serverTrust isServerCertificateTrusted:(BOOL)isServerCertificateTrusted certificatesPinned:(BOOL)certificatesPinned extraInfo:(NSMutableDictionary *_Nonnull)extraInfo { + // The pinnning check + if (certificatesPinned && isServerCertificateTrusted) { + NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; +// [MFSSLPinningHandler logSSLError:MF_IOS_SSL_PINNING_SUCCESS]; + [MVMCoreLoggingHandler logWithDelegateWithObject:(id)self withName:MF_IOS_SSL_PINNING_SUCCESS withExtraInfo:extraInfo]; + MVMCoreLog(@"**********************SSL Pinning End: Pinning Successfully Completed******************************"); + return [MFURLSessionChallengeResult createChallengeResultWithDisposition:NSURLSessionAuthChallengeUseCredential credential:credential]; + } else { + [MVMCoreSessionObject sharedGlobal].connectionCancelledForSSLError = YES; + // either no connection or wrong SSL certificate. + [MFSSLPinningHandler logSSLError:MF_IOS_SSL_PINNING_FAILED]; + [MVMCoreLoggingHandler logWithDelegateWithObject:(id)self withName:MF_IOS_SSL_PINNING_FAILED withExtraInfo:extraInfo]; + MVMCoreLog(@"SSL extraInfo:%@",extraInfo); + MVMCoreLog(@"**********************SSL Pinning End: Pinning Failed******************************"); + return [MFURLSessionChallengeResult createChallengeResultWithDisposition:NSURLSessionAuthChallengeCancelAuthenticationChallenge credential:NULL]; + } +} + ++ (NSData *)clientCertificateData:(NSMutableDictionary *_Nonnull)extraInfo withCommonName:(NSString *_Nullable)commonName { + NSData *clientProdRootCertificateData = nil; + NSString *pathToProdCert = [MFSSLPinningHandler getCertificatePath:commonName]; + if (pathToProdCert.length > 0) { + MVMCoreLog(@"Certificate path is found."); + clientProdRootCertificateData = [NSData dataWithContentsOfFile:pathToProdCert]; + // if not testing SSL expiry + if (!([MVMCoreSessionObject sharedGlobal].sslCertificateExpired)) { + // To check, if client root certificate is expired + [MVMCoreSessionObject sharedGlobal].sslCertificateExpired = [MFSSLPinningHandler isCertificateExpiredAtPath:pathToProdCert]; + [extraInfo setValue:[NSNumber numberWithBool:[MVMCoreSessionObject sharedGlobal].sslCertificateExpired] forKey:@"sslCertificateExpired"]; + } + } else { + MVMCoreLog(@"Failure Case:Not able to find client root certificate path"); + // log this failure with extra info + [extraInfo setObject:@"pathToProdCert is nil" forKey:@"pathToProdCert"]; + [MFSSLPinningHandler logSSLError:MF_IOS_SSL_CERT_PATH_NOT_FOUND]; + [MVMCoreLoggingHandler logWithDelegateWithObject:(id)self withName:MF_IOS_SSL_CERT_PATH_NOT_FOUND withExtraInfo:extraInfo]; + } + return clientProdRootCertificateData; +} + +#pragma mark- NSURLSessionDelegate methods + ++ (MFURLSessionChallengeResult * _Nonnull)getResultForURLSession:(NSURLSession *_Nonnull)session didReceiveChallenge:(NSURLAuthenticationChallenge *_Nonnull)challenge { + + MVMCoreLog(@"**********************SSL Pinning started******************************"); + NSMutableDictionary *extraInfo = [NSMutableDictionary dictionary]; + [extraInfo setObject:[[UIDevice currentDevice] systemVersion] forKey:ParameterOSVersion]; + + // SSL status will always be true until turned off from Test screen + if ([MFSSLPinningHandler shouldBeSSLPinned]) { + + // First, check authenticationMethod, it should be NSURLAuthenticationMethodServerTrust + if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) { + + // Get server trust reference + SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust]; + + // Evaluate server trust or chain of certificates + MVMCoreLog(@"Evaluating server certificate"); + BOOL isServerCertificateTrusted = evaluateCertificate(serverTrust); + if (isServerCertificateTrusted) { + MVMCoreLog(@"Server certificate is Trusted."); + [extraInfo setObject:@"YES" forKey:@"isServerCertificateTrusted"]; + } else { + MVMCoreLog(@"Failure Case:Server certificate is not Trusted."); + [extraInfo setObject:@"NO" forKey:@"isServerCertificateTrusted"]; + } + + // Get certificate count from network + CFIndex certificateCount = 0; + if (serverTrust) { + certificateCount = SecTrustGetCertificateCount(serverTrust); + MVMCoreLog(@"serverCertificateCount:%ld",certificateCount); + [extraInfo setObject:[NSNumber numberWithLong:certificateCount] forKey:@"serverCertificateCount"]; + } else { + MVMCoreLog(@"Failure Case:serverTrust is nil."); + [extraInfo setObject:@"serverTrust is nil" forKey:@"serverTrust"]; + [extraInfo setObject:[NSNumber numberWithLong:certificateCount] forKey:@"serverCertificateCount"]; + } + + // Get server root certificate + SecCertificateRef serverRootCertificate = NULL; + // check certificate count to avoid crash + if (certificateCount>0) { + serverRootCertificate = SecTrustGetCertificateAtIndex(serverTrust, certificateCount-1); + } + + if (serverRootCertificate != NULL) { + + MVMCoreLog(@"Server Root Certificate:%@",serverRootCertificate); + // To check the validity of server certificate, that we are communicating to our designated mobile.vzw.com server + CFStringRef serverHostName = SecCertificateCopySubjectSummary(serverRootCertificate); + NSString *serverHostNameStr = (__bridge NSString *)serverHostName; + if (serverHostNameStr) { + MVMCoreLog(@"Server certificate distinguished common Name: %@",serverHostNameStr); + [extraInfo setObject:serverHostNameStr forKey:@"serverHostNameStr"]; + } else { + MVMCoreLog(@"Failure Case:server host name is nil."); + [extraInfo setObject:@"server host name is nil" forKey:@"serverHostNameStr"]; + } + + // release serverHostName, because it is no logner needed + if (serverHostName) { + CFRelease(serverHostName); + } + + // Get server root certficate data + CFDataRef serverCertificateDataRef = SecCertificateCopyData(serverRootCertificate); + NSData *serverRootCertificateData = (__bridge_transfer NSData*)serverCertificateDataRef; + + // Get/load client root certificate + NSData *clientProdRootCertificateData = [MFSSLPinningHandler clientCertificateData:extraInfo withCommonName:serverHostNameStr]; + + // To track certificate is pinned or not + BOOL certificatesPinned = NO; + + certificatesPinned = [MFSSLPinningHandler processClientCertificate:certificatesPinned extraInfo:extraInfo serverRootCertificateData:serverRootCertificateData clientProdRootCertificateData:clientProdRootCertificateData serverRootCertificate:serverRootCertificate serverHostNameStr:serverHostNameStr]; + + [MFSSLPinningHandler checkSSLTesting:&isServerCertificateTrusted withCertificatesPinned:&certificatesPinned]; + + // add extra info to analytics + [extraInfo setObject:[NSNumber numberWithBool:certificatesPinned] forKey:@"certificatesPinned"]; + [extraInfo setObject:[NSNumber numberWithBool:isServerCertificateTrusted] forKey:@"isServerCertificateTrusted"]; + + return [MFSSLPinningHandler processPinningStatus:serverTrust isServerCertificateTrusted:isServerCertificateTrusted certificatesPinned:certificatesPinned extraInfo:extraInfo]; + } else { + // this is should not happen, if happening then either client or apple bug + // server certificate is nil, track this and inform to server + // bcoz it is client/apple issue, then bypass SSL Pinning + MVMCoreLog(@"Failure: Server root certificate is null."); + [extraInfo setObject:@"server root Certificate null" forKey:@"serverCertificate"]; + NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; + [MFSSLPinningHandler logSSLError:MF_IOS_SSL_SERVER_CERT_NULL]; + [MVMCoreLoggingHandler logWithDelegateWithObject:(id)self withName:MF_IOS_SSL_SERVER_CERT_NULL withExtraInfo:extraInfo]; + MVMCoreLog(@"SSL extraInfo:%@",extraInfo); + MVMCoreLog(@"**********************SSL Pinning End: Pinning Failed******************************"); + return [MFURLSessionChallengeResult createChallengeResultWithDisposition:NSURLSessionAuthChallengeUseCredential credential:credential]; + } + } else { + // authenticationMethod is different + // FYI: NSURLAuthenticationMethodNTLM mean network issue + MVMCoreLog(@"Protection space authenticationMethod is different:%@",[[challenge protectionSpace] authenticationMethod]); + NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; + if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodNTLM]) { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + } + MVMCoreLog(@"If authentication Method is NSURLAuthenticationMethodNTLM, then it is network issue in your device, please referesh you internet and retry."); + MVMCoreLog(@"**********************SSL Pinning End: Pinning Failed******************************"); + return [MFURLSessionChallengeResult createChallengeResultWithDisposition:disposition credential:NULL]; + } + } else { + MVMCoreLog(@"SSL Pinning is switched off."); + [extraInfo setObject:@"Switched off" forKey:@"SSL_Status"]; + [MFSSLPinningHandler logSSLError:MF_IOS_SSL_PINNING_SWITCHED_OFF]; + [MVMCoreLoggingHandler logWithDelegateWithObject:(id)self withName:MF_IOS_SSL_PINNING_SWITCHED_OFF withExtraInfo:extraInfo]; + MVMCoreLog(@"SSL extraInfo:%@",extraInfo); + MVMCoreLog(@"**********************SSL Pinning End: Pinning is turned off ******************************"); + return [MFSSLPinningHandler allowChallenge:challenge]; + } +} + +#pragma mark- SSL Pinning Error Handling + ++ (nonnull MVMCoreErrorObject *)errorObjectForSSLErrors:(nullable NSString *)locationForError { + MVMCoreErrorObject *error = nil; + if ([MVMCoreSessionObject sharedGlobal].sslCertificateExpired) { + // SSL upgrade screen error object + error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:ERROR_SSL_UPDATE_TITLE_KEY] message:[MVMCoreGetterUtility hardcodedStringWithKey:ERROR_SSL_UPDATE_MESSAGE_KEY] messageToLog:[MVMCoreGetterUtility hardcodedStringWithKey:ERROR_SSL_UPDATE_MESSAGE_KEY] code:ErrorCodeSSL domain:ErrorDomainNative location:locationForError]; + } else { + // SSL Error object + NSString *message = [NSString stringWithFormat:@"%@ %@ %@",[MVMCoreGetterUtility hardcodedStringWithKey:ERROR_SSL_MESSAGE_PREFIX_KEY],[MVMCoreGetterUtility hardcodedStringWithKey:ERROR_SSL_MESSAGE_TITLE_KEY],[MVMCoreGetterUtility hardcodedStringWithKey:ERROR_SSL_MESSAGE_POSTFIX_KEY]]; + error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:message messageToLog:message code:ErrorCodeSSL domain:ErrorDomainNative location:locationForError]; + } + error.sslError = YES; + error.errorScreenError = YES; + return error; +} + ++ (nonnull MVMCoreErrorObject *)createErrorObjectForNSErrorAndVerifySSLErrors:(nonnull NSError *)error location:(nullable NSString *)locationForError { + MVMCoreErrorObject *errorObject = nil; + // If the request was cancelled due to SSL error. + if ([error code] == NSURLErrorCancelled && [MVMCoreSessionObject sharedGlobal].connectionCancelledForSSLError) { + errorObject = [MFSSLPinningHandler errorObjectForSSLErrors:locationForError]; + } else { + // Error with the request. + errorObject = [MVMCoreErrorObject createErrorObjectForNSError:error location:locationForError]; + } + return errorObject; +} + ++ (void)handleSSLErrors:(nullable MVMCoreErrorObject *)errorObject requestParameters:(nonnull MVMCoreRequestParameters *)requestParameters { + + // If the request was cancelled due to SSL error. + if (errorObject.code == NSURLErrorCancelled && [MVMCoreSessionObject sharedGlobal].connectionCancelledForSSLError) { + + // show SSL error screen's + MVMCoreErrorObject *sslError = [MFSSLPinningHandler errorObjectForSSLErrors:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:self pageType:requestParameters.pageType modules:[NSString stringWithFormat:@"%@",requestParameters.modules]]]; + [MVMCoreDispatchUtility performBlockOnMainThread:^{ + UIViewController *viewController = [[MVMCoreObject sharedInstance].globalLoadDelegate getNativeScreenForRequestError:sslError requestObject:requestParameters]; + if (viewController) { + [[MVMCoreNavigationHandler sharedNavigationHandler] setViewControllers:@[viewController] navigationController:nil animated:YES delegate:nil replaceInStack:NO completionHandler:nil]; + + } + }]; + } +} + +#pragma mark - SSL Logging + ++ (void)logSSLError:(NSString *_Nonnull)tag { + MVMCoreErrorObject *errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:ERROR_SSL_TITLE] message:tag code:ErrorCodeSSL domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:self pageType:@"SSL" modules:@""]]; + [MVMCoreLoggingHandler addErrorToLog:errorObject]; +} + +@end diff --git a/MVMCore/MVMCore/LoadHandling/SSLPinning/MFURLSessionChallengeResult.h b/MVMCore/MVMCore/LoadHandling/SSLPinning/MFURLSessionChallengeResult.h new file mode 100644 index 0000000..c4cba39 --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/SSLPinning/MFURLSessionChallengeResult.h @@ -0,0 +1,18 @@ +// +// MFURLSessionChallengeResult.h +// mobilefirst +// +// Created by Pfeil, Scott Robert on 8/4/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import + +@interface MFURLSessionChallengeResult : NSObject + +@property (nonatomic) NSURLSessionAuthChallengeDisposition disposition; +@property (nonatomic, nullable, strong) NSURLCredential *credential; + ++ (instancetype _Nullable )createChallengeResultWithDisposition:(NSURLSessionAuthChallengeDisposition)disposition credential:(NSURLCredential * _Nullable)credential; + +@end diff --git a/MVMCore/MVMCore/LoadHandling/SSLPinning/MFURLSessionChallengeResult.m b/MVMCore/MVMCore/LoadHandling/SSLPinning/MFURLSessionChallengeResult.m new file mode 100644 index 0000000..f0beaee --- /dev/null +++ b/MVMCore/MVMCore/LoadHandling/SSLPinning/MFURLSessionChallengeResult.m @@ -0,0 +1,20 @@ +// +// MFURLSessionChallengeResult.m +// mobilefirst +// +// Created by Pfeil, Scott Robert on 8/4/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import "MFURLSessionChallengeResult.h" + +@implementation MFURLSessionChallengeResult + ++ (instancetype _Nullable )createChallengeResultWithDisposition:(NSURLSessionAuthChallengeDisposition)disposition credential:(NSURLCredential * _Nullable)credential { + MFURLSessionChallengeResult *result = [[MFURLSessionChallengeResult alloc] init]; + result.disposition = disposition; + result.credential = credential; + return result; +} + +@end diff --git a/MVMCore/MVMCore/MVMCore.h b/MVMCore/MVMCore/MVMCore.h new file mode 100644 index 0000000..41c00c2 --- /dev/null +++ b/MVMCore/MVMCore/MVMCore.h @@ -0,0 +1,109 @@ +// +// MVMCore.h +// MVMCore +// +// Created by Yang, Tianhang (Chris) on 11/9/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import + +//! Project version number for MVMCore. +FOUNDATION_EXPORT double MVMCoreVersionNumber; + +//! Project version string for MVMCore. +FOUNDATION_EXPORT const unsigned char MVMCoreVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +// Constants +#import +#import +#import +#import + +// Categories +#import +#import +#import +#import +#import + +// Utility +#import +#import +#import +#import +#import + +// Mapping +#import +#import +#import +#import + +// Main Structure +#import +#import +#import + + +// Load Handling +#import +#import +#import +#import +#import +#import +#import +#import + + // Alert Handling +#import +#import +#import +#import +#import +#import +#import +#import + +// Presentation Handling +#import +#import +#import +#import +#import +#import +#import + +// Action Handling +#import +#import +#import +#import + +// Protocols +#import +#import +#import +#import + +// Other Handlers and Protocols +#import +#import +#import + +// Singletons +#import +#import +#import + +// Main Views + + +//Freebee +#import + +//SSL +#import diff --git a/MVMCore/MVMCore/MainProtocols/MVMCoreGlobalLoadProtocol.h b/MVMCore/MVMCore/MainProtocols/MVMCoreGlobalLoadProtocol.h new file mode 100644 index 0000000..47d7a61 --- /dev/null +++ b/MVMCore/MVMCore/MainProtocols/MVMCoreGlobalLoadProtocol.h @@ -0,0 +1,35 @@ +// +// MVMCGlobalLoadProtocol.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/21/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import +#import +#import +#import +#import + +@protocol MVMCoreGlobalLoadProtocol + +// Provides the desired error screen for the native error. If null, will fail silently. Called on the main thread. +- (nullable UIViewController *)getNativeScreenForRequestError:(nonnull MVMCoreErrorObject *)errorObject requestObject:(nonnull MVMCoreRequestParameters *)requestObject; + +@optional + +/* Can choose to handle global error scenarios here... This will happen for every request so only put things in here that aren't controller specific. Must call completion handler. + @param completionHandler must be called when finished handling errors. Pass if we should continue or not, and any error object that you want to be handled. */ +- (void)handleGlobalErrorsForLoadObject:(nonnull MVMCoreLoadObject *)loadObject completionHandler:(nonnull void (^)(BOOL shouldContinueLoad, MVMCoreErrorObject * _Nullable error))completionHandler; + +// Can overwrite this to provide any view controllers that may be cached for the provided load object page type. Allows for using an old screen instead of creating a new screen. This function should not be used to create a screen. +- (nullable UIViewController *)getCachedViewControllerForLoadObject:(nonnull MVMCoreLoadObject *)loadObject; + +// Can overwrite this function to provide a view manager with the embedded view controller instead of just the view controller. For example, load a custom tab bar with this page as the first tab. +- (nullable UIViewController *)getViewManagerWithViewController:(nonnull UIViewController *)viewController loadObject:(nullable MVMCoreLoadObject *)loadObject; + +// Can overwrite to set the request time out time. +- (NSTimeInterval)timeOutIntervalForRequest:(nullable MVMCoreRequestParameters *)request; + +@end diff --git a/MVMCore/MVMCore/MainProtocols/MVMCoreLoggingDelegateProtocol.h b/MVMCore/MVMCore/MainProtocols/MVMCoreLoggingDelegateProtocol.h new file mode 100644 index 0000000..a5cacca --- /dev/null +++ b/MVMCore/MVMCore/MainProtocols/MVMCoreLoggingDelegateProtocol.h @@ -0,0 +1,24 @@ +// +// MVMCoreLoggingDelegateProtocol.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/28/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import + +@protocol MVMCoreLoggingDelegateProtocol + +@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; + +@end diff --git a/MVMCore/MVMCore/MainProtocols/MVMCoreViewControllerProtocol.h b/MVMCore/MVMCore/MainProtocols/MVMCoreViewControllerProtocol.h new file mode 100644 index 0000000..f9899ad --- /dev/null +++ b/MVMCore/MVMCore/MainProtocols/MVMCoreViewControllerProtocol.h @@ -0,0 +1,34 @@ +// +// MVMCoreViewControllerProtocol.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/22/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import +@class MVMCoreLoadObject; +@class MVMCoreErrorObject; + +@protocol MVMCoreViewControllerProtocol + +// TODO: need to be moved out when core is completely finished +@property (nonatomic) BOOL masterShouldBeAccessible; +@property (nonatomic) BOOL supportShouldBeAccessible; +@property (nonatomic) BOOL cartShouldBeAccessible; +@property (nonatomic) BOOL closeButtonAccessible; +@property (nullable, nonatomic, readonly) NSDictionary *closeButtonActionMap; + + +@property (nullable, strong, nonatomic) NSString *pageType; + +// This view controller should subclass this function and check the load to make sure it has all the needed data. Fills the error object if there are any errors. Returns if we should finish the load or not. Ideally error should use code ErrorCodeViewControllerProcessingJSON. +- (BOOL)shouldFinishProcessingLoad:(nonnull MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error; + +@optional + +// Called when the back button is pressed. Overwrite for special functionality. Default is to just popviewcontroller. +- (void)backButtonPressed; + +@end + diff --git a/MVMCore/MVMCore/MainProtocols/MVMCoreViewManagerProtocol.h b/MVMCore/MVMCore/MainProtocols/MVMCoreViewManagerProtocol.h new file mode 100644 index 0000000..a3609e6 --- /dev/null +++ b/MVMCore/MVMCore/MainProtocols/MVMCoreViewManagerProtocol.h @@ -0,0 +1,19 @@ +// +// MVMCoreViewManagerProtocol.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/22/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import + +@protocol MVMCoreViewManagerProtocol + +// Return the current view controller showing +- (nonnull UIViewController *)getCurrentViewController; + +// Returns if the manage currently contains the page with the page type. +- (BOOL)containsPageWithPageType:(nullable NSString *)pageType; + +@end diff --git a/MVMCore/MVMCore/MainProtocols/MVMCoreViewProtocol.h b/MVMCore/MVMCore/MainProtocols/MVMCoreViewProtocol.h new file mode 100644 index 0000000..89a98f5 --- /dev/null +++ b/MVMCore/MVMCore/MainProtocols/MVMCoreViewProtocol.h @@ -0,0 +1,20 @@ +// +// MVMCoreViewProtocol.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/22/17. +// Copyright © 2017 myverizon. All rights reserved. +// +#import + +@protocol MVMCoreViewProtocol + +// Updates the ui to fit the right size. +- (void)updateView:(CGFloat)size; + +@optional + +// Can setup ui here. Should be called in the initialization functions. +- (void)setupView; + +@end diff --git a/MVMCore/MVMCore/MainProtocols/MobileFirstProtocol.h b/MVMCore/MVMCore/MainProtocols/MobileFirstProtocol.h new file mode 100644 index 0000000..61f54b2 --- /dev/null +++ b/MVMCore/MVMCore/MainProtocols/MobileFirstProtocol.h @@ -0,0 +1,65 @@ +// +// MobileFirstProtocol.h +// mobilefirst +// +// Created by Scott Pfeil on 4/26/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import +#import +#import +#import +@class MVMCoreSessionObject; + +@protocol MobileFirstProtocol + +// For if the app is testing. +- (BOOL)isTesting; + +// For if mobile first should use sso. +- (BOOL)shouldUseSSO; + +// For if mobile first should log requests and responses to the console. +- (BOOL)shouldLogRequestsResponse; + +//// Return the session singleton to use. (temp not in use) +//- (nonnull MVMCoreSessionObject *)getSessionSingleton; + +// Adds modified send parameters that will be passed to the server during initial mvm load +- (void)addModifiedSendParametersForLaunch:(nullable NSDictionary *)modifiedSendParameters; + +/* Restarts the application. + * @param pageType A page type to start with loading. + * @param clearSessionVariables A Boolean to determine if we should clear the session variables. + */ +- (void)restartWithPageType:(nullable NSString *)pageType clearSessionVariables:(BOOL)clearSessionVariables; + +// Redirect, leaving mobile first. +- (void)redirectWithAppContext:(MVMAppContext)context pageType:(nullable NSString *)pageType extraParameters:(nullable NSDictionary *)extraParameters storeContextForNextRestart:(BOOL)storeContextForNextRestart; + +// Call when deep link finished. +- (void)deepLinkFinished; + +// Called when the app is launched. +- (void)launchedWithMDN:(nonnull NSString *)mdn context:(MVMAppContext)context dataMeterUsage:(nullable NSDictionary *)dataMeterUsage other:(nullable NSDictionary *)other; + +// Loads content transfer +- (void)loadContentTransfer; + +// loads activation +- (void)loadActivation; + +// loads mvd active +- (void)presentMvdActive:(nullable NSDictionary*)data; + +// Called when pop data is started. +- (void)clearspotStartedWithTimeRemaining:(nonnull NSString *)timeRemaining; + +// Called when legacy SMART should be enabled. +- (void)onSmartLegacyFallback; + +// Currently only used by ipad. It's a token used to identify the device. +- (nullable NSString *)getDeviceToken; + +@end diff --git a/MVMCore/MVMCore/MainStructure/MVMCoreMainSplitViewProtocol.h b/MVMCore/MVMCore/MainStructure/MVMCoreMainSplitViewProtocol.h new file mode 100644 index 0000000..7afcf84 --- /dev/null +++ b/MVMCore/MVMCore/MainStructure/MVMCoreMainSplitViewProtocol.h @@ -0,0 +1,23 @@ +// +// MainSplitViewProtocol.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/14/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import +#import + +@protocol MVMCoreMainSplitViewProtocol + +// Provide the panels that you would like to use. +- (nullable UIViewController *)getLeftPanel; +- (nullable UIViewController *)getRightPanel; + +@optional + +// Provides the top alert view to use. +- (nullable MVMCoreTopAlertView *)getTopAlertView; + +@end diff --git a/MVMCore/MVMCore/MainStructure/MVMCoreTopAlertView.h b/MVMCore/MVMCore/MainStructure/MVMCoreTopAlertView.h new file mode 100644 index 0000000..06d3ffe --- /dev/null +++ b/MVMCore/MVMCore/MainStructure/MVMCoreTopAlertView.h @@ -0,0 +1,33 @@ +// +// MVMCoreTopAlertView.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/22/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import +#import +#import +#import + +@interface MVMCoreTopAlertView : UIView + +// Delegate for the top alert view +@property (nonatomic, nullable, weak) id animationDelegate; + +// Current top alert object +@property (strong, nullable, nonatomic) MVMCoreTopAlertObject *topAlertObject; + +- (void)pinATopViewController:(nonnull UIViewController *)viewController; + +// Show based on the object +- (void)showWithTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id )animationDelegate completionHandler:(void (^ __nullable)(BOOL finished))completionHandler; + +// Hides +- (void)hideAlertView:(void (^ __nullable)(BOOL finished))completionHandler; + +// Collapses the notification if it has a short top message. Otherwise removes notification. +- (void)collapseNotification; + +@end diff --git a/MVMCore/MVMCore/MainStructure/MVMCoreTopAlertView.m b/MVMCore/MVMCore/MainStructure/MVMCoreTopAlertView.m new file mode 100644 index 0000000..fa13269 --- /dev/null +++ b/MVMCore/MVMCore/MainStructure/MVMCoreTopAlertView.m @@ -0,0 +1,44 @@ +// +// MVMCoreTopAlertView.m +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/22/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import "MVMCoreTopAlertView.h" + +@implementation MVMCoreTopAlertView + +- (instancetype)init { + if (self = [super init]) { + [self setupView]; + } + return self; +} + +- (void)setupView { + self.clipsToBounds = YES; +} + +- (void)pinATopViewController:(UIViewController *)viewController { +} + +- (void)updateView:(CGFloat)size { +} + +- (void)showWithTopAlertObject:(nullable MVMCoreTopAlertObject *)topAlertObject animationDelegate:(nonnull id )animationDelegate completionHandler:(void (^ __nullable)(BOOL finished))completionHandler { + + self.animationDelegate = animationDelegate; + self.topAlertObject = topAlertObject; + completionHandler(NO); +} + +- (void)hideAlertView:(void (^ __nullable)(BOOL finished))completionHandler { + completionHandler(NO); +} + +- (void)collapseNotification { +} + +@end diff --git a/MVMCore/MVMCore/MainStructure/PanelProtocol.h b/MVMCore/MVMCore/MainStructure/PanelProtocol.h new file mode 100644 index 0000000..a7920bc --- /dev/null +++ b/MVMCore/MVMCore/MainStructure/PanelProtocol.h @@ -0,0 +1,32 @@ +// +// PanelProtocol.h +// mobilefirst +// +// Created by Seshamani, Shreyas on 6/2/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import +#import +@protocol PanelProtocol + +#pragma mark - life cycle + +- (void)panel:(UIViewController *_Nullable)panel viewWillAppear:(BOOL)animated; +- (void)panel:(UIViewController *_Nullable)panel viewDidAppear:(BOOL)animated; +- (void)panel:(UIViewController *_Nullable)panel viewWillDisappear:(BOOL)animated; +- (void)panel:(UIViewController *_Nullable)panel viewDidDisappear:(BOOL)animated; + +- (void)showArrow; +- (void)hideArrow; + +@optional + +- (void)clearData; +- (void)resetIconToDefault; + +// temp fix to make mf compile, will remove after all the protocols for main split view controller are done +- (void)dataPassForShop;//in support +- (void)getCurrentDetailViewController:(nullable UIViewController *)currentViewController;//in menu + +@end diff --git a/MVMCore/MVMCore/OtherHandlers/MVMCoreCache.h b/MVMCore/MVMCore/OtherHandlers/MVMCoreCache.h new file mode 100644 index 0000000..c5263fb --- /dev/null +++ b/MVMCore/MVMCore/OtherHandlers/MVMCoreCache.h @@ -0,0 +1,135 @@ +// +// MVMCoreCache.h +// myverizon +// +// Created by Scott Pfeil on 11/27/13. +// Copyright (c) 2013 Verizon Wireless. All rights reserved. +// +// Caches the json that comes from the server. + +#import +#import + +@interface MVMCoreCache : NSObject + +// The static string cache. Stored on the device. +@property (nullable, strong, nonatomic, readonly) NSDictionary *staticStringCache; +@property (nonnull, strong, nonatomic) NSOperationQueue *completionQueue; + +// Returns the shared instance of this singleton ++ (nullable instancetype)sharedCache; + +#pragma mark - Page and Module Handling + +// Checks the set of pageTypes to be cached for the given pageType. +- (BOOL)shouldCacheJSONWithPageType:(nonnull NSString *)pageType; + +// Checks the set of modules to be cached for the given module +- (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 *)array; + +// For modules external to the mobile first framework to be added to the list to not cache +- (void)addModulesToNotCache:(nullable NSArray *)array; + +#pragma mark - Simple Fetch + +// Gets a json dictionary from the cache by pageType. Pass in the block that you want to run once the dictionary is received. This will be run on a background thread. +- (void)fetchJSONForPageType:(nullable NSString *)pageType completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler; + +// Gets a json dictionary from the cache by module. Pass in the block that you want to run once the dictionary is received. This will be run on a background thread. +- (void)fetchJSONForModule:(nullable NSString *)module completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler; + +// 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; + +#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. +- (void)fetchJSONForPageType:(nullable NSString *)pageType queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler; + +// Gets a json dictionary from the cache by module. 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. +- (void)fetchJSONForModule:(nullable NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler; + +// 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 and which queue to run it on. Pass in if you'd like the current thread to wait. +- (void)fetchJSONForModules:(nullable NSArray *)modules queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler; + +#pragma mark - Simple Insertion + +// Adds a json dictionary to the cache by pageType. +- (void)addPageToCache:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType; + +// Adds a json dictionary to the cache by module. +- (void)addModuleToCache:(nonnull NSDictionary *)jsonDictionary module:(nonnull NSString *)module; + +// Adds a json dictionary to the cache by modules. +- (void)addModulesToCache:(nonnull NSDictionary *)jsonDictionary; + +#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. +- (void)addPageToCache:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock; + +// Adds a json dictionary to the cache by module. 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. +- (void)addModuleToCache:(nonnull NSDictionary *)jsonDictionary module:(nonnull NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock; + +// Pass in a dictionary containing modules to store in the cache. 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. +- (void)addModulesToCache:(nonnull NSDictionary *)jsonDictionary queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock; + +#pragma mark - Simple Deletion + +// Removes a json dictionary from the cache by pageType. +- (void)removeJSONForPageType:(nonnull NSString *)pageType; + +// Removes a json dictionary from the cache by module. +- (void)removeJSONForModule:(nonnull NSString *)module; + +// Removes the json for modules. +- (void)removeJSONForModules:(nonnull NSArray *)modules; + +// Clears the page and module data. +- (void)clearMFCache; + +#pragma mark - Advanced Deletion + +// Removes a json dictionary from 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. +- (void)removeJSONForPageType:(nonnull NSString *)pageType queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock; + +// Removes a json dictionary from the cache by module. Pass in the block that you want to run once the dictionary is received and which queue to run it on. +- (void)removeJSONForModule:(nonnull NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock; + +// 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; + +#pragma mark Image Functions + +// Subclass to return the bundle to use +- (nonnull NSBundle *)bundleToUseForImages; + +// Checks if the image is in the cache. If not, will try to download or create the image. +// Width is used for downloading from scene7. +// Height is used for downloading from scene7. +// Format is used for downloading from scene7 +// Most cases only need to provide the width. If both are provided and the aspect ratio is different from the images, it will center the image. +// In the future, may need to use align for the shop flow. +// localFallbackImageName: should be the name of a local file to use in case of failure to download. +- (void)getImage:(nonnull NSString *)pathOrName useWidth:(BOOL)useWidth widthForS7:(NSInteger)widthForS7 useHeight:(BOOL)useHeight heightForS7:(NSInteger)heightForS7 localFallbackImageName:(nullable NSString *)fallbackImageName completionHandler:(nonnull void (^)(UIImage * _Nullable))completionHandler; +- (void)getImage:(nonnull NSString *)pathOrName useWidth:(BOOL)useWidth widthForS7:(NSInteger)widthForS7 useHeight:(BOOL)useHeight heightForS7:(NSInteger)heightForS7 format:(nullable NSString *)format localFallbackImageName:(nullable NSString *)fallbackImageName completionHandler:(nonnull void (^)(UIImage * _Nullable))completionHandler; +- (void)getGif:(nonnull NSString *)pathOrName useWidth:(BOOL)useWidth widthForS7:(NSInteger)widthForS7 useHeight:(BOOL)useHeight heightForS7:(NSInteger)heightForS7 format:(nullable NSString *)format localFallbackImageName:(nullable NSString *)fallbackImageName completionHandler:(nonnull void (^)(NSData * _Nullable, UIImage * _Nullable))completionHandler; + +// Gets a cropped version of the requested image. Similar to the method above, but you can specify the final rect +- (void)getCroppedImage:(nonnull NSString *)pathOrName useWidth:(BOOL)useWidth widthForS7:(NSInteger)widthForS7 useHeight:(BOOL)useHeight heightForS7:(NSInteger)heightForS7 finalRect:(CGRect)finalRect flipImage:(BOOL)flipImage localFallbackImageName:(nullable NSString *)fallbackImageName completionHandler:(nonnull void (^)(UIImage * _Nullable))completionHandler; + +// Gets an image from the cache. +- (nullable UIImage *)getCachedImageWithName:(nonnull NSString *)imageName; +- (nullable NSData *)getCachedDataWithName:(nonnull NSString *)imageName; + +// Adds the image to the cache +- (void)addImageToCache:(nonnull UIImage *)image withName:(nonnull NSString *)imageName; +- (void)addDataToCache:(nonnull NSData *)data withName:(nonnull NSString *)imageName; + +// clears the image cache +- (void)clearImageCache; + +@end diff --git a/MVMCore/MVMCore/OtherHandlers/MVMCoreCache.m b/MVMCore/MVMCore/OtherHandlers/MVMCoreCache.m new file mode 100644 index 0000000..093b517 --- /dev/null +++ b/MVMCore/MVMCore/OtherHandlers/MVMCoreCache.m @@ -0,0 +1,627 @@ +// +// MVMCoreCache.m +// myverizon +// +// Created by Scott Pfeil on 11/27/13. +// Copyright (c) 2013 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreCache.h" +#import +#import "MVMCoreErrorObject.h" +#import "MVMCoreLoggingHandler.h" +#import "MFFreebeeHandler.h" +#import "MVMCoreGetterUtility.h" +#import "MVMCoreObject.h" + +@interface MVMCoreCache () + +// The cache for json. +@property (nullable, strong, nonatomic) NSMutableDictionary *pageTypeCache; +@property (nullable, strong, nonatomic) NSMutableDictionary *moduleCache; +@property (nullable, strong, nonatomic) NSMutableDictionary *imgCache; + +// The static string cache. Stored on the device. +@property (nullable, strong, nonatomic, readwrite) NSDictionary *staticStringCache; + +@property (nonnull, strong, nonatomic) NSOperationQueue *pageTypeQueue; +@property (nonnull, strong, nonatomic) NSOperationQueue *moduleQueue; + +@property (nullable, strong, nonatomic) NSMutableSet *pageTypesToNotCache; +@property (nullable, strong, nonatomic) NSMutableSet *modulesToNotCache; + +// For thread safety +@property (strong, nonatomic) dispatch_queue_t imageCacheQueue; + +@end + +@implementation MVMCoreCache + +static NSString * const STATIC_CACHE_COMPONENT = @"StaticCache.txt"; + ++ (nullable instancetype)sharedCache { + return [MVMCoreObject sharedInstance].cache; +} + +- (nullable instancetype)init { + if (self = [super init]) { + self.completionQueue = [[NSOperationQueue alloc] init]; + + self.pageTypeQueue = [[NSOperationQueue alloc] init]; + self.pageTypeQueue.maxConcurrentOperationCount = 1; + + self.moduleQueue = [[NSOperationQueue alloc] init]; + self.moduleQueue.maxConcurrentOperationCount = 1; + + self.imageCacheQueue = dispatch_queue_create("imgCache", DISPATCH_QUEUE_CONCURRENT); + } + return self; +} + +#pragma mark - Page and Module Handling + +- (NSMutableSet *)pageTypesToNotCache { + + // Filled with pages that we do not want to cache on client side. + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _pageTypesToNotCache = [NSMutableSet set]; + }); + return _pageTypesToNotCache; +} + +- (NSMutableSet *)modulesToNotCache { + + // Filled with modules that we do not want to cache on client side. + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _modulesToNotCache = [NSMutableSet set]; + }); + return _modulesToNotCache; +} + +- (BOOL)shouldCacheJSONWithPageType:(nonnull NSString *)pageType { + return ![self.pageTypesToNotCache containsObject:pageType]; +} + +- (BOOL)shouldCacheJSONWithModule:(nonnull NSString *)module { + return ![self.modulesToNotCache containsObject:module]; +} + +- (void)addPageTypesToNotCache:(nullable NSArray *)array { + if (array) { + [self.pageTypesToNotCache addObjectsFromArray:array]; + } +} + +- (void)addModulesToNotCache:(nullable NSArray *)array { + if (array) { + [self.modulesToNotCache addObjectsFromArray:array]; + } +} + +#pragma mark - Simple Fetch + +- (void)fetchJSONForPageType:(nullable NSString *)pageType completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler { + [self fetchJSONForPageType:pageType queue:self.completionQueue waitUntilFinished:NO completionHandler:completionHandler]; +} + +- (void)fetchJSONForModule:(nullable NSString *)module completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler { + [self fetchJSONForModule:module queue:self.completionQueue waitUntilFinished:NO completionHandler:completionHandler]; +} + +- (void)fetchJSONForModules:(nullable NSArray *)modules completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler { + [self fetchJSONForModules:modules queue:self.completionQueue waitUntilFinished:NO completionHandler:completionHandler]; +} + +#pragma mark - Advanced Fetch + +- (void)fetchJSONForPageType:(nullable NSString *)pageType queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler { + + __weak typeof(self) weakSelf = self; + NSBlockOperation *getOperation = [[NSBlockOperation alloc] init]; + __weak NSBlockOperation *weakOperation = getOperation; + [getOperation addExecutionBlock:^{ + + NSDictionary *dictionary = nil; + if (!weakOperation.isCancelled) { + if (pageType) { + + // First checks the cache by page type. + dictionary = [weakSelf.pageTypeCache objectForKey:pageType]; + } else { + + // If no pagetype, return whole cache + dictionary = weakSelf.pageTypeCache; + } + } + + if (completionHandler) { + [(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:^{ + completionHandler(dictionary); + }]] waitUntilFinished:waitUntilFinished]; + } + }]; + [self.pageTypeQueue addOperations:@[getOperation] waitUntilFinished:waitUntilFinished]; +} + +- (void)fetchJSONForModule:(nullable NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler { + [self fetchJSONForModules:@[module] queue:queue waitUntilFinished:waitUntilFinished completionHandler:completionHandler]; +} + +- (void)fetchJSONForModules:(nullable NSArray *)modules queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionHandler:(nonnull void (^)(NSDictionary * _Nullable jsonDictionary))completionHandler { + + __weak typeof(self) weakSelf = self; + NSBlockOperation *getOperation = [[NSBlockOperation alloc] init]; + __weak NSBlockOperation *weakOperation = getOperation; + [getOperation addExecutionBlock:^{ + + NSDictionary *dictionary = nil; + if (!weakOperation.isCancelled) { + + NSMutableDictionary *modulesDictionary = [NSMutableDictionary dictionary]; + for (NSString *module in modules) { + + NSDictionary *moduleDictionary = [weakSelf.moduleCache objectForKey:module]; + if (moduleDictionary) { + [modulesDictionary setObject:moduleDictionary forKey:module]; + } + } + + if (modulesDictionary.count > 0) { + dictionary = [NSDictionary dictionaryWithDictionary:modulesDictionary]; + } + } + if (completionHandler) { + [(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:^{ + completionHandler(dictionary); + }]] waitUntilFinished:waitUntilFinished]; + } + }]; + [self.moduleQueue addOperations:@[getOperation] waitUntilFinished:waitUntilFinished]; +} + +#pragma mark - Simple Insertion + +- (void)addPageToCache:(nonnull NSDictionary *)jsonDictionary pageType:(nonnull NSString *)pageType { + [self addPageToCache:jsonDictionary pageType:pageType queue:nil waitUntilFinished:NO completionBlock:NULL]; +} + +- (void)addModuleToCache:(nonnull NSDictionary *)jsonDictionary module:(nonnull NSString *)module { + [self addModuleToCache:jsonDictionary module:module queue:nil waitUntilFinished:NO completionBlock:NULL]; +} + +- (void)addModulesToCache:(nonnull NSDictionary *)jsonDictionary { + [self addModulesToCache:jsonDictionary queue:nil waitUntilFinished:NO completionBlock:NULL]; +} + +#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 { + + NSBlockOperation *addOperation = [[NSBlockOperation alloc] init]; + __weak NSBlockOperation *weakOperation = addOperation; + __weak typeof(self) weakSelf = self; + [addOperation addExecutionBlock:^{ + + if (!weakOperation.isCancelled && [[MVMCoreCache sharedCache] shouldCacheJSONWithPageType:pageType]) { + + // There must be a dictionary and page type to cache. + if (jsonDictionary && pageType) { + + if (!weakSelf.pageTypeCache) { + + // Create the cache if necessary. + weakSelf.pageTypeCache = [NSMutableDictionary dictionary]; + } + + // Adds json to cache with page type key. + [weakSelf.pageTypeCache setObject:jsonDictionary forKey:pageType]; + } + } + if (completionBlock) { + [(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:completionBlock]] waitUntilFinished:waitUntilFinished]; + } + }]; + [self.pageTypeQueue addOperations:@[addOperation] waitUntilFinished:waitUntilFinished]; +} + +- (void)addModuleToCache:(nonnull NSDictionary *)jsonDictionary module:(nonnull NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock { + [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; + [addOperation addExecutionBlock:^{ + + if (!weakOperation.isCancelled) { + + [jsonDictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + + 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. + [weakSelf.moduleCache setObject:obj forKey:key]; + } + }]; + } + if (completionBlock) { + [(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:completionBlock]] waitUntilFinished:waitUntilFinished]; + } + }]; + [self.moduleQueue addOperations:@[addOperation] waitUntilFinished:waitUntilFinished]; +} + +#pragma mark - Simple Deletion + +- (void)removeJSONForPageType:(nonnull NSString *)pageType { + [self removeJSONForPageType:pageType queue:nil waitUntilFinished:NO completionBlock:NULL]; +} + +- (void)removeJSONForModule:(nonnull NSString *)module { + [self removeJSONForModule:module queue:nil waitUntilFinished:NO completionBlock:NULL]; +} + +- (void)removeJSONForModules:(nonnull NSArray *)modules { + [self removeJSONForModules:modules queue:nil waitUntilFinished:NO completionBlock:NULL]; +} + +- (void)clearMFCache { + [self.pageTypeQueue cancelAllOperations]; + [self.moduleQueue cancelAllOperations]; + + __weak typeof(self) weakSelf = self; + [self.pageTypeQueue addOperation:[NSBlockOperation blockOperationWithBlock:^{ + [weakSelf.pageTypeCache removeAllObjects]; + }]]; + + [self.moduleQueue addOperation:[NSBlockOperation blockOperationWithBlock:^{ + [weakSelf.moduleCache removeAllObjects]; + }]]; +} + +#pragma mark - Advanced Deletion + +- (void)removeJSONForPageType:(nonnull NSString *)pageType queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock { + + NSBlockOperation *removeOperation = [[NSBlockOperation alloc] init]; + __weak NSBlockOperation *weakOperation = removeOperation; + __weak typeof(self) weakSelf = self; + [removeOperation addExecutionBlock:^{ + + if (!weakOperation.isCancelled) { + + // There must be a pageType to remove from cache. + if (weakSelf.pageTypeCache && pageType) { + + // Removes json from cache with page type key. + [weakSelf.pageTypeCache removeObjectForKey:pageType]; + } + } + if (completionBlock) { + [(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:completionBlock]] waitUntilFinished:waitUntilFinished]; + } + }]; + [self.pageTypeQueue addOperations:@[removeOperation] waitUntilFinished:waitUntilFinished]; +} + +- (void)removeJSONForModule:(nonnull NSString *)module queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock { + + NSBlockOperation *removeOperation = [[NSBlockOperation alloc] init]; + __weak NSBlockOperation *weakOperation = removeOperation; + __weak typeof(self) weakSelf = self; + [removeOperation addExecutionBlock:^{ + + if (!weakOperation.isCancelled) { + + // There must be a module to remove from cache. + if (weakSelf.moduleCache && module) { + + // Removes json from cache with module key. + [weakSelf.moduleCache removeObjectForKey:module]; + } + } + if (completionBlock) { + [(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:completionBlock]] waitUntilFinished:waitUntilFinished]; + } + }]; + [self.moduleQueue addOperations:@[removeOperation] waitUntilFinished:waitUntilFinished]; +} + +- (void)removeJSONForModules:(nonnull NSArray *)modules queue:(nullable NSOperationQueue *)queue waitUntilFinished:(BOOL)waitUntilFinished completionBlock:(nullable void (^)(void))completionBlock { + + NSBlockOperation *removeOperation = [[NSBlockOperation alloc] init]; + __weak NSBlockOperation *weakOperation = removeOperation; + __weak typeof(self) weakSelf = self; + [removeOperation addExecutionBlock:^{ + + [modules enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + + if (!weakOperation.isCancelled) { + + // There must be a module to remove from cache. + if (weakSelf.moduleCache && obj) { + + // Removes json from cache with module key. + [weakSelf.moduleCache removeObjectForKey:obj]; + } + } + }]; + if (completionBlock) { + [(queue ?: weakSelf.completionQueue) addOperations:@[[NSBlockOperation blockOperationWithBlock:completionBlock]] waitUntilFinished:waitUntilFinished]; + } + }]; + [self.moduleQueue addOperations:@[removeOperation] waitUntilFinished:waitUntilFinished]; +} + +#pragma mark - Image Functions + +- (nonnull NSBundle *)bundleToUseForImages { + return [MVMCoreGetterUtility bundleForMVMCore]; +} + +- (NSMutableDictionary *)imgCache { + + // Create in memory cache + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _imgCache = [[NSMutableDictionary alloc] init]; + }); + return _imgCache; +} + +- (NSURL *)handleS7Path:(NSString *)pathOrName useWidth:(BOOL)useWidth widthForS7:(NSInteger)widthForS7 useHeight:(BOOL)useHeight heightForS7:(NSInteger)heightForS7 format:(nullable NSString *)format finalRect:(CGRect)finalRect flipImage:(BOOL)flipImage { + + // For scene 7 + NSURLComponents *components = [[NSURLComponents alloc] initWithString:pathOrName]; + if (components) { + + // First, remove any hash tag sent by the server because it invalidates our parameters. + components.fragment = nil; + + // Set up the new query parameters. + NSMutableArray *newQueries = [[NSMutableArray alloc] init]; + + // By default use png format. + NSString *s7Format = format; + if (!s7Format) { + s7Format = @"png-alpha"; + } + [newQueries addObject:[[NSURLQueryItem alloc] initWithName:@"fmt" value:s7Format]]; + + // Set up the width and height of the image to download (pixels) + CGFloat scale = [UIScreen mainScreen].scale; + if (useWidth) { + [newQueries addObject:[[NSURLQueryItem alloc] initWithName:@"wid" value:[NSString stringWithFormat:@"%li",(long)(widthForS7 * scale)]]]; + } + if (useHeight) { + [newQueries addObject:[[NSURLQueryItem alloc] initWithName:@"hei" value:[NSString stringWithFormat:@"%li",(long)(heightForS7 * scale)]]]; + } + + if (!CGRectIsNull(finalRect)) { + CGRect scaledRect = CGRectMake(finalRect.origin.x*scale, finalRect.origin.y*scale, finalRect.size.width*scale, finalRect.size.height*scale); + [newQueries addObject:[[NSURLQueryItem alloc] initWithName:@"rect" value:[NSString stringWithFormat:@"%d,%d,%d,%d",(int)scaledRect.origin.x, (int)scaledRect.origin.y, (int)scaledRect.size.width, (int)scaledRect.size.height]]]; + } + + if (flipImage) { + [newQueries addObject:[[NSURLQueryItem alloc] initWithName:@"flip" value:@"lr"]]; + } + + // Temporary. We will stop stripping parameters after server stops sending bad values... + components.query = nil; + components.queryItems = nil; + + if (newQueries.count > 0) { + if (components.queryItems.count > 0) { + + // Ensure not to have duplicate query parameters. (Replace server sent parameters with local). + NSMutableDictionary *duplicateKiller = [NSMutableDictionary dictionaryWithCapacity:components.queryItems.count + newQueries.count]; + [components.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + // Only add if there are values. + if (obj.value && obj.name) { + [duplicateKiller setObject:obj forKey:obj.name]; + } + }]; + [newQueries enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if (obj.name) { + [duplicateKiller setObject:obj forKey:obj.name]; + } + }]; + components.queryItems = [duplicateKiller allValues]; + } else { + components.queryItems = newQueries; + } + } + } + return [components URL]; +} + +- (void)getImage:(nonnull NSString *)pathOrName useWidth:(BOOL)useWidth widthForS7:(NSInteger)widthForS7 useHeight:(BOOL)useHeight heightForS7:(NSInteger)heightForS7 localFallbackImageName:(nullable NSString *)fallbackImageName completionHandler:(nonnull void (^)(UIImage * _Nullable))completionHandler { + [self getImage:pathOrName useWidth:useWidth widthForS7:widthForS7 useHeight:useHeight heightForS7:heightForS7 format:nil localFallbackImageName:fallbackImageName completionHandler:completionHandler]; +} + +- (void)getImage:(nonnull NSString *)pathOrName useWidth:(BOOL)useWidth widthForS7:(NSInteger)widthForS7 useHeight:(BOOL)useHeight heightForS7:(NSInteger)heightForS7 format:(nullable NSString *)format localFallbackImageName:(nullable NSString *)fallbackImageName completionHandler:(nonnull void (^)(UIImage * _Nullable))completionHandler { + [self getImage:pathOrName useWidth:useWidth widthForS7:widthForS7 useHeight:useHeight heightForS7:heightForS7 format:format finalRect:CGRectNull flipImage:NO localFallbackImageName:fallbackImageName completionHandler:completionHandler]; +} + +- (void)getGif:(nonnull NSString *)pathOrName useWidth:(BOOL)useWidth widthForS7:(NSInteger)widthForS7 useHeight:(BOOL)useHeight heightForS7:(NSInteger)heightForS7 format:(nullable NSString *)format localFallbackImageName:(nullable NSString *)fallbackImageName completionHandler:(nonnull void (^)(NSData * _Nullable, UIImage * _Nullable))completionHandler { + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + NSData *imageData = nil; + if (pathOrName.length > 0) { + + if([pathOrName containsString:@"http"]) { + + // Gets the full path + NSURL *s7URL = [self handleS7Path:pathOrName useWidth:useWidth widthForS7:widthForS7 useHeight:useHeight heightForS7:heightForS7 format:format finalRect:CGRectNull flipImage:NO]; + if (s7URL) { + + // Check in memory cache + imageData = [self getCachedDataWithName:s7URL.absoluteString]; + + if (!imageData) { + imageData = [[MFFreebeeHandler sharedHandler] freebee_dataWithContentsOfURL:s7URL]; + if (imageData) { + [self addDataToCache:imageData withName:s7URL.absoluteString]; + } + } + } + } else { + + // Try Local. Check in memory cache + imageData = [self getCachedDataWithName:pathOrName]; + + if (!imageData) { + imageData = [NSData dataWithContentsOfFile:[[self bundleToUseForImages] pathForResource:pathOrName ofType:@"gif"]]; + if (imageData) { + [self addDataToCache:imageData withName:pathOrName]; + } + } + } + } + + // Set to the fallback image. + UIImage *fallbackImage = nil; + if (!imageData && fallbackImageName) { + + // Grab fallback from cache + fallbackImage = [self getCachedImageWithName:fallbackImageName]; + if (!fallbackImage) { + + fallbackImage = [UIImage imageNamed:fallbackImageName inBundle:[self bundleToUseForImages] compatibleWithTraitCollection:nil]; + if (fallbackImage) { + + // Cache the fallback image if not already cached. + [self addImageToCache:fallbackImage withName:fallbackImageName]; + } + } + } + + completionHandler(imageData,fallbackImage); + }); +} + +- (void)getCroppedImage:(nonnull NSString *)pathOrName useWidth:(BOOL)useWidth widthForS7:(NSInteger)widthForS7 useHeight:(BOOL)useHeight heightForS7:(NSInteger)heightForS7 finalRect:(CGRect)finalRect flipImage:(BOOL)flip localFallbackImageName:(nullable NSString *)fallbackImageName completionHandler:(nonnull void (^)(UIImage * _Nullable))completionHandler { + + [self getImage:pathOrName useWidth:useWidth widthForS7:widthForS7 useHeight:useHeight heightForS7:heightForS7 format:nil finalRect:finalRect flipImage:flip localFallbackImageName:fallbackImageName completionHandler:completionHandler]; +} + +- (void)getImage:(nonnull NSString *)pathOrName useWidth:(BOOL)useWidth widthForS7:(NSInteger)widthForS7 useHeight:(BOOL)useHeight heightForS7:(NSInteger)heightForS7 format:(nullable NSString *)format finalRect:(CGRect)finalRect flipImage:(BOOL)flip localFallbackImageName:(nullable NSString *)fallbackImageName completionHandler:(nonnull void (^)(UIImage * _Nullable))completionHandler { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + UIImage *img = nil; + if (pathOrName.length > 0) { + + // try the url if it's a url. + if (pathOrName.length >=4 && [[pathOrName substringToIndex:4] isEqualToString:@"http"]) { + + // Gets the full path + NSURL *s7URL = [self handleS7Path:pathOrName useWidth:useWidth widthForS7:widthForS7 useHeight:useHeight heightForS7:heightForS7 format:format finalRect:finalRect flipImage:flip]; + if (s7URL) { + + // Check in memory cache + img = [self getCachedImageWithName:s7URL.absoluteString]; + + if (!img) { + NSData *imageData = [[MFFreebeeHandler sharedHandler] freebee_dataWithContentsOfURL:s7URL]; + img = [[UIImage alloc] initWithData:imageData scale:[UIScreen mainScreen].scale]; + if (img) { + [self addImageToCache:img withName:s7URL.absoluteString]; + } + } + } + } else { + + // Try native image. Check in memory cache + img = [self getCachedImageWithName:pathOrName]; + + if (!img) { + img = [UIImage imageNamed:pathOrName inBundle:[self bundleToUseForImages] compatibleWithTraitCollection:nil]; + + if (img) { + [self addImageToCache:img withName:pathOrName]; + } + } + } + } + + // Set to the fallback image. + if (!img && fallbackImageName) { + + // Grab fallback from cache + img = [self getCachedImageWithName:fallbackImageName]; + if (!img) { + + img = [UIImage imageNamed:fallbackImageName inBundle:[self bundleToUseForImages] compatibleWithTraitCollection:nil]; + if (img) { + + // Cache the fallback image if not already cached. + [self addImageToCache:img withName:fallbackImageName]; + } + } + } + completionHandler(img); + }); +} + +- (nullable UIImage *)getCachedImageWithName:(nonnull NSString *)imageName { + + if (self.imgCache && imageName) { + __block UIImage *image; + dispatch_sync(self.imageCacheQueue, ^{ + image = [self.imgCache objectForKey:imageName ofType:[UIImage class]]; + }); + return image; + } else { + return nil; + } +} + +- (nullable NSData *)getCachedDataWithName:(nonnull NSString *)imageName { + + if (self.imgCache && imageName) { + __block NSData *data; + dispatch_sync(self.imageCacheQueue, ^{ + data = [self.imgCache objectForKey:imageName ofType:[NSData class]]; + }); + return data; + } else { + return nil; + } +} + +- (void)addImageToCache:(nonnull UIImage *)image withName:(nonnull NSString *)imageName { + + if (image && imageName) { + dispatch_barrier_async(self.imageCacheQueue, ^{ + [self.imgCache setObject:image forKey:imageName]; + }); + } +} + +- (void)addDataToCache:(nonnull NSData *)data withName:(nonnull NSString *)imageName { + + if (data && imageName) { + dispatch_barrier_async(self.imageCacheQueue, ^{ + [self.imgCache setObject:data forKey:imageName]; + }); + } +} + +- (void)clearImageCache { + dispatch_barrier_async(self.imageCacheQueue, ^{ + [self.imgCache removeAllObjects]; + }); +} + +@end diff --git a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.h b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.h new file mode 100644 index 0000000..b9ed6d1 --- /dev/null +++ b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.h @@ -0,0 +1,21 @@ +// +// MVMCoreLoggingHandler.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/28/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import +#import + +#define MVMCoreLog(fmt, ...) \ +[MVMCoreLoggingHandler logDebugMessageWithDelegate:[NSString stringWithFormat:(@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__]]; + +@interface MVMCoreLoggingHandler : NSObject + ++ (void)addErrorToLog:(nonnull MVMCoreErrorObject *)errorObject; ++ (void)logDebugMessageWithDelegate:(nullable NSString *)message; ++ (void)logWithDelegateWithObject:(nullable id)object withName:(nullable NSString *)name withExtraInfo:(nullable NSDictionary *)extra; + +@end diff --git a/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.m b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.m new file mode 100644 index 0000000..9b6593c --- /dev/null +++ b/MVMCore/MVMCore/OtherHandlers/MVMCoreLoggingHandler.m @@ -0,0 +1,32 @@ +// +// MVMCoreLoggingHandler.m +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/28/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import "MVMCoreLoggingHandler.h" +#import "MVMCoreObject.h" + +@implementation MVMCoreLoggingHandler + ++ (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]; + } +} + +@end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.h b/MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.h new file mode 100644 index 0000000..05fbd92 --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.h @@ -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 "MVMCoreOperation.h" +#import + +@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 diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.m b/MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.m new file mode 100644 index 0000000..bd2287d --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.m @@ -0,0 +1,86 @@ +// +// MVMCoreDismissViewControllerOperation.m +// myverizon +// +// Created by Scott Pfeil on 9/28/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreDismissViewControllerOperation.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]; + }]; + } else { + [self markAsFinished]; + } +} + +@end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h new file mode 100644 index 0000000..49625ee --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h @@ -0,0 +1,132 @@ +// +// 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 +#import + +@class MVMCoreNavigationObject; +@class MVMCoreLoadObject; + +@interface MVMCoreNavigationHandler : NSObject + +// 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; + +// Will navigate appropriately based on the load style +- (void)navigateWithLoadObject:(nullable MVMCoreLoadObject *)loadObject viewController:(nonnull UIViewController *)viewController delegate:(nullable NSObject*)delegate completionHandler:(nullable void (^)(void))completionBlock; + +// Perform navigation with object. +- (void)startNavigationWithNavigationObject:(nonnull MVMCoreNavigationObject *)navigationObject delegate:(nullable NSObject*)delegate completionHandler:(nullable void (^)(void))completionBlock; + +// pops or dimisses as needed +- (void)removeCurrentViewController; + +#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; + +// 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*)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*)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*)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*)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*)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*)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*)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*)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*)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*)delegate completionHandler:(nullable void (^)(void))completionBlock; + +#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*)delegate completionHandler:(nullable void (^)(void))completionBlock; + +// Use this to dismiss. +- (void)dismissTopViewControllerAnimated:(BOOL)animated delegate:(nullable NSObject*)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*)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*)delegate completionHandler:(nullable void (^)(void))completionBlock; + +@end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m new file mode 100644 index 0000000..c69b22e --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m @@ -0,0 +1,298 @@ +// +// 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" + +@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; + } + return self; +} + +- (void)navigateWithLoadObject:(nullable MVMCoreLoadObject *)loadObject viewController:(nonnull UIViewController *)viewController delegate:(nullable NSObject*)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*)delegate completionHandler:(nullable void (^)(void))completionBlock { + + MVMCoreNavigationOperation *navigationOperation = [[MVMCoreNavigationOperation alloc] initWithNavigationObject:navigationObject]; + navigationOperation.delegate = delegate; + navigationOperation.completionBlock = completionBlock; + [self.navigationQueue addOperation:navigationOperation]; +} + +- (void)removeCurrentViewController { + 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)cancelNavigation { + [self.navigationQueue cancelAllOperations]; +} + +#pragma mark - Navigation Extra + +- (void)pushViewController:(nonnull UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController animated:(BOOL)animated delegate:(nullable NSObject*)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*)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*)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*)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*)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*)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*)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*)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*)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*)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]; +} + +#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*)delegate completionHandler:(nullable void (^)(void))completionBlock { + + // Present on the main navigation controller. + MVMCorePresentViewControllerOperation *operation = [[MVMCorePresentViewControllerOperation alloc] initWithPresentingViewController:(self.viewControllerToPresentOn ?: [MVMCoreObject sharedInstance].navigationController) presentedViewController:viewController animated:animated]; + operation.delegate = delegate; + operation.completionBlock = completionBlock; + [self.presentationQueue addOperation:operation]; +} + + +- (void)dismissTopViewControllerAnimated:(BOOL)animated delegate:(nullable NSObject*)delegate completionHandler:(nullable void (^)(void))completionBlock { + + // Dismiss on the main navigation controller. + MVMCoreDismissViewControllerOperation *operation = [[MVMCoreDismissViewControllerOperation alloc] initAndDismissTopViewController:[MVMCoreObject sharedInstance].navigationController animated:animated]; + operation.completionBlock = completionBlock; + [[NSOperationQueue mainQueue] addOperation:operation]; +} + +- (void)dismissViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated delegate:(nullable NSObject*)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*)delegate completionHandler:(nullable void (^)(void))completionBlock { + + // Dismiss on the main navigation controller. + MVMCoreDismissViewControllerOperation *operation = [[MVMCoreDismissViewControllerOperation alloc] initAndDismissViewController:[MVMCoreObject sharedInstance].navigationController animated:animated]; + operation.completionBlock = completionBlock; + [[NSOperationQueue mainQueue] addOperation:operation]; +} + +@end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationObject.h b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationObject.h new file mode 100644 index 0000000..71e7c72 --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationObject.h @@ -0,0 +1,26 @@ +// +// MVMCoreNavigationObject.h +// mobilefirst +// +// Created by Scott Pfeil on 4/22/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import +#import +#import + +@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; + +- (nullable instancetype)initWithViewController:(nullable UIViewController *)viewController navigationController:(nullable UINavigationController *)navigationController viewControllers:(nullable NSArray *)viewControllers animated:(BOOL)animated tryToReplaceFirst:(BOOL)tryToReplaceFirst navigationType:(NavigationType)navigationType; + +@end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationObject.m b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationObject.m new file mode 100644 index 0000000..e539357 --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationObject.m @@ -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 "MVMCoreObject.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 ?: [MVMCoreObject sharedInstance].navigationController; + self.navigationController = navigationToUse; + + self.viewControllers = viewControllers; + self.animated = animated; + self.tryToReplaceFirst = tryToReplaceFirst; + self.navigationType = navigationType; + } + return self; +} + + +@end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.h b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.h new file mode 100644 index 0000000..3bcd58d --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.h @@ -0,0 +1,22 @@ +// +// MVMCoreNavigationOperation.h +// myverizon +// +// Created by Scott Pfeil on 11/17/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// +// Handles navigation + +#import +#import "MVMCoreOperation.h" +#import "MVMCorePresentationDelegateProtocol.h" + +@class MVMCoreNavigationObject; + +@interface MVMCoreNavigationOperation : MVMCoreOperation + +@property (nullable, weak, nonatomic) NSObject *delegate; + +- (nullable instancetype)initWithNavigationObject:(nonnull MVMCoreNavigationObject *)navigationObject; + +@end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m new file mode 100644 index 0000000..9750d65 --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m @@ -0,0 +1,235 @@ +// +// 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" + +@interface MVMCoreNavigationOperation () + +@property (strong, nonatomic) MVMCoreNavigationObject *navigationObject; + +@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 ([self.navigationObject.viewController conformsToProtocol:@protocol(MVMCoreViewManagerProtocol)]) { + + // Check if page is in view manager, if so, replace it. + if ([((UIViewController *)self.navigationObject.viewController) 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.navigationObject.navigationController pushViewController:self.navigationObject.viewController animated:self.navigationObject.animated]; + } + 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.navigationObject.navigationController pushViewController:self.navigationObject.viewController animated:self.navigationObject.animated]; + } + } + 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.navigationObject.navigationController pushViewController:self.navigationObject.viewController animated:self.navigationObject.animated]; + } + } + break; + case NavigationTypePop: + { + if (self.navigationObject.navigationController.viewControllers.count > 1) { + [self.navigationObject.navigationController popViewControllerAnimated:self.navigationObject.animated]; + } + } + 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.navigationController popToViewController:self.navigationObject.viewController animated:self.navigationObject.animated]; + } else { + [self markAsFinished]; + } + } + break; + case NavigationTypePopToRoot: + { + [self.navigationObject.navigationController popToRootViewControllerAnimated:self.navigationObject.animated]; + } + break; + default: + [self markAsFinished]; + break; + } + } + }]]; +} + +- (void)setViewControllers:(NSArray *)viewControllers { + + if (![self.navigationObject.navigationController.viewControllers isEqualToArray:viewControllers]) { + [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]; + } +} + +- (void)markAsFinished { + self.navigationObject.navigationController.delegate = nil; + [super markAsFinished]; +} + +- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { + + if (self.delegate && [self.delegate respondsToSelector:@selector(navigationController:willDisplayViewController:)]) { + [self.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]; + } + [self markAsFinished]; + +} + +- (nullable id )navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { + if (self.delegate && [self.delegate respondsToSelector:@selector(navigationController:animationControllerForOperation:fromViewController:toViewController:)]) { + return [self.delegate navigationController:navigationController animationControllerForOperation:operation fromViewController:fromVC toViewController:toVC]; + } else { + return nil; + } +} + +@end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCorePresentAnimationOperation.h b/MVMCore/MVMCore/PresentationHandling/MVMCorePresentAnimationOperation.h new file mode 100644 index 0000000..fa9e862 --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCorePresentAnimationOperation.h @@ -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 "MVMCoreOperation.h" +#import "MVMCorePresentationDelegateProtocol.h" + +@interface MVMCorePresentAnimationOperation : MVMCoreOperation + +@property (nullable, weak, nonatomic) NSObject *delegate; + +- (nullable instancetype)initWithPresentingViewController:(nullable UIViewController *)presentingViewController presentedViewController:(nullable UIViewController *)presentedViewController animated:(BOOL)animated; + +@end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCorePresentAnimationOperation.m b/MVMCore/MVMCore/PresentationHandling/MVMCorePresentAnimationOperation.m new file mode 100644 index 0000000..15b60fc --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCorePresentAnimationOperation.m @@ -0,0 +1,51 @@ +// +// MVMCorePresentAnimationOperation.m +// myverizon +// +// Created by Scott Pfeil on 11/25/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// + +#import "MVMCorePresentAnimationOperation.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.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]; + } + + [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]; + } + [self markAsFinished]; + }]; +} + +@end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCorePresentViewControllerOperation.h b/MVMCore/MVMCore/PresentationHandling/MVMCorePresentViewControllerOperation.h new file mode 100644 index 0000000..7906638 --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCorePresentViewControllerOperation.h @@ -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 +#import "MVMCoreOperation.h" +#import "MVMCorePresentationDelegateProtocol.h" + +@interface MVMCorePresentViewControllerOperation : MVMCoreOperation + +@property (nullable, weak, nonatomic) NSObject *delegate; + +- (nullable instancetype)initWithPresentingViewController:(nullable UIViewController *)presentingViewController presentedViewController:(nullable UIViewController *)presentedViewController animated:(BOOL)animated; + +@end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCorePresentViewControllerOperation.m b/MVMCore/MVMCore/PresentationHandling/MVMCorePresentViewControllerOperation.m new file mode 100644 index 0000000..127b271 --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCorePresentViewControllerOperation.m @@ -0,0 +1,116 @@ +// +// 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" + +@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 { + + // 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]; + }]; + [[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 diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCorePresentationDelegateProtocol.h b/MVMCore/MVMCore/PresentationHandling/MVMCorePresentationDelegateProtocol.h new file mode 100644 index 0000000..219abd8 --- /dev/null +++ b/MVMCore/MVMCore/PresentationHandling/MVMCorePresentationDelegateProtocol.h @@ -0,0 +1,33 @@ +// +// MVMCorePresentationDelegateProtocol.h +// mobilefirst +// +// Created by Scott Pfeil on 2/16/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import +#import + +@protocol MVMCorePresentationDelegateProtocol + +@optional + +// Called when a view controller will be displayed on a navigation controller +- (void)navigationController:(nonnull UINavigationController *)navigationController willDisplayViewController:(nonnull UIViewController *)viewController; + +// Called when a view controller has been displayed on a navigation controller +- (void)navigationController:(nonnull UINavigationController *)navigationController displayedViewController:(nonnull UIViewController *)viewController; + +- (nullable id )navigationController:(nonnull UINavigationController *)navigationController + animationControllerForOperation:(UINavigationControllerOperation)operation + fromViewController:(nonnull UIViewController *)fromVC + toViewController:(nonnull UIViewController *)toVC NS_AVAILABLE_IOS(7_0); + +// Called when a view controller will be presented on another view controller +- (void)viewController:(nonnull UIViewController *)presentingViewController willPresentViewController:(nonnull UIViewController *)presentedViewController; + +// Called when a view controller did be present on another +- (void)viewController:(nonnull UIViewController *)presentingViewController didPresentViewController:(nonnull UIViewController *)presentedViewController; + +@end diff --git a/MVMCore/MVMCore/Session/MVMCoreSessionObject.h b/MVMCore/MVMCore/Session/MVMCoreSessionObject.h new file mode 100644 index 0000000..7c9eb33 --- /dev/null +++ b/MVMCore/MVMCore/Session/MVMCoreSessionObject.h @@ -0,0 +1,47 @@ +// +// MVMCoreSessionObject.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/22/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import + +@interface MVMCoreSessionObject : NSObject + +// the session used for all requests. +@property (nullable, strong, nonatomic) NSURLSession *session; + +// the session used for all Freebee requests. +@property (nullable, strong, nonatomic) NSURLSession *freeBeeSession; + +// The server base url (Until prepay is native, this will always be postpay). +@property (nullable, strong, nonatomic) NSURL *baseURL; + +// The context root to use on the base url. +@property (nullable, strong, nonatomic) NSString *contextRoot; + +// SSL error +@property (assign, nonatomic) BOOL sslCertificateExpired; +@property (assign, nonatomic) BOOL testingSSLError; +@property (assign, nonatomic) BOOL connectionCancelledForSSLError; +@property (assign, nonatomic) BOOL isClientPulling; + + +// Returns the shared instance of this singleton ++ (nullable instancetype)sharedGlobal; + +// Gets inital parameters for request parameters. +- (nullable NSDictionary *)getInitialParameters; + +// Restarts the application session state. +- (void)restartSession:(BOOL)clearAllVariables; + +// Redirect, leaving the current app experience. +- (void)redirectWithInfo:(nullable NSDictionary *)dictionary; + +// Clears the session singleton. +- (void)clearSessionObject; + +@end diff --git a/MVMCore/MVMCore/Session/MVMCoreSessionObject.m b/MVMCore/MVMCore/Session/MVMCoreSessionObject.m new file mode 100644 index 0000000..4daae8a --- /dev/null +++ b/MVMCore/MVMCore/Session/MVMCoreSessionObject.m @@ -0,0 +1,36 @@ +// +// MVMCoreSessionObject.m +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/22/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import "MVMCoreSessionObject.h" +#import "MVMCoreObject.h" + +@implementation MVMCoreSessionObject + ++ (nullable instancetype)sharedGlobal { + return [MVMCoreObject sharedInstance].session; +} + +- (nullable NSDictionary *)getInitialParameters { + return nil; +} + +- (void)restartSession:(BOOL)clearAllVariables { + // Clears the singletons of any session related data. + if (clearAllVariables) { + [[MVMCoreSessionObject sharedGlobal] clearSessionObject]; + } +} + +- (void)redirectWithInfo:(nullable NSDictionary *)dictionary { + +} + +- (void)clearSessionObject { +} + +@end diff --git a/MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.h b/MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.h new file mode 100644 index 0000000..bcc06db --- /dev/null +++ b/MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.h @@ -0,0 +1,56 @@ +// +// MVMCoreSessionTimeHandler.h +// myverizon +// +// Created by Scott Pfeil on 2/25/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// +// Handles the session timer. + +#import + +@class MVMCoreErrorObject; + +@interface MVMCoreSessionTimeHandler : NSObject + +// The time that we started the last session timer. +@property (assign, nonatomic, readonly) NSTimeInterval timeTimerStarted; + +#pragma mark - functions to override + +// Can override to provide a time until the warning shows in seconds. Set to 0 if there should be no warning. Default is 0 +- (NSTimeInterval)timeUntilWarning; + +// Can override to provide a time until the timeout happens in seconds. If there is a warning, then this value is used after the warning happens. Set to 0 if there should be no timeout. Default is 0. +- (NSTimeInterval)timeUntilTimeout; + +// Starts the timeout timer. Override to handle what happens on timeout warning. Should call super if want the timeout timer going. +- (void)sessionTimeoutWarning NS_REQUIRES_SUPER; + +// Called when the session has timed out. Override to handle what happens on timeout. Should call super. Can be called to force timeout... should never need to call unless simulating timout. +- (void)sessionTimeout:(BOOL)whileInBackground NS_REQUIRES_SUPER; + +// Keeps the session alive. A boolean for if we should show the alert if there is an error. Does nothing by default. Can override to do something. +- (void)sendKeepAliveToServer:(BOOL)notifyUserIfError; + +// Invalidates the server session and then calls the completion handler. Error may or may not populate. By default this only calls the completion handler, override to invalidate your server session as you see fit then call completion. +- (void)invalidateSession:(void (^ __nullable)(MVMCoreErrorObject * _Nullable error))completion; + +#pragma mark - Session timer functions + +// Returns the shared instance of this singleton ++ (nullable instancetype)sharedSessionHandler; + +// Starts the session timer. Should be called after every response from the server. Happens on the main thread. +- (void)startSessionTimer; + +// Should only be used in rare occassions, like on the original wifi screen. +- (void)stopSessionTimer; + +// Returns whether the app is in session. +- (BOOL)isAppInSession; + +// Resets everything. +- (void)resetState; + +@end diff --git a/MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.m b/MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.m new file mode 100644 index 0000000..6259683 --- /dev/null +++ b/MVMCore/MVMCore/Session/MVMCoreSessionTimeHandler.m @@ -0,0 +1,195 @@ +// +// MVMCoreSessionTimeHandler.m +// myverizon +// +// Created by Scott Pfeil on 2/25/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreSessionTimeHandler.h" +#import "MVMCoreAlertHandler.h" +#import "MVMCoreLoggingHandler.h" +#import "MVMCoreSessionObject.h" +#import "MVMCoreLoadHandler.h" +#import "MVMCoreLoadingOverlayHandler.h" +#import "MVMCoreGetterUtility.h" +#import "MVMCoreJSONConstants.h" +#import "MVMCoreErrorConstants.h" +#import "MVMCoreErrorObject.h" +#import "MVMCoreRequestParameters.h" +#import "NSDictionary+MFConvenience.h" +#import "MVMCoreObject.h" + +@interface MVMCoreSessionTimeHandler () + +@property (strong, nonatomic) NSTimer *sessionTimer; + +// The time that we started the last session timer. +@property (assign, nonatomic, readwrite) NSTimeInterval timeTimerStarted; + +// Keeps track of if the session is currently being timed. Used for entering from the background. +@property (assign, nonatomic) BOOL sessionBeingTimed; + +// Keeps track of if the session has already timed out. +@property (assign, nonatomic) BOOL sessionTimedOut; + +@property (assign, nonatomic) NSTimeInterval secondsUntilWarning; +@property (assign, nonatomic) NSTimeInterval secondsUntilTimeout; + +// Should be called when the app enters the background. +- (void)appEnteredBackground; + +// Should be called when the app enters the foreground. +- (void)appEnteredForeground; + +@end + +@implementation MVMCoreSessionTimeHandler + ++ (nullable instancetype)sharedSessionHandler { + return [MVMCoreObject sharedInstance].sessionHandler; +} + +- (instancetype)init { + if (self = [super init]) { + // Adds notifications for if the app entered the background/foreground. + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnteredBackground) name:UIApplicationDidEnterBackgroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnteredForeground) name:UIApplicationWillEnterForegroundNotification object:nil]; + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; +} + +#pragma mark - functions to override + +- (NSTimeInterval)timeUntilWarning { + return 0; +} + +- (NSTimeInterval)timeUntilTimeout { + return 0; +} + +- (void)sessionTimeoutWarning { + + // Starts the timeout timer + if (!fequal(0, self.secondsUntilTimeout)) { + self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:self.secondsUntilTimeout target:self selector:@selector(sessionTimeout:) userInfo:nil repeats:NO]; + } else { + [self stopSessionTimer]; + } +} + +- (void)sessionTimeout:(BOOL)whileInBackground { + self.sessionTimedOut = YES; + [self stopSessionTimer]; +} + +- (void)sendKeepAliveToServer:(BOOL)notifyUserIfError { +} + +#pragma mark - Session timer functions + +- (void)startSessionTimer { + dispatch_async(dispatch_get_main_queue(), ^(void) { + + if (!self.sessionTimedOut) { + + [self.sessionTimer invalidate]; + + self.secondsUntilWarning = [self timeUntilWarning]; + self.secondsUntilTimeout = [self timeUntilTimeout]; + if (!fequal(0, self.secondsUntilWarning) || !fequal(0, self.secondsUntilTimeout)) { + self.sessionBeingTimed = YES; + self.timeTimerStarted = [NSDate timeIntervalSinceReferenceDate]; + } + + if (!fequal(0, self.secondsUntilWarning)) { + self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:self.secondsUntilWarning target:self selector:@selector(sessionTimeoutWarning) userInfo:nil repeats:NO]; + } else if (!fequal(0, self.secondsUntilTimeout)) { + self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:self.secondsUntilTimeout target:self selector:@selector(sessionTimeout:) userInfo:nil repeats:NO]; + } + } + }); +} + +- (void)stopSessionTimer { + + // nil timer, session no longer timed. + dispatch_async(dispatch_get_main_queue(), ^(void) { + self.sessionBeingTimed = NO; + [self.sessionTimer invalidate]; + self.sessionTimer = nil; + }); +} + +- (void)appEnteredBackground { + + // Session is still being timed. Invalidates here, will start up again on enter foreground if need be. + dispatch_async(dispatch_get_main_queue(), ^(void) { + [self.sessionTimer invalidate]; + self.sessionTimer = nil; + }); +} + +- (void)appEnteredForeground { + + if (self.sessionBeingTimed || self.sessionTimedOut) { + + NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; + if ((!fequal(0, self.secondsUntilWarning) && now > self.timeTimerStarted + self.secondsUntilWarning) || (!fequal(0, self.secondsUntilTimeout) && now > self.timeTimerStarted + self.secondsUntilTimeout)) { + + // Timeout if we are passed the warning. + [[MVMCoreAlertHandler sharedAlertHandler] removeAllAlertViews]; + [self sessionTimeout:YES]; + } else if (!fequal(0, self.secondsUntilWarning)) { + + // Restart the warning timer! + NSTimeInterval timeLeftTillWarning = self.timeTimerStarted + self.secondsUntilWarning - now; + [self.sessionTimer invalidate]; + self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:timeLeftTillWarning target:self selector:@selector(sessionTimeoutWarning) userInfo:nil repeats:NO]; + } else if (!fequal(0, self.secondsUntilTimeout)) { + + // Restart the timeout timer! + NSTimeInterval timeLeftTillTimeout = self.timeTimerStarted + self.secondsUntilTimeout - now; + [self.sessionTimer invalidate]; + self.sessionTimer = [NSTimer scheduledTimerWithTimeInterval:timeLeftTillTimeout target:self selector:@selector(sessionTimeout:) userInfo:nil repeats:NO]; + } + } +} + +- (void)invalidateSession:(void (^ __nullable)(MVMCoreErrorObject * _Nullable error))completion { + completion(nil); +} + +// Checks to make sure session is still valid and that the timer is running. +- (void)revalidateSessionTimestamp { + if (self.sessionBeingTimed && !self.sessionTimedOut) { + + NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; + if ((!fequal(0, self.secondsUntilWarning) && now > self.timeTimerStarted + self.secondsUntilWarning) || (!fequal(0, self.secondsUntilTimeout) && now > self.timeTimerStarted + self.secondsUntilTimeout)) { + self.sessionTimedOut = YES; + [self.sessionTimer invalidate]; + self.sessionTimer = nil; + } else if (![self.sessionTimer isValid]) { + // Restart the session timer! + [self startSessionTimer]; + } + } +} + +- (BOOL)isAppInSession { + [self revalidateSessionTimestamp]; + return self.sessionBeingTimed && !self.sessionTimedOut; +} + +- (void)resetState { + [self stopSessionTimer]; + self.sessionTimedOut = NO; +} + +@end diff --git a/MVMCore/MVMCore/Singletons/MVMCoreObject.h b/MVMCore/MVMCore/Singletons/MVMCoreObject.h new file mode 100644 index 0000000..2a7eac5 --- /dev/null +++ b/MVMCore/MVMCore/Singletons/MVMCoreObject.h @@ -0,0 +1,45 @@ +// +// MVMCoreObject.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/21/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +@interface MVMCoreObject : NSObject + +@property (nullable, strong, nonatomic) MVMCoreSessionObject *session; +@property (nullable, strong, nonatomic) MVMCoreCache *cache; +@property (nullable, strong, nonatomic) MVMCoreViewControllerMappingObject *viewControllerMapping; +@property (nullable, strong, nonatomic) MVMCoreActionHandler *actionHandler; +@property (nullable, strong, nonatomic) MVMCoreSessionTimeHandler *sessionHandler; + +// reference to main navigation controller +@property (nullable, weak, nonatomic) UINavigationController *navigationController; + +// Reference to the top alert view +@property (nullable, weak, nonatomic) MVMCoreTopAlertView *topAlertView; + +// The delegates +@property (nullable, weak, nonatomic) id mobileFirstDelegate; +@property (nullable, weak, nonatomic) NSObject *splitViewDelegate; +@property (nullable, weak, nonatomic) id globalLoadDelegate; +@property (nullable, weak, nonatomic) id loadingProtocol; +@property (nullable, weak, nonatomic) id loggingDelegate; + +// A singleton. ++ (nullable instancetype)sharedInstance; + +@end diff --git a/MVMCore/MVMCore/Singletons/MVMCoreObject.m b/MVMCore/MVMCore/Singletons/MVMCoreObject.m new file mode 100644 index 0000000..37ed0cd --- /dev/null +++ b/MVMCore/MVMCore/Singletons/MVMCoreObject.m @@ -0,0 +1,25 @@ +// +// MVMCoreObject.m +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/21/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import "MVMCoreObject.h" + +@implementation MVMCoreObject + ++ (nullable instancetype)sharedInstance { + + static dispatch_once_t once; + static id sharedInstance; + dispatch_once(&once, ^ + { + sharedInstance = [[self alloc] init]; + }); + + return sharedInstance; +} + +@end diff --git a/MVMCore/MVMCore/Strings/en.lproj/Localizable.strings b/MVMCore/MVMCore/Strings/en.lproj/Localizable.strings new file mode 100644 index 0000000..b4ce3b2 --- /dev/null +++ b/MVMCore/MVMCore/Strings/en.lproj/Localizable.strings @@ -0,0 +1,23 @@ +/* + Localizable.strings + MVMCore + + Created by Pfeil, Scott Robert on 11/28/17. + Copyright © 2017 myverizon. All rights reserved. +*/ + +"error" = "Error"; +"Restart Key" = "Restart"; +"okCaps" = "OK"; + +"Error Message Critical Key" = "Unable to process your request at this time. Please contact Customer Service by dialing *611. Thank you."; +"Error Message Unable To Process Request Key" = "Unable to process your request. Please try again later."; + +//SSL Pinning +"SSL Error Message Prefix" = "Sorry; you're unable to login to My Verizon App at this time. Please check your network connection or try again later or visit"; +"SSL Error Message Title" = "verizonwireless.com"; +"SSL Error Message Postfix" = "(SSL Cerificate Verification Failed)."; +"SSL Update Title" = "Help us keep you safe."; +"SSL Update Message" = "Update your My Verizon App to ensure it's aligned with our latest security standards."; +"SSL Error Title" = "SSL Error"; + diff --git a/MVMCore/MVMCore/Utility/HardCodedServerResponse/MFHardCodedServerResponse.h b/MVMCore/MVMCore/Utility/HardCodedServerResponse/MFHardCodedServerResponse.h new file mode 100644 index 0000000..a1c3eb9 --- /dev/null +++ b/MVMCore/MVMCore/Utility/HardCodedServerResponse/MFHardCodedServerResponse.h @@ -0,0 +1,24 @@ +// +// MFHardCodedServerResponse.h +// mobilefirst +// +// Created by Hedden, Kyle Matthew on 9/10/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import +#import "MVMCoreRequestParameters.h" + +#define ENABLE_HARD_CODED_RESPONSE 0 && DEBUG + +#if ENABLE_HARD_CODED_RESPONSE + +@interface MFHardCodedServerResponse : NSObject + ++ (instancetype) sharedInstance; + +- (NSDictionary *) getHardCodedResponseForRequest:(MVMCoreRequestParameters *)request; + +@end + +#endif diff --git a/MVMCore/MVMCore/Utility/HardCodedServerResponse/MFHardCodedServerResponse.m b/MVMCore/MVMCore/Utility/HardCodedServerResponse/MFHardCodedServerResponse.m new file mode 100644 index 0000000..d71c797 --- /dev/null +++ b/MVMCore/MVMCore/Utility/HardCodedServerResponse/MFHardCodedServerResponse.m @@ -0,0 +1,98 @@ +// +// MFHardCodedServerResponse.m +// mobilefirst +// +// Created by Hedden, Kyle Matthew on 9/10/16. +// Copyright © 2016 Verizon Wireless. All rights reserved. +// + +#import "MFHardCodedServerResponse.h" +#import "MVMCoreRequestParameters.h" +#import "NSDictionary+MFConvenience.h" + +#if ENABLE_HARD_CODED_RESPONSE + +@implementation MFHardCodedServerResponse { + NSMutableDictionary *_jsonDictionary; + NSArray *_sortedKeys; +} + ++ (instancetype) sharedInstance { + static dispatch_once_t once; + static id sharedInstance; + + dispatch_once(&once, ^{ + sharedInstance = [[self alloc] init]; + }); + + return sharedInstance; +} + +- (instancetype) init { + if(self = [super init]) { + NSError *error; + + NSString *filePath = [[NSBundle mainBundle] pathForResource:@"canned" ofType:@"json"]; + if (filePath) { + _jsonDictionary = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:filePath] options:NSJSONReadingMutableContainers error:&error]; + for(NSString *key in [self disabledList]) { + [_jsonDictionary removeObjectForKey:key]; + } + _sortedKeys = [[_jsonDictionary allKeys] sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) { + if([obj2 hasPrefix:obj1]) { + return NSOrderedDescending; + } else { + return [obj1 localizedCaseInsensitiveCompare:obj2]; + } + }]; + if(error) { + MVMCoreLog(@"Error creating JSON!"); + } else { + //DLog(@"Canned json responses:%@", jsonObj); + } + } + } + return self; +} + +- (NSDictionary *) getHardCodedResponseForRequest:(MVMCoreRequestParameters *)request { + NSDictionary *cannedResponse = nil; + + if(_jsonDictionary) { + BOOL matchesQuery = NO; + for(NSString *urlKey in _sortedKeys) { + NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:urlKey]; + if([urlComponents.path isEqualToString:request.pageType]) { + matchesQuery = YES; + if(urlComponents.query) { + for(NSURLQueryItem *item in urlComponents.queryItems) { + NSString *requestVal = [request.parameters stringForKey:item.name]; + if(!requestVal || ![requestVal isEqualToString:item.value]) { + matchesQuery = NO; + break; + } + } + } else { + matchesQuery = NO; + cannedResponse = _jsonDictionary[urlKey]; + // keep searching to see if there is a more exact match on parameters + } + if(matchesQuery) { + cannedResponse = _jsonDictionary[urlKey]; + // found a request parameters match, accept as the best result and break out + break; + } + } + } + } + + return cannedResponse; +} + +- (NSArray *) disabledList { + return @[]; +} + +@end + +#endif diff --git a/MVMCore/MVMCore/Utility/HardCodedServerResponse/canned.json b/MVMCore/MVMCore/Utility/HardCodedServerResponse/canned.json new file mode 100644 index 0000000..781fb51 --- /dev/null +++ b/MVMCore/MVMCore/Utility/HardCodedServerResponse/canned.json @@ -0,0 +1,678 @@ +{ + "selectFeedbackSource": { + "ResponseInfo": { + "locale": "EN", + "server": "twswcmvmzad03.tdc.vzwcorp.com-srv01_nmobilefirst01", + "userMessage": "0", + "code": "00000", + "message": "0", + "mdn": "7323798012", + "buildNumber": "9633", + "type": "Success", + "requestId": "990372c1-19de-44b1-935f-6cedfbe36d41" + }, + "Page": { + "pageType": "exploreZoneRtl", + "parentPageType": "visitUsRtl", + "tab": [ + { + "actionType": "openPage", + "pageType": "exploreZoneRtl", + "extraParameters": { + "zoneId": "90", + "zoneName": "GetFitZone" + }, + "itemName": "Get Fit", + "title": "Get Fit", + "appContext": "mobileFirstSS", + "modules": [ + "GetFitZone" + ] + }, + { + "actionType": "openPage", + "pageType": "exploreZoneRtl", + "extraParameters": { + "zoneId": "91", + "zoneName": "AmplifyItZone" + }, + "itemName": "Amplify It", + "title": "Amplify It", + "appContext": "mobileFirstSS", + "modules": [ + "AmplifyItZone" + ] + }, + { + "actionType": "openPage", + "pageType": "exploreZoneRtl", + "extraParameters": { + "zoneId": "93", + "zoneName": "HomeServicesZone" + }, + "itemName": "Home Service", + "title": "Home Service", + "appContext": "mobileFirstSS", + "modules": [ + "HomeServicesZone" + ] + } + ], + "beaconids": [ + { + "tabIndex": 0, + "minor": 23233, + "zoneId": 90, + "major": 7203, + "locationCode": "W078801", + "header": null + }, + { + "tabIndex": 1, + "minor": 23424, + "zoneId": 91, + "major": 7203, + "locationCode": "W078801", + "header": null + }, + { + "tabIndex": 2, + "minor": 24016, + "zoneId": 93, + "major": 7203, + "locationCode": "W078801", + "header": null + } + ], + "title": "Title", + "message": "Please enable your Bluetooth to detect the zone you are exploring", + "currentTabIndex": 1, + "ButtonMap": { + "AsyncCallButton": { + "actionType": "openPage", + "pageType": "exploreZoneModAsyncRtl", + "tryToReplaceFirst": false, + "title": "Explore Zone Async Call", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + } + }, + "screenHeading": "Explore Products" + }, + "ModuleMap": { + "AmplifyItZone": { + "mappoint": "AmplifyItZone", + "zoneId": "91", + "name": "Amplify It", + "ResponseInfo": { + "locale": "EN", + "code": "00000", + "message": "0", + "userMessage": "0", + "type": "Success" + }, + "skuList": [ + [ + { + "deviceTitle": "Flip 2", + "stockInWarehouse": false, + "colorCode": "#003399", + "colorName": "Blue", + "isDevice": false, + "discountFlag": false, + "imageUrl": "https://mobile-edev.vzw.com/dev01/geofencing/instore/images/smart/s7/is/image/VerizonWireless/jbl-flip-2-blue-FLIPIIBLUAM-imageset?$acc-lg$", + "preSelected": false, + "qty": 349991, + "skuId": "FLIPIIBLUAM", + "ButtonMap": { + "PrimaryButton": { + "actionType": "openPage", + "pageType": "addToSharedCartRtl", + "extraParameters": { + "cmd": "add", + "cart": [ + { + "sku": "FLIPIIBLUAM", + "qty": 1 + } + ] + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Add to cart", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "MoreColors": { + "actionType": "openPage", + "pageType": "exploreZoneMoreClrsRtl", + "extraParameters": { + "sku": "FLIPIIBLUAM" + }, + "tryToReplaceFirst": false, + "title": "More colors", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "SecondaryButton": { + "actionType": "openPage", + "pageType": "getPDPBySKURtl", + "extraParameters": { + "sku": "FLIPIIBLUAM" + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Learn more", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + } + }, + "inStorePurchase": false, + "fallBackImageUrl": "ICON_DFLT_ACCESSORY_IMG", + "accessoryId": "acc2040005", + "inventoryMsg": "In stock, take it with you today.", + "priceMap": { + "netPrice": { + "price": "79.98" + }, + "discountPrice": { + "price": "0" + }, + "originalPrice": { + "price": "79.98" + } + }, + "device": false, + "sorId": "sku870003", + "stockInStore": true + }, + { + "deviceTitle": "Flip 2", + "stockInWarehouse": false, + "colorCode": "#000000", + "colorName": "Black", + "isDevice": false, + "discountFlag": false, + "imageUrl": "https://mobile-edev.vzw.com/dev01/geofencing/instore/images/smart/s7/is/image/VerizonWireless/jbl-flip-2-black-FLIPIIBLKAM-imageset?$acc-lg$", + "preSelected": true, + "qty": 349880, + "skuId": "FLIPIIBLKAM", + "ButtonMap": { + "PrimaryButton": { + "actionType": "openPage", + "pageType": "addToSharedCartRtl", + "extraParameters": { + "cmd": "add", + "cart": [ + { + "sku": "FLIPIIBLKAM", + "qty": 1 + } + ] + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Add to cart", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "MoreColors": { + "actionType": "openPage", + "pageType": "exploreZoneMoreClrsRtl", + "extraParameters": { + "sku": "FLIPIIBLKAM" + }, + "tryToReplaceFirst": false, + "title": "More colors", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "SecondaryButton": { + "actionType": "openPage", + "pageType": "getPDPBySKURtl", + "extraParameters": { + "sku": "FLIPIIBLKAM" + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Learn more", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + } + }, + "inStorePurchase": false, + "fallBackImageUrl": "ICON_DFLT_ACCESSORY_IMG", + "accessoryId": "acc2040005", + "inventoryMsg": "In stock, take it with you today.", + "priceMap": { + "netPrice": { + "price": "69.98" + }, + "discountPrice": { + "price": "0" + }, + "originalPrice": { + "price": "69.98" + } + }, + "device": false, + "sorId": "sku870008", + "stockInStore": true + }, + { + "deviceTitle": "Flip 2", + "stockInWarehouse": false, + "colorCode": "#ff0000", + "colorName": "Red", + "isDevice": false, + "discountFlag": false, + "imageUrl": "https://mobile-edev.vzw.com/dev01/geofencing/instore/images/smart/s7/is/image/VerizonWireless/jbl-flip-2-red-FLIPIIREDAM-imageset?$acc-lg$", + "preSelected": false, + "qty": 349989, + "skuId": "FLIPIIREDAM", + "ButtonMap": { + "PrimaryButton": { + "actionType": "openPage", + "pageType": "addToSharedCartRtl", + "extraParameters": { + "cmd": "add", + "cart": [ + { + "sku": "FLIPIIREDAM", + "qty": 1 + } + ] + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Add to cart", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "MoreColors": { + "actionType": "openPage", + "pageType": "exploreZoneMoreClrsRtl", + "extraParameters": { + "sku": "FLIPIIREDAM" + }, + "tryToReplaceFirst": false, + "title": "More colors", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "SecondaryButton": { + "actionType": "openPage", + "pageType": "getPDPBySKURtl", + "extraParameters": { + "sku": "FLIPIIREDAM" + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Learn more", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + } + }, + "inStorePurchase": false, + "fallBackImageUrl": "ICON_DFLT_ACCESSORY_IMG", + "accessoryId": "acc2040005", + "inventoryMsg": "In stock, take it with you today.", + "priceMap": { + "netPrice": { + "price": "79.98" + }, + "discountPrice": { + "price": "0" + }, + "originalPrice": { + "price": "79.98" + } + }, + "device": false, + "sorId": "sku870005", + "stockInStore": true + }, + { + "deviceTitle": "Flip 2", + "stockInWarehouse": false, + "colorCode": "#FFFF00", + "colorName": "Yellow", + "isDevice": false, + "discountFlag": false, + "imageUrl": "https://mobile-edev.vzw.com/dev01/geofencing/instore/images/smart/s7/is/image/VerizonWireless/jbl-flip-2-yellow-FLIPIIYELAM-imageset?$acc-lg$", + "preSelected": false, + "qty": 350000, + "skuId": "FLIPIIYELAM", + "ButtonMap": { + "PrimaryButton": { + "actionType": "openPage", + "pageType": "addToSharedCartRtl", + "extraParameters": { + "cmd": "add", + "cart": [ + { + "sku": "FLIPIIYELAM", + "qty": 1 + } + ] + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Add to cart", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "MoreColors": { + "actionType": "openPage", + "pageType": "exploreZoneMoreClrsRtl", + "extraParameters": { + "sku": "FLIPIIYELAM" + }, + "tryToReplaceFirst": false, + "title": "More colors", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "SecondaryButton": { + "actionType": "openPage", + "pageType": "getPDPBySKURtl", + "extraParameters": { + "sku": "FLIPIIYELAM" + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Learn more", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + } + }, + "inStorePurchase": false, + "fallBackImageUrl": "ICON_DFLT_ACCESSORY_IMG", + "accessoryId": "acc2040005", + "inventoryMsg": "In stock, take it with you today.", + "priceMap": { + "netPrice": { + "price": "79.98" + }, + "discountPrice": { + "price": "0" + }, + "originalPrice": { + "price": "79.98" + } + }, + "device": false, + "sorId": "sku870006", + "stockInStore": true + }, + { + "deviceTitle": "Flip 2", + "stockInWarehouse": false, + "colorCode": "#FFFFFF", + "colorName": "White", + "isDevice": false, + "discountFlag": false, + "imageUrl": "https://mobile-edev.vzw.com/dev01/geofencing/instore/images/smart/s7/is/image/VerizonWireless/jbl-flip-2-white-FLIPIIWHTAM-imageset?$acc-lg$", + "preSelected": false, + "qty": 349999, + "skuId": "FLIPIIWHTAM", + "ButtonMap": { + "PrimaryButton": { + "actionType": "openPage", + "pageType": "addToSharedCartRtl", + "extraParameters": { + "cmd": "add", + "cart": [ + { + "sku": "FLIPIIWHTAM", + "qty": 1 + } + ] + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Add to cart", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "MoreColors": { + "actionType": "openPage", + "pageType": "exploreZoneMoreClrsRtl", + "extraParameters": { + "sku": "FLIPIIWHTAM" + }, + "tryToReplaceFirst": false, + "title": "More colors", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "SecondaryButton": { + "actionType": "openPage", + "pageType": "getPDPBySKURtl", + "extraParameters": { + "sku": "FLIPIIWHTAM" + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Learn more", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + } + }, + "inStorePurchase": false, + "fallBackImageUrl": "ICON_DFLT_ACCESSORY_IMG", + "accessoryId": "acc2040005", + "inventoryMsg": "In stock, take it with you today.", + "priceMap": { + "netPrice": { + "price": "79.98" + }, + "discountPrice": { + "price": "0" + }, + "originalPrice": { + "price": "79.98" + } + }, + "device": false, + "sorId": "sku870007", + "stockInStore": true + } + ], + [ + { + "deviceTitle": "QuietComfort 25 Acoustic Noise Cancelling headphones - Apple devices", + "stockInWarehouse": false, + "colorCode": "#FFFFFF", + "colorName": "White", + "isDevice": false, + "discountFlag": false, + "imageUrl": "https://mobile-edev.vzw.com/dev01/geofencing/instore/images/smart/s7/is/image/VerizonWireless/bose-quietcomfort-25-acoustic-noise-cancelling-headphones-white-iset-715053-0020?$acc-lg$", + "preSelected": false, + "qty": 99986, + "skuId": "715053-0020", + "ButtonMap": { + "PrimaryButton": { + "actionType": "openPage", + "pageType": "addToSharedCartRtl", + "extraParameters": { + "cmd": "add", + "cart": [ + { + "sku": "715053-0020", + "qty": 1 + } + ] + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Add to cart", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "MoreColors": { + "actionType": "openPage", + "pageType": "exploreZoneMoreClrsRtl", + "extraParameters": { + "sku": "715053-0020" + }, + "tryToReplaceFirst": false, + "title": "More colors", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "SecondaryButton": { + "actionType": "openPage", + "pageType": "getPDPBySKURtl", + "extraParameters": { + "sku": "715053-0020" + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Learn more", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + } + }, + "inStorePurchase": false, + "fallBackImageUrl": "ICON_DFLT_ACCESSORY_IMG", + "accessoryId": "acc4120007", + "inventoryMsg": "In stock, take it with you today.", + "priceMap": { + "netPrice": { + "price": "299.99" + }, + "discountPrice": { + "price": "0" + }, + "originalPrice": { + "price": "299.99" + } + }, + "device": false, + "sorId": "sku1450111", + "stockInStore": true + }, + { + "deviceTitle": "QuietComfort 25 Acoustic Noise Cancelling headphones - Apple devices", + "stockInWarehouse": false, + "colorCode": "#000000", + "colorName": "Black", + "isDevice": false, + "discountFlag": false, + "imageUrl": "https://mobile-edev.vzw.com/dev01/geofencing/instore/images/smart/s7/is/image/VerizonWireless/bose-quietcomfort-25-acoustic-noise-cancelling-headphones-black-iset-715053-0010?$acc-lg$", + "preSelected": true, + "qty": 99974, + "skuId": "715053-0010", + "ButtonMap": { + "PrimaryButton": { + "actionType": "openPage", + "pageType": "addToSharedCartRtl", + "extraParameters": { + "cmd": "add", + "cart": [ + { + "sku": "715053-0010", + "qty": 1 + } + ] + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Add to cart", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "MoreColors": { + "actionType": "openPage", + "pageType": "exploreZoneMoreClrsRtl", + "extraParameters": { + "sku": "715053-0010" + }, + "tryToReplaceFirst": false, + "title": "More colors", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + }, + "SecondaryButton": { + "actionType": "openPage", + "pageType": "getPDPBySKURtl", + "extraParameters": { + "sku": "715053-0010" + }, + "presentationStyle": "push", + "tryToReplaceFirst": false, + "title": "Learn more", + "selected": false, + "disableAction": false, + "appContext": "mobileFirstSS", + "isSelected": false + } + }, + "inStorePurchase": false, + "fallBackImageUrl": "ICON_DFLT_ACCESSORY_IMG", + "accessoryId": "acc4120007", + "inventoryMsg": "In stock, take it with you today.", + "priceMap": { + "netPrice": { + "price": "299.99" + }, + "discountPrice": { + "price": "0" + }, + "originalPrice": { + "price": "299.99" + } + }, + "device": false, + "sorId": "sku1450115", + "stockInStore": true + } + ] + ] + } + } + } +} diff --git a/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.h b/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.h new file mode 100644 index 0000000..6c0a6ed --- /dev/null +++ b/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.h @@ -0,0 +1,38 @@ +// +// MVMCoreActionUtility.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/14/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import +#import + +@interface MVMCoreActionUtility : NSObject + +// Takes a dictionary and formats it into a json string. ++ (nonnull NSString *)formatDictionaryAsJSONString:(nonnull NSDictionary *)dictionary; + +// Links away to safari or another app. Returns if successful. ++ (BOOL)linkAway:(nullable NSString *)safariURLString appURLString:(nullable NSString *)appURLString; + +// Hashes the passed in string with SHA256. Returns the hashed string. ++ (nullable NSString *)hashStringWithSHA256:(nullable NSString *)stringToHash; + +#pragma mark - keyboard + +/** Handles setting the content inset for a passed in scroll view. + * @param notification pass in the notification from the UIKeyboardWillShowNotification. + * @param scrollView the scroll view to set the content inset. + * @param viewController Pass in the main view that the scroll view is in for calculations. + * @param rectToScrollTo Pass in a block that returns the rectangle. **/ ++ (void)setScrollViewInsetForKeyboardShow:(nonnull NSNotification *)notification scrollView:(nonnull UIScrollView *)scrollView viewController:(nonnull UIViewController *)viewController rectToScrollTo:(nonnull CGRect (^)(void))rectToScrollTo; + +/** Handles setting the content inset for a passed in scroll view. + * @param notification pass in the notification from the UIKeyboardWillShowNotification. + * @param scrollView the scroll view to set the content inset. + * @param contentInset The pre-keyboard inset to set after hiding. **/ ++ (void)setScrollViewInsetForKeyboardHide:(nonnull NSNotification *)notification scrollView:(nonnull UIScrollView *)scrollView viewController:(nonnull UIViewController *)viewController contentInset:(UIEdgeInsets)contentInset; + +@end diff --git a/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.m b/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.m new file mode 100644 index 0000000..857d27a --- /dev/null +++ b/MVMCore/MVMCore/Utility/Helpers/MVMCoreActionUtility.m @@ -0,0 +1,165 @@ +// +// MVMCoreActionUtility.m +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/14/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import "MVMCoreActionUtility.h" +#import "MVMCoreDispatchUtility.h" +#import +#import + +@implementation MVMCoreActionUtility + ++ (nonnull NSString *)formatArrayAsJSONString:(nonnull NSArray *)array { + + NSMutableString *string = [NSMutableString stringWithString:@"["]; + for (NSUInteger i = 0; i < [array count]; i++) { + id object = [array objectAtIndex:i]; + + if ([object isKindOfClass:[NSString class]]) { + [string appendFormat:@"\"%@\"",object]; + } else if ([object isKindOfClass:[NSDictionary class]]) { + [string appendString:[self formatDictionaryAsJSONString:object]]; + } else if ([object isKindOfClass:[NSArray class]]) { + [string appendString:[self formatArrayAsJSONString:object]]; + } else if ([object isKindOfClass:[NSNull class]]) { + [string appendString:@"\"\""]; + } else if ([object isKindOfClass:[NSNumber class]]) { + [string appendString:[object stringValue]]; + } + + if (i < [array count] - 1) { + [string appendString:@","]; + } + } + [string appendString:@"]"]; + + return string; +} + ++ (nonnull NSString *)formatDictionaryAsJSONString:(nonnull NSDictionary *)dictionary { + + NSMutableString *string = [NSMutableString stringWithString:@"{"]; + NSInteger count = [dictionary count]; + id __unsafe_unretained objects[count]; + id __unsafe_unretained keys[count]; + [dictionary getObjects:objects andKeys:keys]; + for (NSUInteger i = 0; i < count; i++) { + id object = objects[i]; + NSString *key = keys[i]; + [string appendFormat:@"\"%@\":",key]; + if ([object isKindOfClass:[NSString class]]) { + [string appendFormat:@"\"%@\"",object]; + } else if ([object isKindOfClass:[NSDictionary class]]) { + [string appendString:[self formatDictionaryAsJSONString:object]]; + } else if ([object isKindOfClass:[NSArray class]]) { + [string appendString:[self formatArrayAsJSONString:object]]; + } else if ([object isKindOfClass:[NSNull class]]) { + [string appendString:@"\"\""]; + } else if ([object isKindOfClass:[NSNumber class]]) { + [string appendString:[object stringValue]]; + } + + if (i < count - 1) { + [string appendString:@","]; + } + } + [string appendString:@"}"]; + + return string; +} + ++ (BOOL)linkAway:(nullable NSString *)safariURLString appURLString:(nullable NSString *)appURLString { + + __block BOOL successful = YES; + [MVMCoreDispatchUtility performBlockOnMainThread:^{ + + if (appURLString.length && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:appURLString]]) { + + // If we receive an app url to open, open if we can. + [[self class] openURL:[NSURL URLWithString:appURLString] completionHandler:nil]; + } else if(safariURLString.length && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:safariURLString]]) { + + // Open url. + [[self class] openURL:[NSURL URLWithString:safariURLString] completionHandler:nil]; + } else { + successful = NO; + } + + }]; + return successful; +} + ++ (void)openURL:(nullable NSURL *)url completionHandler:(void (^ __nullable)(BOOL success))completion{ + if (@available(iOS 10.0, *)) { + [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:completion]; + } else { + BOOL successful = [[UIApplication sharedApplication] openURL:url]; + if (completion != nil){ + completion(successful); + } + } +} + ++ (NSString *)hashStringWithSHA256:(NSString *)stringToHash { + + NSString *hashedString = nil; + if (stringToHash) { + NSData *data = [stringToHash dataUsingEncoding:NSUTF8StringEncoding]; + + unsigned char hashedBytes[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256([data bytes],(uint)[data length], hashedBytes); + + NSData *hashedData = [NSData dataWithBytes:hashedBytes length:CC_SHA256_DIGEST_LENGTH]; + hashedString = [hashedData base64EncodedStringWithOptions:0]; + } + return hashedString; +} + +#pragma mark - keyboard + ++ (void)setScrollViewInsetForKeyboardShow:(nonnull NSNotification *)notification scrollView:(nonnull UIScrollView *)scrollView viewController:(nonnull UIViewController *)viewController rectToScrollTo:(nonnull CGRect (^)(void))rectToScrollTo { + + NSDictionary *info = [notification userInfo]; + + CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; + + // Gets the scrollview end point in the window coordinate space to see if the keyboard will cover the scroll view at all. (This conversion allows the scrollview to be anyhwere in the hierarchy). + CGPoint scrollEndPointInWindowSpace = [[UIApplication sharedApplication].keyWindow convertPoint:CGPointMake(CGRectGetMinX(scrollView.frame), CGRectGetMaxY(scrollView.frame)) fromView:[scrollView superview]]; + + // Also takes into account the current content inset. (Even if the keyboard covers the frame, it might not matter if the bottom content inset is set to above the keyboard anyway). + if (scrollEndPointInWindowSpace.y - scrollView.contentInset.bottom > CGRectGetMinY(keyboardRect)) { + + // Sets the scrollview insets to account for the portion covered by the keyboard (as apposed to changing the frame size). + UIEdgeInsets contentInsets = UIEdgeInsetsMake(scrollView.contentInset.top, scrollView.contentInset.left, scrollEndPointInWindowSpace.y - CGRectGetMinY(keyboardRect), scrollView.contentInset.right); + scrollView.contentInset = contentInsets; + scrollView.scrollIndicatorInsets = contentInsets; + } + + // Looks smoother if animated + [UIView animateWithDuration:[[notification.userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue] delay:0 options:[[notification.userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue] animations:^{ + + // Gets the field view rect in the scroll view coordinate space and scrolls to it. (This conversion allows the field to be anyhwere in the scrollviews subview hierarchy). + CGRect fieldRectInScrollViewSpace = rectToScrollTo(); + [scrollView scrollRectToVisible:fieldRectInScrollViewSpace animated:YES]; + [viewController updateViewConstraints]; + [viewController.view layoutIfNeeded]; + } completion:NULL]; +} + ++ (void)setScrollViewInsetForKeyboardHide:(nonnull NSNotification *)notification scrollView:(nonnull UIScrollView *)scrollView viewController:(nonnull UIViewController *)viewController contentInset:(UIEdgeInsets)contentInset { + + [UIView animateWithDuration:[[notification.userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue] delay:0 options:[[notification.userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue] animations:^{ + + // Go back to how it was before the user touched a text field. + scrollView.contentInset = contentInset; + scrollView.scrollIndicatorInsets = contentInset; + [viewController updateViewConstraints]; + [viewController.view layoutIfNeeded]; + } completion:NULL]; +} + +@end diff --git a/MVMCore/MVMCore/Utility/Helpers/MVMCoreDispatchUtility.h b/MVMCore/MVMCore/Utility/Helpers/MVMCoreDispatchUtility.h new file mode 100644 index 0000000..3cdffb3 --- /dev/null +++ b/MVMCore/MVMCore/Utility/Helpers/MVMCoreDispatchUtility.h @@ -0,0 +1,21 @@ +// +// MVMCoreDispatchUtility.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/9/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import + +@interface MVMCoreDispatchUtility : NSObject + +// Ensures the block is performed on the main thread. ++ (void)performBlockOnMainThread:(nonnull void (^)(void))block; ++ (void)performSyncBlockOnMainThread:(nonnull void (^)(void))block; + +// Ensures the block is performed in the background. ++ (void)performBlockInBackground:(nonnull void (^)(void))block; ++ (void)performSyncBlockInBackground:(nonnull void (^)(void))block; + +@end diff --git a/MVMCore/MVMCore/Utility/Helpers/MVMCoreDispatchUtility.m b/MVMCore/MVMCore/Utility/Helpers/MVMCoreDispatchUtility.m new file mode 100644 index 0000000..8ff96fb --- /dev/null +++ b/MVMCore/MVMCore/Utility/Helpers/MVMCoreDispatchUtility.m @@ -0,0 +1,45 @@ +// +// MVMCoreDispatchUtility.m +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/9/17. +// Copyright © 2017 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreDispatchUtility.h" + +@implementation MVMCoreDispatchUtility + ++ (void)performBlockOnMainThread:(nonnull void (^)(void))block { + if ([NSThread mainThread] == [NSThread currentThread]) { + block(); + } else { + dispatch_async(dispatch_get_main_queue(),block); + } +} + ++ (void)performSyncBlockOnMainThread:(nonnull void (^)(void))block { + if ([NSThread mainThread] == [NSThread currentThread]) { + block(); + } else { + dispatch_sync(dispatch_get_main_queue(), block); + } +} + ++ (void)performBlockInBackground:(nonnull void (^)(void))block { + if ([NSThread mainThread] != [NSThread currentThread]) { + block(); + } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),block); + } +} + ++ (void)performSyncBlockInBackground:(nonnull void (^)(void))block { + if ([NSThread mainThread] != [NSThread currentThread]) { + block(); + } else { + dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); + } +} + +@end diff --git a/MVMCore/MVMCore/Utility/Helpers/MVMCoreGetterUtility.h b/MVMCore/MVMCore/Utility/Helpers/MVMCoreGetterUtility.h new file mode 100644 index 0000000..5bf0375 --- /dev/null +++ b/MVMCore/MVMCore/Utility/Helpers/MVMCoreGetterUtility.h @@ -0,0 +1,22 @@ +// +// MVMCoreGetterUtility.h +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/13/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import +#import + +#define fequal(a,b) (fabs((a) - (b)) < FLT_EPSILON) + +@interface MVMCoreGetterUtility : NSObject + +// The bundle for this framework ++ (nullable NSBundle *)bundleForMVMCore; + +// Returns the hardcoded string from the string file. ++ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key; + +@end diff --git a/MVMCore/MVMCore/Utility/Helpers/MVMCoreGetterUtility.m b/MVMCore/MVMCore/Utility/Helpers/MVMCoreGetterUtility.m new file mode 100644 index 0000000..82c2c04 --- /dev/null +++ b/MVMCore/MVMCore/Utility/Helpers/MVMCoreGetterUtility.m @@ -0,0 +1,26 @@ +// +// MVMCoreGetterUtility.m +// MVMCore +// +// Created by Pfeil, Scott Robert on 11/13/17. +// Copyright © 2017 myverizon. All rights reserved. +// + +#import "MVMCoreGetterUtility.h" +#import +#import "MVMCoreConstants.h" +#import + +@implementation MVMCoreGetterUtility + +// The bundle for this framework ++ (nullable NSBundle *)bundleForMVMCore { + return [NSBundle bundleWithIdentifier:@"com.vzw.MVMCore"]; +} + ++ (nullable NSString *)hardcodedStringWithKey:(nonnull NSString *)key { + // If the app language is not english... force load from the english file anyway. + return [[NSBundle bundleWithPath:[[MVMCoreGetterUtility bundleForMVMCore] pathForResource:@"en" ofType:@"lproj"]] localizedStringForKey:key value:@"" table:nil]; +} + +@end diff --git a/MVMCore/MVMCore/Utility/MVMCoreErrorObject.h b/MVMCore/MVMCore/Utility/MVMCoreErrorObject.h new file mode 100644 index 0000000..86fdda0 --- /dev/null +++ b/MVMCore/MVMCore/Utility/MVMCoreErrorObject.h @@ -0,0 +1,65 @@ +// +// MVMCoreErrorObject.h +// myverizon +// +// Created by Scott Pfeil on 3/6/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// +// An object that keeps track of error details. + +#import +#import + +@interface MVMCoreErrorObject : NSObject + +// error information +@property (nullable, strong, nonatomic) NSString *title; +@property (nullable, strong, nonatomic) NSString *messageToDisplay; +@property (nullable, strong, nonatomic) NSString *messageToLog; +@property (nullable, strong, nonatomic) NSString *domain; +@property (nullable, strong, nonatomic) NSString *location; +@property (nullable, strong, nonatomic) NSDate *date; +@property (nullable, strong, nonatomic) NSString *systemDomain; +@property (nonatomic) NSInteger code; +@property (nonatomic) UIApplicationState applicationState; + +// For the crash log. +@property (nullable, strong, nonatomic) NSDictionary *crashLog; + +// A flag for if the error is silent or not (displayed to the user) +@property (nonatomic) BOOL silentError; + +// A flag for if the error should be logged. (Used in the standardrequestprotocol flow). +@property (nonatomic) BOOL logError; + +// A flag for keeping track if this is an error screen error. +@property (nonatomic) BOOL errorScreenError; +@property (nonatomic) BOOL nativeDrivenErrorScreen; +// For SSL Pinning +// A flag for keeping track if this is an SSL error. +@property (nonatomic) BOOL sslError; + +// Creates an object with the given error info. +- (nullable instancetype)initWithTitle:(nullable NSString *)title message:(nullable NSString *)message code:(NSInteger)code domain:(nullable NSString *)domain location:(nullable NSString *)location; +- (nullable instancetype)initWithTitle:(nullable NSString *)title messageToLog:(nullable NSString *)messageToLog code:(NSInteger)code domain:(nullable NSString *)domain location:(nullable NSString *)location; +- (nullable instancetype)initWithTitle:(nullable NSString *)title message:(nullable NSString *)message messageToLog:(nullable NSString *)messageToLog code:(NSInteger)code domain:(nullable NSString *)domain location:(nullable NSString *)location; + +// Creates and returns an error object for the load object. May be nil. ++ (nullable instancetype)createErrorObjectForErrorInfo:(nullable NSDictionary *)errorInfo location:(nullable NSString *)location; + +// Creates and returns an error object for the NSError. May be nil. ++ (nullable instancetype)createErrorObjectForNSError:(nonnull NSError *)error location:(nullable NSString *)location; + +// Initializes the error object with a given crash log. +- (nullable instancetype)initWithCrashLog:(nullable NSDictionary *)crashLog; + +// Returns the error code as readable string and appends a signifier for domain. +- (nonnull NSString *)stringErrorCode; + +// Returns the error code as readable string and appends a signifier for domain. ++ (nonnull NSString *)stringErrorCode:(NSInteger)code domain:(nonnull NSString *)domain; + +// Returns the state of the application when the error occurred. +- (nonnull NSString *)stringApplicationState; + +@end diff --git a/MVMCore/MVMCore/Utility/MVMCoreErrorObject.m b/MVMCore/MVMCore/Utility/MVMCoreErrorObject.m new file mode 100644 index 0000000..552893a --- /dev/null +++ b/MVMCore/MVMCore/Utility/MVMCoreErrorObject.m @@ -0,0 +1,142 @@ +// +// MVMCoreErrorObject.m +// myverizon +// +// Created by Scott Pfeil on 3/6/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreErrorObject.h" +#import "MVMCoreErrorConstants.h" +#import "MVMCoreGetterUtility.h" +#import "NSDictionary+MFConvenience.h" +#import "MVMCoreJSONConstants.h" +#import "MVMCoreHardcodedStringsConstants.h" + +@implementation MVMCoreErrorObject + +- (nullable instancetype)initWithTitle:(nullable NSString *)title message:(nullable NSString *)message code:(NSInteger)code domain:(nullable NSString *)domain location:(nullable NSString *)location { + + if (self = [super init]) { + // Initialization code + self.title = title; + self.messageToDisplay = message; + self.code = code; + self.domain = domain; + self.location = location; + self.date = [NSDate date]; + self.applicationState = [UIApplication sharedApplication].applicationState; + + // We don't log server errors. + if (![domain isEqualToString:ErrorDomainServer]) { + self.logError = YES; + + // Native and system errors have an error screen. + self.errorScreenError = YES; + self.nativeDrivenErrorScreen = YES; + } else { + self.logError = NO; + } + } + return self; +} + +- (nullable instancetype)initWithTitle:(nullable NSString *)title messageToLog:(nullable NSString *)messageToLog code:(NSInteger)code domain:(nullable NSString *)domain location:(nullable NSString *)location { + + if (self = [super init]) { + // Initialization code + self.title = title; + self.messageToLog = messageToLog; + self.code = code; + self.domain = domain; + self.location = location; + self.date = [NSDate date]; + self.applicationState = [UIApplication sharedApplication].applicationState; + + // We don't log server errors. + if (![domain isEqualToString:ErrorDomainServer]) { + self.logError = YES; + + // Native and system errors have an error screen. + self.errorScreenError = YES; + self.nativeDrivenErrorScreen = YES; + } else { + self.logError = NO; + } + } + return self; +} + +- (nullable instancetype)initWithTitle:(nullable NSString *)title message:(nullable NSString *)message messageToLog:(nullable NSString *)messageToLog code:(NSInteger)code domain:(nullable NSString *)domain location:(nullable NSString *)location { + + if (self = [self initWithTitle:title message:message code:code domain:domain location:location]) { + // Initialization code + self.messageToLog = messageToLog; + } + return self; +} + ++ (nullable instancetype)createErrorObjectForErrorInfo:(nullable NSDictionary *)errorInfo location:(nullable NSString *)location { + + NSInteger errorCode = [[errorInfo string:KeyCode] integerValue]; + NSString *type = [errorInfo string:KeyType]; + if (![ValueTypeSuccess isEqualToString:type]) { + + MVMCoreErrorObject *error = [[MVMCoreErrorObject alloc] initWithTitle:[errorInfo stringForKey:KeyErrorHeading] message:[errorInfo stringForKey:KeyUserMessage] messageToLog:[errorInfo stringForKey:KeyMessage] code:errorCode domain:ErrorDomainServer location:location]; + if ([ValueTypeErrorScreen isEqualToString:type]) { + + // If this is a server error screen, there should be no additional alerts... It will be handled by the load handler. + error.errorScreenError = YES; + } + return error; + } else { + return nil; + } +} + ++ (nullable instancetype)createErrorObjectForNSError:(nonnull NSError *)error location:(nullable NSString *)location { + + MVMCoreErrorObject *errorObject = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[error localizedDescription] code:[error code] domain:ErrorDomainSystem location:location]; + if ([error.domain isEqualToString:NSURLErrorDomain]) { + errorObject.errorScreenError = YES; + errorObject.nativeDrivenErrorScreen = YES; + errorObject.systemDomain = error.domain; + } + return errorObject; +} + +- (nullable instancetype)initWithCrashLog:(nullable NSDictionary *)crashLog { + if (self = [super init]) { + self.crashLog = crashLog; + self.logError = NO; + } + return self; +} + +- (NSString *)stringErrorCode { + return [MVMCoreErrorObject stringErrorCode:self.code domain:self.domain]; +} + +- (NSString *)stringApplicationState { + switch (self.applicationState) { + case UIApplicationStateBackground: + return @"background"; + case UIApplicationStateInactive: + return @"inactive"; + default: + return @"active"; + } +} + ++ (nonnull NSString *)stringErrorCode:(NSInteger)code domain:(nonnull NSString *)domain { + if (domain == ErrorDomainNative) { + domain = @"N"; + } else if (domain == ErrorDomainSystem) { + domain = @"A"; + } else { + domain = @""; + } + return [NSString stringWithFormat:@"%li%@",(long)code,domain]; +} + +@end diff --git a/MVMCore/MVMCore/Utility/MVMCoreOperation.h b/MVMCore/MVMCore/Utility/MVMCoreOperation.h new file mode 100644 index 0000000..f9aae79 --- /dev/null +++ b/MVMCore/MVMCore/Utility/MVMCoreOperation.h @@ -0,0 +1,20 @@ +// +// MVMCoreOperation.h +// myverizon +// +// Created by Scott Pfeil on 9/28/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// +// Base operation. It is concurrent/asyncronous, but it is only expected to be started with NSOperationQueue (the start function does not fire off another thread! So don't call start manually unless you are on the thread you desire). + +#import + +@interface MVMCoreOperation : NSOperation + +// Checks for cancellation and then marks as finished if so. +- (BOOL)checkAndHandleForCancellation; + +// Does the proper KVO finished stuff. +- (void)markAsFinished; + +@end diff --git a/MVMCore/MVMCore/Utility/MVMCoreOperation.m b/MVMCore/MVMCore/Utility/MVMCoreOperation.m new file mode 100644 index 0000000..139a8c1 --- /dev/null +++ b/MVMCore/MVMCore/Utility/MVMCoreOperation.m @@ -0,0 +1,108 @@ +// +// MVMCoreOperation.m +// myverizon +// +// Created by Scott Pfeil on 9/28/15. +// Copyright © 2015 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreOperation.h" + +@interface MVMCoreOperation () { + __block BOOL executing; + __block BOOL finished; +} + +// For thread safety +@property (strong, nonatomic) dispatch_queue_t executingQueue; +@property (strong, nonatomic) dispatch_queue_t finishedQueue; + +@end + +@implementation MVMCoreOperation + +- (instancetype)init { + + self = [super init]; + if (self) { + executing = NO; + finished = NO; + self.executingQueue = dispatch_queue_create("executing", DISPATCH_QUEUE_CONCURRENT); + self.finishedQueue = dispatch_queue_create("finished", DISPATCH_QUEUE_CONCURRENT); + } + return self; +} + +- (BOOL)isConcurrent { + return YES; +} + +- (BOOL)isAsynchronous { + return YES; +} + +- (BOOL)isExecuting { + __block BOOL isExecuting; + dispatch_sync(self.executingQueue, ^{ + isExecuting = executing; + }); + return isExecuting; +} + +- (BOOL)isFinished { + __block BOOL isFinished; + dispatch_sync(self.finishedQueue, ^{ + isFinished = finished; + }); + return isFinished; +} + +- (BOOL)checkAndHandleForCancellation { + + // Must move the operation to the finished state if it is canceled. + if ([self isCancelled]) { + [self markAsFinished]; + return YES; + } else { + return NO; + } +} + +- (void)markAsFinished { + + if ([self isExecuting]) { + [self willChangeValueForKey:@"isExecuting"]; + dispatch_barrier_async(self.executingQueue, ^{ + executing = NO; + }); + [self didChangeValueForKey:@"isExecuting"]; + } + if (![self isFinished]) { + [self willChangeValueForKey:@"isFinished"]; + dispatch_barrier_async(self.finishedQueue, ^{ + finished = YES; + }); + [self didChangeValueForKey:@"isFinished"]; + } +} + +- (void)start { + + // Always check for cancellation before launching the task. + if ([self checkAndHandleForCancellation]) { + return; + } + + // If the operation is not canceled, begin executing the task. + if (![self isExecuting]) { + [self willChangeValueForKey:@"isExecuting"]; + dispatch_barrier_async(self.executingQueue, ^{ + executing = YES; + }); + [self didChangeValueForKey:@"isExecuting"]; + } + + [self main]; +} + +@end diff --git a/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.h b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.h new file mode 100644 index 0000000..0ba210c --- /dev/null +++ b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.h @@ -0,0 +1,54 @@ +// +// MVMCoreViewControllerMappingObject.h +// myverizon +// +// Created by Scott Pfeil on 12/2/13. +// Copyright (c) 2013 Verizon Wireless. All rights reserved. +// +// Any given pageType will map to a view controller. This object tells if it is loaded from a story board with the id in viewControllerString. If wish to load from nib, leave storyBoard nil, and viewControllerString the nib name. + +#import +#import +@class MVMCoreErrorObject; + +@interface MVMCoreViewControllerMappingObject : NSObject + +@property (nullable, strong, nonatomic) NSMutableDictionary *viewControllerMapping; +@property (nullable, strong, nonatomic) NSMutableDictionary *requiredModuleMapping; +@property (nullable, strong, nonatomic) NSMutableDictionary *optionalModuleMapping; + +// Returns the shared instance ++ (nullable instancetype)sharedViewControllerMappingObject; + +#pragma mark - View Controller Mapping + +// Returns the mapping object which maps the given page type to how it's view controller is loaded. +- (nullable MVMCoreViewControllerMappingObject *)getViewControllerMappingForPageType:(nonnull NSString *)pageType; + +// For pages external to the mobile first framework to be added to the view controller mapping. +- (void)addToViewControllerMapping:(nullable NSDictionary *)map; + +// Creates and returns an mvm view controller of the passed in page type using the mapping. +- (nullable UIViewController *)createMFViewControllerOfPageType:(nonnull NSString *)pageType error:(MVMCoreErrorObject *_Nullable *_Nullable)error; + +#pragma mark - Module PageType Mapping + +// Returns the required modules for a given page. +- (nullable NSArray *)modulesRequiredForPageType:(nonnull NSString *)pageType; + +// For pages external to the mobile first framework to add their required modules. +- (void)addRequiredModulesToMapping:(nonnull NSArray *)mapping forPageType:(nonnull NSString *)pageType; + +// Returns the optional modules for a given page +- (nullable NSArray *)modulesOptionalForPageType:(nonnull NSString *)pageType; + +// For pages external to the mobile first framework to add their optional modules. +- (void)addOptionalModulesToMapping:(nonnull NSArray *)mapping forPageType:(nonnull NSString *)pageType; + +// Returns all the modules needed for a given page type. +- (nullable NSArray *)allModulesForPageType:(nonnull NSString *)pageType; + +// Add optional modules for multiple pages. Used by external frameworks +- (void)addOptionalModulesForPages:(nonnull NSDictionary*>*)optionalModulesForPages; + +@end diff --git a/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.m b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.m new file mode 100644 index 0000000..1bc9fe0 --- /dev/null +++ b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerMappingObject.m @@ -0,0 +1,132 @@ + + +// +// MVMCoreViewControllerMappingObject.m +// myverizon +// +// Created by Scott Pfeil on 12/2/13. +// Copyright (c) 2013 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreViewControllerMappingObject.h" +#import +#import "MVMCoreErrorObject.h" +#import "MVMCoreGetterUtility.h" +#import "MVMCoreViewControllerStoryBoardMappingObject.h" +#import "MVMCoreViewControllerNibMappingObject.h" +#import "MVMCoreViewControllerProgrammaticMappingObject.h" +#import "MVMCoreErrorConstants.h" +#import "MVMCoreHardcodedStringsConstants.h" +#import "MVMCoreObject.h" + +@interface MVMCoreViewControllerMappingObject () + +@end + +@implementation MVMCoreViewControllerMappingObject + ++ (nullable instancetype)sharedViewControllerMappingObject { + return [MVMCoreObject sharedInstance].viewControllerMapping; +} + +#pragma mark - View Controller Mapping + +- (nullable MVMCoreViewControllerMappingObject *)getViewControllerMappingForPageType:(nonnull NSString *)pageType { + return [self.viewControllerMapping objectForKey:pageType]; +} + +- (void)addToViewControllerMapping:(nullable NSDictionary *)map { + if (map) { + [self.viewControllerMapping addEntriesFromDictionary:map]; + } +} + +- (nullable UIViewController *)createMFViewControllerOfPageType:(nonnull NSString *)pageType error:(MVMCoreErrorObject *_Nullable *_Nullable)error { + + // Initialize the view controller using the pageType-ViewController mapping. + UIViewController *viewController = nil; + + MVMCoreViewControllerMappingObject *mapping = [self getViewControllerMappingForPageType:pageType]; + if ([mapping isKindOfClass:[MVMCoreViewControllerStoryBoardMappingObject class]]) { + + // Initialize from story board. + NSString *storyboardName = ((MVMCoreViewControllerStoryBoardMappingObject *)mapping).storyBoard; + NSString *viewIdentifier = ((MVMCoreViewControllerStoryBoardMappingObject *)mapping).storyBoardIdentifier; + NSString *bundleName = ((MVMCoreViewControllerStoryBoardMappingObject *)mapping).bundleName; + if (storyboardName.length != 0 && viewIdentifier.length != 0 && bundleName) { + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:[NSBundle bundleWithIdentifier:bundleName]]; + viewController = (UIViewController *)[storyboard instantiateViewControllerWithIdentifier:viewIdentifier]; + } else if (error) { + + NSInteger code = 0; + if (storyboardName.length == 0) { + code = ErrorCodeNoStoryboardName; + } else if (viewIdentifier.length == 0) { + code = ErrorCodeNoStoryBoardIdentifier; + } + *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:code domain:ErrorDomainNative location:nil]; + } + } else if ([mapping isKindOfClass:[MVMCoreViewControllerNibMappingObject class]]) { + + // Initialize from nib + NSString *viewControllerNibName = ((MVMCoreViewControllerNibMappingObject *)mapping).nibName; + NSString *bundleName = ((MVMCoreViewControllerStoryBoardMappingObject *)mapping).bundleName; + if (viewControllerNibName.length != 0) { + viewController = [[((MVMCoreViewControllerNibMappingObject *)mapping).viewControllerClass alloc] initWithNibName:viewControllerNibName bundle:[NSBundle bundleWithIdentifier:bundleName]]; + } else if (error) { + *error = [[MVMCoreErrorObject alloc] initWithTitle:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorTitle] message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] code:ErrorCodeNoNibName domain:ErrorDomainNative location:nil]; + } + } else { + // Initialize programmatically. + viewController = [[((MVMCoreViewControllerProgrammaticMappingObject *)mapping).viewControllerClass alloc] init]; + } + + return viewController; +} + +#pragma mark - Module PageType Mapping + +- (nullable NSArray *)modulesRequiredForPageType:(nonnull NSString *)pageType { + return [self.requiredModuleMapping objectForKey:pageType]; +} + +- (void)addRequiredModulesToMapping:(nonnull NSArray *)mapping forPageType:(nonnull NSString *)pageType { + if (mapping && pageType) { + [self.requiredModuleMapping setObject:mapping forKey:pageType]; + } +} + +- (nullable NSArray *)modulesOptionalForPageType:(nonnull NSString *)pageType { + return [self.optionalModuleMapping objectForKey:pageType]; +} + +- (void)addOptionalModulesToMapping:(nonnull NSArray *)mapping forPageType:(nonnull NSString *)pageType { + if (mapping && pageType) { + [self.optionalModuleMapping setObject:mapping forKey:pageType]; + } +} + +- (nullable NSArray *)allModulesForPageType:(nonnull NSString *)pageType { + NSArray *optionalModules = [self modulesOptionalForPageType:pageType]; + NSArray *requiredModules = [self modulesRequiredForPageType:pageType]; + if (optionalModules && requiredModules) { + NSMutableArray *modules = [NSMutableArray arrayWithArray:optionalModules]; + [modules addObjectsFromArray:requiredModules]; + return modules; + } else if (optionalModules) { + return optionalModules; + } else if (requiredModules) { + return requiredModules; + } else { + return nil; + } +} + +// Add optional modules for multiple pages +- (void)addOptionalModulesForPages:(nonnull NSDictionary*>*)optionalModulesForPages { + if (optionalModulesForPages) { + [self.optionalModuleMapping addEntriesFromDictionary:optionalModulesForPages]; + } +} + +@end diff --git a/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerNibMappingObject.h b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerNibMappingObject.h new file mode 100644 index 0000000..a862c1d --- /dev/null +++ b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerNibMappingObject.h @@ -0,0 +1,25 @@ +// +// MVMCoreViewControllerNibMappingObject.h +// myverizon +// +// Created by Scott Pfeil on 3/26/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import + +@interface MVMCoreViewControllerNibMappingObject : MVMCoreViewControllerMappingObject + +// View Controller Class, for loading from a nib. +@property (nonnull, strong, nonatomic) Class viewControllerClass; + +// Nib Name, for loading from a nib. +@property (nonnull, strong, nonatomic) NSString *nibName; + +// The bundle where the nib is located. +@property (nonnull, strong, nonatomic) NSString *bundleName; + +// Initializes for a nib load. +- (nullable id)initWithClass:(nonnull Class)viewControllerClass nibName:(nonnull NSString *)nibName bundleName:(nonnull NSString *)bundleName; + +@end diff --git a/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerNibMappingObject.m b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerNibMappingObject.m new file mode 100644 index 0000000..9d82eab --- /dev/null +++ b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerNibMappingObject.m @@ -0,0 +1,24 @@ +// +// MVMCoreViewControllerNibMappingObject.m +// myverizon +// +// Created by Scott Pfeil on 3/26/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreViewControllerNibMappingObject.h" + +@implementation MVMCoreViewControllerNibMappingObject + +- (nullable id)initWithClass:(nonnull Class)viewControllerClass nibName:(nonnull NSString *)nibName bundleName:(nonnull NSString *)bundleName { + self = [super init]; + if (self) { + // Custom initialization + self.viewControllerClass = viewControllerClass; + self.nibName = nibName; + self.bundleName = bundleName; + } + return self; +} + +@end diff --git a/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerProgrammaticMappingObject.h b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerProgrammaticMappingObject.h new file mode 100644 index 0000000..be973b5 --- /dev/null +++ b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerProgrammaticMappingObject.h @@ -0,0 +1,19 @@ +// +// MVMCoreViewControllerProgrammaticMappingObject.h +// myverizon +// +// Created by Scott Pfeil on 3/26/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import + +@interface MVMCoreViewControllerProgrammaticMappingObject : MVMCoreViewControllerMappingObject + +// View Controller Class, for loading by class. +@property (nonnull, strong, nonatomic) Class viewControllerClass; + +// Initializes with the given class. +- (nullable id)initWithClass:(nonnull Class)viewControllerClass; + +@end diff --git a/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerProgrammaticMappingObject.m b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerProgrammaticMappingObject.m new file mode 100644 index 0000000..b00a785 --- /dev/null +++ b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerProgrammaticMappingObject.m @@ -0,0 +1,22 @@ +// +// MVMCoreViewControllerProgrammaticMappingObject.m +// myverizon +// +// Created by Scott Pfeil on 3/26/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreViewControllerProgrammaticMappingObject.h" + +@implementation MVMCoreViewControllerProgrammaticMappingObject + +- (nullable id)initWithClass:(nonnull Class)viewControllerClass { + self = [super init]; + if (self) { + // Custom initialization + self.viewControllerClass = viewControllerClass; + } + return self; +} + +@end diff --git a/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerStoryBoardMappingObject.h b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerStoryBoardMappingObject.h new file mode 100644 index 0000000..8847427 --- /dev/null +++ b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerStoryBoardMappingObject.h @@ -0,0 +1,25 @@ +// +// MVMCoreViewControllerStoryBoardMappingObject.h +// myverizon +// +// Created by Scott Pfeil on 3/26/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import + +@interface MVMCoreViewControllerStoryBoardMappingObject : MVMCoreViewControllerMappingObject + +// Initializes with a story board load. +- (nullable id)initWithStoryBoard:(nonnull NSString *)storyBoard storyBoardIdentifier:(nonnull NSString *)storyBoardIdentifier bundleName:(nonnull NSString *)bundleName; + +// The story board to load from. nil if from a nib. +@property (nonnull, strong, nonatomic) NSString *storyBoard; + +// The identifier of the view controller in the story board. nil if from a nib. +@property (nonnull, strong, nonatomic) NSString *storyBoardIdentifier; + +// The bundle where the storyboard is located. +@property (nonnull, strong, nonatomic) NSString *bundleName; + +@end diff --git a/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerStoryBoardMappingObject.m b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerStoryBoardMappingObject.m new file mode 100644 index 0000000..69744ab --- /dev/null +++ b/MVMCore/MVMCore/ViewControllerMapping/MVMCoreViewControllerStoryBoardMappingObject.m @@ -0,0 +1,24 @@ +// +// MVMCoreViewControllerStoryBoardMappingObject.m +// myverizon +// +// Created by Scott Pfeil on 3/26/14. +// Copyright (c) 2014 Verizon Wireless. All rights reserved. +// + +#import "MVMCoreViewControllerStoryBoardMappingObject.h" + +@implementation MVMCoreViewControllerStoryBoardMappingObject + +- (nullable id)initWithStoryBoard:(nonnull NSString *)storyBoard storyBoardIdentifier:(nonnull NSString *)storyBoardIdentifier bundleName:(nonnull NSString *)bundleName { + self = [super init]; + if (self) { + // Custom initialization + self.storyBoard = storyBoard; + self.storyBoardIdentifier = storyBoardIdentifier; + self.bundleName = bundleName; + } + return self; +} + +@end