From 94055fe3080f46220cda5b916f8c8226ad7723d0 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Sun, 11 Dec 2022 22:16:02 +0530 Subject: [PATCH 01/86] deprecated "Product Experiences" methods --- CleverTapSDK.xcodeproj/project.pbxproj | 4 +++ CleverTapSDK/CleverTap+FeatureFlags.h | 11 +++++-- CleverTapSDK/CleverTap+ProductConfig.h | 43 +++++++++++++++++--------- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/CleverTapSDK.xcodeproj/project.pbxproj b/CleverTapSDK.xcodeproj/project.pbxproj index f76c6c3d..226ef742 100644 --- a/CleverTapSDK.xcodeproj/project.pbxproj +++ b/CleverTapSDK.xcodeproj/project.pbxproj @@ -1902,6 +1902,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_PREPROCESSOR_DEFINITIONS = ""; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; INFOPLIST_FILE = "CleverTapSDK/tvOS-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; @@ -1935,6 +1936,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_PREPROCESSOR_DEFINITIONS = ""; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; INFOPLIST_FILE = "CleverTapSDK/tvOS-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; @@ -2291,6 +2293,7 @@ "DEBUG=1", "$(inherited)", ); + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; INFOPLIST_FILE = CleverTapSDK/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; @@ -2328,6 +2331,7 @@ "$(PROJECT_DIR)", "$(PROJECT_DIR)/Vendors", ); + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; INFOPLIST_FILE = CleverTapSDK/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; diff --git a/CleverTapSDK/CleverTap+FeatureFlags.h b/CleverTapSDK/CleverTap+FeatureFlags.h index c1b1597d..f4275ab9 100644 --- a/CleverTapSDK/CleverTap+FeatureFlags.h +++ b/CleverTapSDK/CleverTap+FeatureFlags.h @@ -1,19 +1,24 @@ #import #import "CleverTap.h" +__attribute__((deprecated("This protocol has been deprecated and will be removed in the future versions of this SDK."))) @protocol CleverTapFeatureFlagsDelegate @optional - (void)ctFeatureFlagsUpdated; +__attribute__((deprecated("This protocol method has been deprecated and will be removed in the future versions of this SDK."))); @end @interface CleverTap (FeatureFlags) -@property (atomic, strong, readonly, nonnull) CleverTapFeatureFlags *featureFlags; +@property (atomic, strong, readonly, nonnull) CleverTapFeatureFlags *featureFlags +__attribute__((deprecated("This property has been deprecated and will be removed in the future versions of this SDK.")));; @end @interface CleverTapFeatureFlags : NSObject -@property (nonatomic, weak) id _Nullable delegate; +@property (nonatomic, weak) id _Nullable delegate +__attribute__((deprecated("This property has been deprecated and will be removed in the future versions of this SDK.")));; -- (BOOL)get:(NSString* _Nonnull)key withDefaultValue:(BOOL)defaultValue; +- (BOOL)get:(NSString* _Nonnull)key withDefaultValue:(BOOL)defaultValue +__attribute__((deprecated("This method has been deprecated and will be removed in the future versions of this SDK.")));; @end diff --git a/CleverTapSDK/CleverTap+ProductConfig.h b/CleverTapSDK/CleverTap+ProductConfig.h index bab09bcc..b60e476b 100644 --- a/CleverTapSDK/CleverTap+ProductConfig.h +++ b/CleverTapSDK/CleverTap+ProductConfig.h @@ -1,11 +1,15 @@ #import #import "CleverTap.h" +__attribute__((deprecated("This protocol has been deprecated and will be removed in the future versions of this SDK."))) @protocol CleverTapProductConfigDelegate @optional -- (void)ctProductConfigFetched; -- (void)ctProductConfigActivated; -- (void)ctProductConfigInitialized; +- (void)ctProductConfigFetched +__attribute__((deprecated("This protocol method has been deprecated and will be removed in the future versions of this SDK."))); +- (void)ctProductConfigActivated +__attribute__((deprecated("This protocol method has been deprecated and will be removed in the future versions of this SDK."))); +- (void)ctProductConfigInitialized +__attribute__((deprecated("This protocol method has been deprecated and will be removed in the future versions of this SDK."))); @end @interface CleverTap(ProductConfig) @@ -32,7 +36,8 @@ @interface CleverTapProductConfig : NSObject -@property (nonatomic, weak) id _Nullable delegate; +@property (nonatomic, weak) id _Nullable delegate +__attribute__((deprecated("This property has been deprecated and will be removed in the future versions of this SDK."))); /*! @method @@ -41,7 +46,8 @@ Fetches product configs, adhering to the default minimum fetch interval. */ -- (void)fetch; +- (void)fetch +__attribute__((deprecated("This method has been deprecated and will be removed in the future versions of this SDK."))); /*! @method @@ -50,7 +56,8 @@ Fetches product configs, adhering to the specified minimum fetch interval in seconds. */ -- (void)fetchWithMinimumInterval:(NSTimeInterval)minimumInterval; +- (void)fetchWithMinimumInterval:(NSTimeInterval)minimumInterval +__attribute__((deprecated("This method has been deprecated and will be removed in the future versions of this SDK."))); /*! @method @@ -59,7 +66,8 @@ Sets the minimum interval between successive fetch calls. */ -- (void)setMinimumFetchInterval:(NSTimeInterval)minimumFetchInterval; +- (void)setMinimumFetchInterval:(NSTimeInterval)minimumFetchInterval +__attribute__((deprecated("This method has been deprecated and will be removed in the future versions of this SDK."))); /*! @method @@ -68,7 +76,8 @@ Activates Fetched Config data to the Active Config, so that the fetched key value pairs take effect. */ -- (void)activate; +- (void)activate +__attribute__((deprecated("This method has been deprecated and will be removed in the future versions of this SDK."))); /*! @method @@ -77,7 +86,8 @@ Fetches and then activates the fetched product configs. */ -- (void)fetchAndActivate; +- (void)fetchAndActivate +__attribute__((deprecated("This method has been deprecated and will be removed in the future versions of this SDK."))); /*! @method @@ -86,7 +96,8 @@ Sets default configs using the given Dictionary */ -- (void)setDefaults:(NSDictionary *_Nullable)defaults; +- (void)setDefaults:(NSDictionary *_Nullable)defaults +__attribute__((deprecated("This method has been deprecated and will be removed in the future versions of this SDK."))); /*! @method @@ -95,7 +106,8 @@ Sets default configs using the given plist */ -- (void)setDefaultsFromPlistFileName:(NSString *_Nullable)fileName; +- (void)setDefaultsFromPlistFileName:(NSString *_Nullable)fileName +__attribute__((deprecated("This method has been deprecated and will be removed in the future versions of this SDK."))); /*! @method @@ -104,7 +116,8 @@ Returns the config value of the given key */ -- (CleverTapConfigValue *_Nullable)get:(NSString* _Nonnull)key; +- (CleverTapConfigValue *_Nullable)get:(NSString* _Nonnull)key +__attribute__((deprecated("This method has been deprecated and will be removed in the future versions of this SDK."))); /*! @method @@ -113,7 +126,8 @@ Returns the last fetch timestamp */ -- (NSDate *_Nullable)getLastFetchTimeStamp; +- (NSDate *_Nullable)getLastFetchTimeStamp +__attribute__((deprecated("This method has been deprecated and will be removed in the future versions of this SDK."))); /*! @method @@ -122,7 +136,8 @@ Deletes all activated, fetched and defaults configs and resets all Product Config settings. */ -- (void)reset; +- (void)reset +__attribute__((deprecated("This method has been deprecated and will be removed in the future versions of this SDK."))); @end From 541c6a7d9fca58efec66482a414355f6803808a0 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Thu, 23 Feb 2023 15:37:08 +0530 Subject: [PATCH 02/86] - Added PE Var implementations. TODO/Pending stuff: - Add support for Cocoapods and SPM. Add headers/files to podspec and package.swift. Currently tested with manual integration only. - Remove mocked JSON in parseResponse() in CleverTap.m: line 3033 and test Vars response with staging API. - Remove irrelevant LP (files/color/image type) code or keep it commented for future phases/releases. - Review all TODO comments. - Check if any headers need to be added in ios.modulemap and tvos.modulemap. - Cancel syncVariables call when user has opted out via setOptOut. --- CleverTapSDK.xcodeproj/project.pbxproj | 118 ++- CleverTapSDK/CTConstants.h | 18 + CleverTapSDK/CTConstants.m | 10 + CleverTapSDK/CTDeviceInfo.h | 1 - CleverTapSDK/CTDomainFactory.h | 31 + CleverTapSDK/CTDomainFactory.m | 137 +++ CleverTapSDK/CTLogger.h | 2 +- CleverTapSDK/CTLogger.m | 5 + CleverTapSDK/CTPreferences.h | 4 +- CleverTapSDK/CTPreferences.m | 20 + CleverTapSDK/CTRequest.h | 28 + CleverTapSDK/CTRequest.m | 66 ++ CleverTapSDK/CTRequestFactory.h | 20 + CleverTapSDK/CTRequestFactory.m | 39 + CleverTapSDK/CTRequestSender.h | 26 + CleverTapSDK/CTRequestSender.m | 118 +++ CleverTapSDK/CTUtils.h | 3 +- CleverTapSDK/CTUtils.m | 30 + CleverTapSDK/CleverTap+CTVar.h | 60 ++ CleverTapSDK/CleverTap.h | 24 + CleverTapSDK/CleverTap.m | 619 +++++++++----- .../controllers/CTProductConfigController.m | 1 - .../ProductExperiences/CTVar-Internal.h | 35 + CleverTapSDK/ProductExperiences/CTVar.h | 168 ++++ CleverTapSDK/ProductExperiences/CTVar.m | 497 +++++++++++ CleverTapSDK/ProductExperiences/CTVarCache.h | 89 ++ CleverTapSDK/ProductExperiences/CTVarCache.m | 808 ++++++++++++++++++ .../ProductExperiences/ContentMerger.h | 13 + .../ProductExperiences/ContentMerger.m | 48 ++ CleverTapSDK/ios.modulemap | 3 + CleverTapSDK/tvos.modulemap | 2 + CleverTapWatchOS/CleverTapWatchOS.h | 4 +- CleverTapWatchOS/CleverTapWatchOS.m | 7 +- Package.swift | 3 +- 34 files changed, 2842 insertions(+), 215 deletions(-) create mode 100644 CleverTapSDK/CTDomainFactory.h create mode 100644 CleverTapSDK/CTDomainFactory.m create mode 100644 CleverTapSDK/CTRequest.h create mode 100644 CleverTapSDK/CTRequest.m create mode 100644 CleverTapSDK/CTRequestFactory.h create mode 100644 CleverTapSDK/CTRequestFactory.m create mode 100644 CleverTapSDK/CTRequestSender.h create mode 100644 CleverTapSDK/CTRequestSender.m create mode 100644 CleverTapSDK/CleverTap+CTVar.h create mode 100644 CleverTapSDK/ProductExperiences/CTVar-Internal.h create mode 100644 CleverTapSDK/ProductExperiences/CTVar.h create mode 100644 CleverTapSDK/ProductExperiences/CTVar.m create mode 100644 CleverTapSDK/ProductExperiences/CTVarCache.h create mode 100644 CleverTapSDK/ProductExperiences/CTVarCache.m create mode 100644 CleverTapSDK/ProductExperiences/ContentMerger.h create mode 100644 CleverTapSDK/ProductExperiences/ContentMerger.m diff --git a/CleverTapSDK.xcodeproj/project.pbxproj b/CleverTapSDK.xcodeproj/project.pbxproj index 44120e50..c54405b9 100644 --- a/CleverTapSDK.xcodeproj/project.pbxproj +++ b/CleverTapSDK.xcodeproj/project.pbxproj @@ -157,7 +157,7 @@ 07FD65A2223BC26300A845B7 /* CTCoverViewController~iphoneland.xib in Resources */ = {isa = PBXBuildFile; fileRef = 07FD65A1223BC26300A845B7 /* CTCoverViewController~iphoneland.xib */; }; 07FD65A4223BCB8200A845B7 /* CTCoverViewController~ipadland.xib in Resources */ = {isa = PBXBuildFile; fileRef = 07FD65A3223BCB8200A845B7 /* CTCoverViewController~ipadland.xib */; }; 4808030E292EB4FB00C06E2F /* CleverTap+PushPermission.h in Headers */ = {isa = PBXBuildFile; fileRef = 4808030D292EB4FB00C06E2F /* CleverTap+PushPermission.h */; }; - 48080311292EB50D00C06E2F /* CTLocalInApp.h in Headers */ = {isa = PBXBuildFile; fileRef = 4808030F292EB50D00C06E2F /* CTLocalInApp.h */; }; + 48080311292EB50D00C06E2F /* CTLocalInApp.h in Headers */ = {isa = PBXBuildFile; fileRef = 4808030F292EB50D00C06E2F /* CTLocalInApp.h */; settings = {ATTRIBUTES = (Public, ); }; }; 48080312292EB50D00C06E2F /* CTLocalInApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 48080310292EB50D00C06E2F /* CTLocalInApp.m */; }; 4987C665251B5E79003E6BE8 /* CTImageInAppViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4987C663251B5E79003E6BE8 /* CTImageInAppViewController.h */; }; 4987C666251B5E79003E6BE8 /* CTImageInAppViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4987C664251B5E79003E6BE8 /* CTImageInAppViewController.m */; }; @@ -197,18 +197,54 @@ 4E25E3D02788889E0008C888 /* CTLoginInfoProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E49AE39275CB7670074A774 /* CTLoginInfoProvider.h */; }; 4E25E3D12788889F0008C888 /* CTLoginInfoProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E49AE3A275CB7670074A774 /* CTLoginInfoProvider.m */; }; 4E25E3D22788889F0008C888 /* CTLoginInfoProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E49AE39275CB7670074A774 /* CTLoginInfoProvider.h */; }; + 4E41FD8A294F441D0001FBED /* CTVar.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E41FD89294F441D0001FBED /* CTVar.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4E41FD8B294F44200001FBED /* CTVar.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E41FD89294F441D0001FBED /* CTVar.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4E41FD92294F46510001FBED /* CTVar-Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E41FD8C294F46500001FBED /* CTVar-Internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4E41FD93294F46510001FBED /* CTVar-Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E41FD8C294F46500001FBED /* CTVar-Internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4E41FD94294F46510001FBED /* CTVar.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E41FD8D294F46500001FBED /* CTVar.m */; }; + 4E41FD95294F46510001FBED /* CTVar.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E41FD8D294F46500001FBED /* CTVar.m */; }; + 4E41FD98294F46510001FBED /* CTVarCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E41FD8F294F46510001FBED /* CTVarCache.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4E41FD99294F46510001FBED /* CTVarCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E41FD8F294F46510001FBED /* CTVarCache.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4E41FD9C294F46510001FBED /* CTVarCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E41FD91294F46510001FBED /* CTVarCache.m */; }; + 4E41FD9D294F46510001FBED /* CTVarCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E41FD91294F46510001FBED /* CTVarCache.m */; }; 4E49AE53275D24570074A774 /* CTValidationResultStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E49AE51275D24570074A774 /* CTValidationResultStack.h */; }; 4E49AE54275D24570074A774 /* CTValidationResultStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E49AE51275D24570074A774 /* CTValidationResultStack.h */; }; 4E49AE55275D24570074A774 /* CTValidationResultStack.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E49AE52275D24570074A774 /* CTValidationResultStack.m */; }; 4E49AE56275D24570074A774 /* CTValidationResultStack.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E49AE52275D24570074A774 /* CTValidationResultStack.m */; }; + 4E6383D7296DE9A8001E83E3 /* CTRequestSender.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E6383D5296DE9A7001E83E3 /* CTRequestSender.h */; }; + 4E6383D8296DE9A8001E83E3 /* CTRequestSender.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E6383D5296DE9A7001E83E3 /* CTRequestSender.h */; }; + 4E6383D9296DE9A8001E83E3 /* CTRequestSender.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E6383D6296DE9A7001E83E3 /* CTRequestSender.m */; }; + 4E6383DA296DE9A8001E83E3 /* CTRequestSender.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E6383D6296DE9A7001E83E3 /* CTRequestSender.m */; }; 4E64855B287440BA00C2F409 /* AmazonRootCA1.cer in Resources */ = {isa = PBXBuildFile; fileRef = 4E64855A287440BA00C2F409 /* AmazonRootCA1.cer */; }; 4E64855C287440BA00C2F409 /* AmazonRootCA1.cer in Resources */ = {isa = PBXBuildFile; fileRef = 4E64855A287440BA00C2F409 /* AmazonRootCA1.cer */; }; 4E7704B82679DCEF005222D0 /* CleverTapURLDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E7704B72679DCEF005222D0 /* CleverTapURLDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4E7929F929799E8F00B81F3C /* CTDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E7929F729799E8F00B81F3C /* CTDomainFactory.h */; }; + 4E7929FA29799E8F00B81F3C /* CTDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E7929F729799E8F00B81F3C /* CTDomainFactory.h */; }; + 4E7929FB29799E8F00B81F3C /* CTDomainFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E7929F829799E8F00B81F3C /* CTDomainFactory.m */; }; + 4E7929FC29799E8F00B81F3C /* CTDomainFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E7929F829799E8F00B81F3C /* CTDomainFactory.m */; }; + 4E7929FF2979CAE100B81F3C /* CTSSLPinningFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E7929FD2979CAE100B81F3C /* CTSSLPinningFactory.h */; }; + 4E792A002979CAE100B81F3C /* CTSSLPinningFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E7929FD2979CAE100B81F3C /* CTSSLPinningFactory.h */; }; + 4E792A012979CAE100B81F3C /* CTSSLPinningFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E7929FE2979CAE100B81F3C /* CTSSLPinningFactory.m */; }; + 4E792A022979CAE100B81F3C /* CTSSLPinningFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E7929FE2979CAE100B81F3C /* CTSSLPinningFactory.m */; }; + 4E838C40299F419900ED0875 /* ContentMerger.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E838C3E299F419800ED0875 /* ContentMerger.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4E838C41299F419900ED0875 /* ContentMerger.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E838C3E299F419800ED0875 /* ContentMerger.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4E838C42299F419900ED0875 /* ContentMerger.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E838C3F299F419900ED0875 /* ContentMerger.m */; }; + 4E838C43299F419900ED0875 /* ContentMerger.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E838C3F299F419900ED0875 /* ContentMerger.m */; }; + 4E838C4629A0C94B00ED0875 /* CleverTap+CTVar.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E838C4429A0C94B00ED0875 /* CleverTap+CTVar.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4E838C4729A0C94B00ED0875 /* CleverTap+CTVar.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E838C4429A0C94B00ED0875 /* CleverTap+CTVar.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4E872967277CDF9000A7A618 /* inapp_interstitial.json in Resources */ = {isa = PBXBuildFile; fileRef = 4E1F156427709304009387AE /* inapp_interstitial.json */; }; 4E872968277CDF9000A7A618 /* app_inbox.json in Resources */ = {isa = PBXBuildFile; fileRef = 4E1F156727709848009387AE /* app_inbox.json */; }; 4E872969277CDF9000A7A618 /* inapp_alert.json in Resources */ = {isa = PBXBuildFile; fileRef = 4E1F1561277090D6009387AE /* inapp_alert.json */; }; 4E87296E277CE8EB00A7A618 /* StubHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E87296D277CE8EB00A7A618 /* StubHelper.m */; }; 4E872973277CEE6700A7A618 /* TestConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E872972277CEE6700A7A618 /* TestConstants.m */; }; + 4EA64A26296C115E001D9B22 /* CTRequestFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EA64A24296C115E001D9B22 /* CTRequestFactory.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4EA64A27296C115E001D9B22 /* CTRequestFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EA64A24296C115E001D9B22 /* CTRequestFactory.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4EA64A28296C115E001D9B22 /* CTRequestFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EA64A25296C115E001D9B22 /* CTRequestFactory.m */; }; + 4EA64A29296C115E001D9B22 /* CTRequestFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EA64A25296C115E001D9B22 /* CTRequestFactory.m */; }; + 4EA64A2C296C1190001D9B22 /* CTRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EA64A2A296C1190001D9B22 /* CTRequest.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4EA64A2D296C1190001D9B22 /* CTRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EA64A2A296C1190001D9B22 /* CTRequest.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4EA64A2E296C1190001D9B22 /* CTRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EA64A2B296C1190001D9B22 /* CTRequest.m */; }; + 4EA64A2F296C1190001D9B22 /* CTRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EA64A2B296C1190001D9B22 /* CTRequest.m */; }; 4EC2D085278AAD8000F4DE54 /* IdentityManagementTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EC2D084278AAD8000F4DE54 /* IdentityManagementTests.m */; }; 5709005327FD8E1F0011B89F /* CleverTap+SCDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = 5709005227FD8E1E0011B89F /* CleverTap+SCDomain.h */; settings = {ATTRIBUTES = (Public, ); }; }; 57D2E1C82684B1630068E45A /* CleverTap.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C7BBC8207D8837001345EF /* CleverTap.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -567,6 +603,11 @@ 4E1F1561277090D6009387AE /* inapp_alert.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = inapp_alert.json; sourceTree = ""; }; 4E1F156427709304009387AE /* inapp_interstitial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = inapp_interstitial.json; sourceTree = ""; }; 4E1F156727709848009387AE /* app_inbox.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = app_inbox.json; sourceTree = ""; }; + 4E41FD89294F441D0001FBED /* CTVar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CTVar.h; sourceTree = ""; }; + 4E41FD8C294F46500001FBED /* CTVar-Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CTVar-Internal.h"; sourceTree = ""; }; + 4E41FD8D294F46500001FBED /* CTVar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTVar.m; sourceTree = ""; }; + 4E41FD8F294F46510001FBED /* CTVarCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CTVarCache.h; sourceTree = ""; }; + 4E41FD91294F46510001FBED /* CTVarCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTVarCache.m; sourceTree = ""; }; 4E49AE39275CB7670074A774 /* CTLoginInfoProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTLoginInfoProvider.h; sourceTree = ""; }; 4E49AE3A275CB7670074A774 /* CTLoginInfoProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTLoginInfoProvider.m; sourceTree = ""; }; 4E49AE42275D00E80074A774 /* CTIdentityRepo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTIdentityRepo.h; sourceTree = ""; }; @@ -578,12 +619,25 @@ 4E49AE52275D24570074A774 /* CTValidationResultStack.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTValidationResultStack.m; sourceTree = ""; }; 4E49AE57275D31910074A774 /* CTIdentityRepoFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTIdentityRepoFactory.h; sourceTree = ""; }; 4E49AE58275D31910074A774 /* CTIdentityRepoFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTIdentityRepoFactory.m; sourceTree = ""; }; + 4E6383D5296DE9A7001E83E3 /* CTRequestSender.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTRequestSender.h; sourceTree = ""; }; + 4E6383D6296DE9A7001E83E3 /* CTRequestSender.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTRequestSender.m; sourceTree = ""; }; 4E64855A287440BA00C2F409 /* AmazonRootCA1.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = AmazonRootCA1.cer; sourceTree = ""; }; 4E7704B72679DCEF005222D0 /* CleverTapURLDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CleverTapURLDelegate.h; sourceTree = ""; }; + 4E7929F729799E8F00B81F3C /* CTDomainFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTDomainFactory.h; sourceTree = ""; }; + 4E7929F829799E8F00B81F3C /* CTDomainFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTDomainFactory.m; sourceTree = ""; }; + 4E7929FD2979CAE100B81F3C /* CTSSLPinningFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTSSLPinningFactory.h; sourceTree = ""; }; + 4E7929FE2979CAE100B81F3C /* CTSSLPinningFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTSSLPinningFactory.m; sourceTree = ""; }; + 4E838C3E299F419800ED0875 /* ContentMerger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContentMerger.h; sourceTree = ""; }; + 4E838C3F299F419900ED0875 /* ContentMerger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContentMerger.m; sourceTree = ""; }; + 4E838C4429A0C94B00ED0875 /* CleverTap+CTVar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CleverTap+CTVar.h"; sourceTree = ""; }; 4E87296C277CE8EB00A7A618 /* StubHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StubHelper.h; sourceTree = ""; }; 4E87296D277CE8EB00A7A618 /* StubHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StubHelper.m; sourceTree = ""; }; 4E872971277CEE6700A7A618 /* TestConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestConstants.h; sourceTree = ""; }; 4E872972277CEE6700A7A618 /* TestConstants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestConstants.m; sourceTree = ""; }; + 4EA64A24296C115E001D9B22 /* CTRequestFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTRequestFactory.h; sourceTree = ""; }; + 4EA64A25296C115E001D9B22 /* CTRequestFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTRequestFactory.m; sourceTree = ""; }; + 4EA64A2A296C1190001D9B22 /* CTRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTRequest.h; sourceTree = ""; }; + 4EA64A2B296C1190001D9B22 /* CTRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTRequest.m; sourceTree = ""; }; 4EC2D084278AAD8000F4DE54 /* IdentityManagementTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IdentityManagementTests.m; sourceTree = ""; }; 4EDCDE4D278AC4DF0065E699 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 5709005227FD8E1E0011B89F /* CleverTap+SCDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CleverTap+SCDomain.h"; sourceTree = ""; }; @@ -979,6 +1033,20 @@ path = "Stub Responses"; sourceTree = ""; }; + 4E41FD88294F43E70001FBED /* ProductExperiences */ = { + isa = PBXGroup; + children = ( + 4E41FD8C294F46500001FBED /* CTVar-Internal.h */, + 4E41FD89294F441D0001FBED /* CTVar.h */, + 4E41FD8D294F46500001FBED /* CTVar.m */, + 4E41FD8F294F46510001FBED /* CTVarCache.h */, + 4E41FD91294F46510001FBED /* CTVarCache.m */, + 4E838C3E299F419800ED0875 /* ContentMerger.h */, + 4E838C3F299F419900ED0875 /* ContentMerger.m */, + ); + path = ProductExperiences; + sourceTree = ""; + }; D02AC2D9276044F70031C1BE /* CleverTapSDKTests */ = { isa = PBXGroup; children = ( @@ -1088,7 +1156,9 @@ isa = PBXGroup; children = ( 4808030D292EB4FB00C06E2F /* CleverTap+PushPermission.h */, + 4E838C4429A0C94B00ED0875 /* CleverTap+CTVar.h */, 4E64855A287440BA00C2F409 /* AmazonRootCA1.cer */, + 4E41FD88294F43E70001FBED /* ProductExperiences */, D0BD7598241760A30006EE55 /* ProductConfig */, D0D4C9E62414CBA30029477E /* FeatureFlags */, 0701E9572372AD550034AAC2 /* DisplayUnit */, @@ -1177,6 +1247,16 @@ 071EB4A1217F6427008F0FAB /* CTInAppDisplayViewControllerPrivate.h */, 071EB4AA217F6427008F0FAB /* CTNotificationButton.h */, 071EB481217F6427008F0FAB /* CTNotificationButton.m */, + 4EA64A24296C115E001D9B22 /* CTRequestFactory.h */, + 4EA64A25296C115E001D9B22 /* CTRequestFactory.m */, + 4EA64A2A296C1190001D9B22 /* CTRequest.h */, + 4EA64A2B296C1190001D9B22 /* CTRequest.m */, + 4E6383D5296DE9A7001E83E3 /* CTRequestSender.h */, + 4E6383D6296DE9A7001E83E3 /* CTRequestSender.m */, + 4E7929F729799E8F00B81F3C /* CTDomainFactory.h */, + 4E7929F829799E8F00B81F3C /* CTDomainFactory.m */, + 4E7929FD2979CAE100B81F3C /* CTSSLPinningFactory.h */, + 4E7929FE2979CAE100B81F3C /* CTSSLPinningFactory.m */, ); path = CleverTapSDK; sourceTree = ""; @@ -1226,16 +1306,19 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 4E41FD8B294F44200001FBED /* CTVar.h in Headers */, D014B90620E2FB62001E0780 /* CTKnownProfileFields.h in Headers */, D014B8E820E2FA6D001E0780 /* CleverTapBuildInfo.h in Headers */, D014B90020E2FB46001E0780 /* CTValidationResult.h in Headers */, D014B8E320E2F9FA001E0780 /* CleverTapInstanceConfigPrivate.h in Headers */, D014B91E20E2FBDA001E0780 /* CTPinnedNSURLSessionDelegate.h in Headers */, + 4E41FD93294F46510001FBED /* CTVar-Internal.h in Headers */, D014B8E920E2FA88001E0780 /* CleverTapEventDetail.h in Headers */, 071EB521217F6764008F0FAB /* CTNotificationButton.h in Headers */, D014B8FC20E2FB12001E0780 /* CTPreferences.h in Headers */, 071EB518217F6510008F0FAB /* CTInAppFCManager.h in Headers */, D014B8EE20E2FAA8001E0780 /* CleverTapUTMDetail.h in Headers */, + 4E838C4729A0C94B00ED0875 /* CleverTap+CTVar.h in Headers */, D014B90820E2FB6B001E0780 /* CTLocalDataStore.h in Headers */, D0BD75A12417690E0006EE55 /* CleverTapProductConfigPrivate.h in Headers */, D014B90220E2FB4F001E0780 /* CTEventBuilder.h in Headers */, @@ -1245,11 +1328,14 @@ D0BD75AF241769E40006EE55 /* CleverTap+ProductConfig.h in Headers */, D014B8F020E2FAB1001E0780 /* CTPlistInfo.h in Headers */, D014B8EC20E2FA9D001E0780 /* CleverTapTrackedViewController.h in Headers */, + 4E792A002979CAE100B81F3C /* CTSSLPinningFactory.h in Headers */, D0BD75AC241769710006EE55 /* CleverTap+FeatureFlags.h in Headers */, + 4E7929FA29799E8F00B81F3C /* CTDomainFactory.h in Headers */, 4E25E3CC278887A80008C888 /* CTFlexibleIdentityRepo.h in Headers */, D014B8EB20E2FA94001E0780 /* CleverTapSyncDelegate.h in Headers */, 4E49AE54275D24570074A774 /* CTValidationResultStack.h in Headers */, 07BF465D217F7C88002E166D /* CTInAppDisplayViewControllerPrivate.h in Headers */, + 4E838C41299F419900ED0875 /* ContentMerger.h in Headers */, D0BD75A82417694F0006EE55 /* CTFeatureFlagsController.h in Headers */, 4E25E3CD278887A80008C888 /* CTLegacyIdentityRepo.h in Headers */, D014B8F820E2FADD001E0780 /* CTUtils.h in Headers */, @@ -1261,12 +1347,16 @@ 49E2B18824237DCB00AD704B /* CleverTapFeatureFlagsPrivate.h in Headers */, 071EB525217F6C8F008F0FAB /* CTUIUtils.h in Headers */, D014B8FE20E2FB3B001E0780 /* CTLogger.h in Headers */, + 4EA64A2D296C1190001D9B22 /* CTRequest.h in Headers */, D014B8E120E2F9E9001E0780 /* CleverTap+SSLPinning.h in Headers */, D014B91C20E2FBD1001E0780 /* CTCertificatePinning.h in Headers */, D014B90420E2FB59001E0780 /* CTValidator.h in Headers */, + 4E6383D8296DE9A8001E83E3 /* CTRequestSender.h in Headers */, D0A9E9CD20EEA8630004BC6F /* CTLocationManager.h in Headers */, + 4EA64A27296C115E001D9B22 /* CTRequestFactory.h in Headers */, D014B8F420E2FACA001E0780 /* CTSwizzle.h in Headers */, D014B8E620E2FA64001E0780 /* CleverTapInstanceConfig.h in Headers */, + 4E41FD99294F46510001FBED /* CTVarCache.h in Headers */, D014B8F220E2FABC001E0780 /* CTDeviceInfo.h in Headers */, D014B90A20E2FB74001E0780 /* CTProfileBuilder.h in Headers */, 07BF4662217F8359002E166D /* CTInAppUtils.h in Headers */, @@ -1278,6 +1368,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 4EA64A2C296C1190001D9B22 /* CTRequest.h in Headers */, 57D2E1C82684B1630068E45A /* CleverTap.h in Headers */, D0CACF8B20B8923700A02327 /* CleverTap+SSLPinning.h in Headers */, 4E7704B82679DCEF005222D0 /* CleverTapURLDelegate.h in Headers */, @@ -1290,8 +1381,10 @@ D014B8E220E2F9F9001E0780 /* CleverTapInstanceConfigPrivate.h in Headers */, 071EB4CF217F6427008F0FAB /* CTBaseHeaderFooterViewControllerPrivate.h in Headers */, D0BD759D241760C60006EE55 /* CTProductConfigController.h in Headers */, + 4E41FD8A294F441D0001FBED /* CTVar.h in Headers */, 4E49AE53275D24570074A774 /* CTValidationResultStack.h in Headers */, 07B94544219EA34300D4C542 /* CTInboxController.h in Headers */, + 4E838C40299F419900ED0875 /* ContentMerger.h in Headers */, 5709005327FD8E1F0011B89F /* CleverTap+SCDomain.h in Headers */, D0213D4F207D905800FE5740 /* CleverTapUTMDetail.h in Headers */, 071EB4D3217F6427008F0FAB /* CTAVPlayerViewController.h in Headers */, @@ -1300,13 +1393,17 @@ 07B94546219EA34300D4C542 /* CTMessageMO+CoreDataProperties.h in Headers */, D01651B22097B42C00660178 /* CTValidator.h in Headers */, D0A84ADC209136D500191B1F /* CTLogger.h in Headers */, + 4E41FD98294F46510001FBED /* CTVarCache.h in Headers */, 07B94541219EA34300D4C542 /* CleverTapInboxViewControllerPrivate.h in Headers */, 071EB4EE217F6427008F0FAB /* CTAlertViewController.h in Headers */, + 4E838C4629A0C94B00ED0875 /* CleverTap+CTVar.h in Headers */, D0CACF9620B8A4F800A02327 /* CTPinnedNSURLSessionDelegate.h in Headers */, + 4E41FD92294F46510001FBED /* CTVar-Internal.h in Headers */, 071EB4FE217F6427008F0FAB /* CTDismissButton.h in Headers */, 071EB511217F6427008F0FAB /* CTUIUtils.h in Headers */, D01651AE2097B38400660178 /* CTEventBuilder.h in Headers */, 072F9E3D21B1368000BC6313 /* CTInboxIconMessageCell.h in Headers */, + 4EA64A26296C115E001D9B22 /* CTRequestFactory.h in Headers */, D01651B62097B81400660178 /* CTKnownProfileFields.h in Headers */, 071EB4D7217F6427008F0FAB /* CTCoverViewController.h in Headers */, D0047B0E2098E2F00019C6FD /* CTProfileBuilder.h in Headers */, @@ -1318,6 +1415,7 @@ 07D8C08B21DDEC54006F5A1B /* CTCarouselImageView.h in Headers */, 071EB515217F6427008F0FAB /* CTInterstitialViewController.h in Headers */, 0701E9622372C1950034AAC2 /* CTDisplayUnitController.h in Headers */, + 4E7929F929799E8F00B81F3C /* CTDomainFactory.h in Headers */, 0797132F21A2F09A0011C9A3 /* CTSwipeView.h in Headers */, D0BD75A02417690E0006EE55 /* CleverTapProductConfigPrivate.h in Headers */, D0D4C9EB2414D1C50029477E /* CTFeatureFlagsController.h in Headers */, @@ -1330,6 +1428,7 @@ 071EB4FD217F6427008F0FAB /* CTInAppUtils.h in Headers */, D0213D54207D93C300FE5740 /* CTConstants.h in Headers */, D0047B0A2098D45B0019C6FD /* CTLocalDataStore.h in Headers */, + 4E7929FF2979CAE100B81F3C /* CTSSLPinningFactory.h in Headers */, 071EB4F0217F6427008F0FAB /* CTInAppDisplayViewControllerPrivate.h in Headers */, 4987C668251B5F9E003E6BE8 /* CTImageInAppViewControllerPrivate.h in Headers */, D01A0899207ED98300423D6F /* CTPlistInfo.h in Headers */, @@ -1344,6 +1443,7 @@ 071EB4FB217F6427008F0FAB /* CTInAppDisplayViewController.h in Headers */, D0A6626D20801E7F00B403F3 /* CTDeviceInfo.h in Headers */, D0A6626A207EE6F500B403F3 /* CleverTapBuildInfo.h in Headers */, + 4E6383D7296DE9A8001E83E3 /* CTRequestSender.h in Headers */, D0D4C9F52414EE770029477E /* CleverTapFeatureFlagsPrivate.h in Headers */, D0213D50207D905800FE5740 /* CleverTapEventDetail.h in Headers */, 07B94550219EA39000D4C542 /* CleverTap+Inbox.h in Headers */, @@ -1696,7 +1796,11 @@ files = ( 49C189A3243B0CBB0003E4D4 /* CTFeatureFlagsController.m in Sources */, 071EB519217F6513008F0FAB /* CTInAppFCManager.m in Sources */, + 4EA64A2F296C1190001D9B22 /* CTRequest.m in Sources */, 49C189A6243B13110003E4D4 /* CleverTapFeatureFlags.m in Sources */, + 4E792A022979CAE100B81F3C /* CTSSLPinningFactory.m in Sources */, + 4E41FD95294F46510001FBED /* CTVar.m in Sources */, + 4E6383DA296DE9A8001E83E3 /* CTRequestSender.m in Sources */, D014B8ED20E2FAA2001E0780 /* CleverTapTrackedViewController.m in Sources */, D014B8F120E2FAB9001E0780 /* CTPlistInfo.m in Sources */, 4E25E3D12788889F0008C888 /* CTLoginInfoProvider.m in Sources */, @@ -1706,6 +1810,8 @@ 07BF465C217F7C4B002E166D /* CTInAppDisplayViewController.m in Sources */, 071EB51B217F6568008F0FAB /* CTInAppNotification.m in Sources */, D0A9E9CE20EEA8770004BC6F /* CTLocationManager.m in Sources */, + 4E838C43299F419900ED0875 /* ContentMerger.m in Sources */, + 4E7929FC29799E8F00B81F3C /* CTDomainFactory.m in Sources */, D014B90B20E2FB7A001E0780 /* CTProfileBuilder.m in Sources */, D014B90120E2FB4C001E0780 /* CTValidationResult.m in Sources */, 4E25E3CB278887A80008C888 /* CTLegacyIdentityRepo.m in Sources */, @@ -1720,12 +1826,14 @@ D014B8E720E2FA6A001E0780 /* CleverTapInstanceConfig.m in Sources */, D014B8F520E2FAD0001E0780 /* CTSwizzle.m in Sources */, D014B8EF20E2FAAD001E0780 /* CleverTapUTMDetail.m in Sources */, + 4E41FD9D294F46510001FBED /* CTVarCache.m in Sources */, D014B91D20E2FBD6001E0780 /* CTCertificatePinning.m in Sources */, D014B90920E2FB71001E0780 /* CTLocalDataStore.m in Sources */, D0BD75A7241769440006EE55 /* CleverTapProductConfig.m in Sources */, D014B8EA20E2FA8D001E0780 /* CleverTapEventDetail.m in Sources */, D014B90320E2FB55001E0780 /* CTEventBuilder.m in Sources */, D014B8FB20E2FB0E001E0780 /* CTUriHelper.m in Sources */, + 4EA64A29296C115E001D9B22 /* CTRequestFactory.m in Sources */, D014B8FF20E2FB40001E0780 /* CTLogger.m in Sources */, D014B8FD20E2FB18001E0780 /* CTPreferences.m in Sources */, 4E49AE56275D24570074A774 /* CTValidationResultStack.m in Sources */, @@ -1778,6 +1886,7 @@ files = ( 071EB509217F6427008F0FAB /* CTAlertViewController.m in Sources */, 07B9454B219EA34300D4C542 /* CleverTapInboxMessage.m in Sources */, + 4E41FD9C294F46510001FBED /* CTVarCache.m in Sources */, 07053B7321E653E70085B44A /* UIView+CTToast.m in Sources */, D0BD75A6241769440006EE55 /* CleverTapProductConfig.m in Sources */, D0213D52207D905800FE5740 /* CleverTapEventDetail.m in Sources */, @@ -1791,15 +1900,18 @@ D079742A21FE2F2300773602 /* CTVideoThumbnailGenerator.m in Sources */, 071EB4C9217F6427008F0FAB /* CTHeaderViewController.m in Sources */, 4E25E3C7278887A70008C888 /* CTIdentityRepoFactory.m in Sources */, + 4E41FD94294F46510001FBED /* CTVar.m in Sources */, 071EB505217F6427008F0FAB /* CTCoverViewController.m in Sources */, 071EB4CD217F6427008F0FAB /* CTInAppUtils.m in Sources */, D0CACF9120B8A44C00A02327 /* CTCertificatePinning.m in Sources */, + 4EA64A28296C115E001D9B22 /* CTRequestFactory.m in Sources */, D033FB84208FE51200B4390F /* CTUtils.m in Sources */, 0701E975237A9A760034AAC2 /* CleverTapDisplayUnitContent.m in Sources */, 4987C666251B5E79003E6BE8 /* CTImageInAppViewController.m in Sources */, D0405B4722050C5200D64EC3 /* CTInboxUtils.m in Sources */, D01651B32097B42C00660178 /* CTValidator.m in Sources */, 49C189A2243B08A40003E4D4 /* CTFeatureFlagsController.m in Sources */, + 4E838C42299F419900ED0875 /* ContentMerger.m in Sources */, 071EB513217F6427008F0FAB /* CTInAppNotification.m in Sources */, D0CACF9720B8A4F800A02327 /* CTPinnedNSURLSessionDelegate.m in Sources */, D0047B0F2098E2F00019C6FD /* CTProfileBuilder.m in Sources */, @@ -1807,6 +1919,7 @@ 0797133A21A309720011C9A3 /* CTCarouselImageView.m in Sources */, 072F9E4321B14ECC00BC6313 /* CTInboxMessageActionView.m in Sources */, 0796FB6A21AE5B6300FC380D /* CTCarouselImageMessageCell.m in Sources */, + 4E6383D9296DE9A8001E83E3 /* CTRequestSender.m in Sources */, 071EB4D1217F6427008F0FAB /* CTNotificationButton.m in Sources */, 0701E9632372C1950034AAC2 /* CTDisplayUnitController.m in Sources */, D0213D4E207D905800FE5740 /* CleverTapTrackedViewController.m in Sources */, @@ -1814,6 +1927,7 @@ D0047B0B2098D45B0019C6FD /* CTLocalDataStore.m in Sources */, 071EB4CC217F6427008F0FAB /* CTHalfInterstitialViewController.m in Sources */, 07B94549219EA34300D4C542 /* CTMessageMO+CoreDataProperties.m in Sources */, + 4EA64A2E296C1190001D9B22 /* CTRequest.m in Sources */, 071EB4CE217F6427008F0FAB /* CTInAppDisplayViewController.m in Sources */, 07B9454D219EA34300D4C542 /* Inbox.xcdatamodeld in Sources */, D0213D51207D905800FE5740 /* CleverTapUTMDetail.m in Sources */, @@ -1828,6 +1942,7 @@ 071EB4D4217F6427008F0FAB /* CTBaseHeaderFooterViewController.m in Sources */, D06F052921E802D400D1B6BD /* CTInboxBaseMessageCell.m in Sources */, 07B9453F219EA34300D4C542 /* CleverTapInboxStyleConfig.m in Sources */, + 4E7929FB29799E8F00B81F3C /* CTDomainFactory.m in Sources */, 0796FB6421AE5B2900FC380D /* CTCarouselMessageCell.m in Sources */, 071EB4F6217F6427008F0FAB /* CTUIUtils.m in Sources */, 071EB4FA217F6427008F0FAB /* CTAVPlayerViewController.m in Sources */, @@ -1844,6 +1959,7 @@ 49E2B18324178E7400AD704B /* CleverTapConfigValue.m in Sources */, 07B94540219EA34300D4C542 /* CTInboxController.m in Sources */, 4E25E3C4278887A70008C888 /* CTLegacyIdentityRepo.m in Sources */, + 4E792A012979CAE100B81F3C /* CTSSLPinningFactory.m in Sources */, 078C63AA22420321001FDDB8 /* CleverTapJSInterface.m in Sources */, 4E49AE55275D24570074A774 /* CTValidationResultStack.m in Sources */, D01A0895207EC2D400423D6F /* CleverTapInstanceConfig.m in Sources */, diff --git a/CleverTapSDK/CTConstants.h b/CleverTapSDK/CTConstants.h index 91f38adc..cc90a889 100644 --- a/CleverTapSDK/CTConstants.h +++ b/CleverTapSDK/CTConstants.h @@ -2,6 +2,14 @@ extern NSString *const kCTApiDomain; extern NSString *const kCTNotifViewedApiDomain; +extern NSString *CT_KIND_INT; +extern NSString *CT_KIND_FLOAT; +extern NSString *CT_KIND_STRING; +extern NSString *CT_KIND_BOOLEAN; +extern NSString *CT_KIND_DICTIONARY; +extern NSString *CT_KIND_ARRAY; +extern NSString *CLEVERTAP_DEFAULTS_VARIABLES_KEY; +extern NSString *CLEVERTAP_DEFAULTS_VARS_JSON_KEY; #define CleverTapLogInfo(level, fmt, ...) if(level >= 0) { NSLog((@"%@" fmt), @"[CleverTap]: ", ##__VA_ARGS__); } #define CleverTapLogDebug(level, fmt, ...) if(level > 0) { NSLog((@"%@" fmt), @"[CleverTap]: ", ##__VA_ARGS__); } @@ -10,6 +18,13 @@ extern NSString *const kCTNotifViewedApiDomain; #define CleverTapLogStaticDebug(fmt, ...) if([CTLogger getDebugLevel] > 0) { NSLog((@"%@" fmt), @"[CleverTap]: ", ##__VA_ARGS__); } #define CleverTapLogStaticInternal(fmt, ...) if([CTLogger getDebugLevel] > 1) { NSLog((@"%@" fmt), @"[CleverTap]: ", ##__VA_ARGS__); } + + +#define CT_TRY @try { +#define CT_END_TRY }\ +@catch (NSException *e) {\ +[CTLogger logInternalError:e]; } + #define CLTAP_REQUEST_TIME_OUT_INTERVAL 10 #define CLTAP_ACCOUNT_ID_LABEL @"CleverTapAccountID" #define CLTAP_TOKEN_LABEL @"CleverTapToken" @@ -63,6 +78,7 @@ extern NSString *const kCTNotifViewedApiDomain; #define CLTAP_PRODUCT_CONFIG_JSON_RESPONSE_KEY @"pc_notifs" #define CLTAP_PREFS_INAPP_KEY @"inapp_notifs" #define CLTAP_GEOFENCES_JSON_RESPONSE_KEY @"geofences" +#define CLTAP_PE_VARS_RESPONSE_KEY @"vars" #define CLTAP_DISCARDED_EVENT_JSON_KEY @"d_e" #define CLTAP_INAPP_CLOSE_IV_WIDTH 40 #define CLTAP_NOTIFICATION_ID_TAG @"wzrk_id" @@ -124,4 +140,6 @@ extern NSString *const kCTNotifViewedApiDomain; #define CLTAP_PROFILE_IDENTIFIER_KEYS @[@"Identity", @"Email"] // LEGACY KEYS #define CLTAP_ALL_PROFILE_IDENTIFIER_KEYS @[@"Identity", @"Email", @"Phone"] +#define CLTAP_DEFINE_VARS_URL @"/defineVars" + diff --git a/CleverTapSDK/CTConstants.m b/CleverTapSDK/CTConstants.m index e01145d7..49853793 100644 --- a/CleverTapSDK/CTConstants.m +++ b/CleverTapSDK/CTConstants.m @@ -2,3 +2,13 @@ NSString *const kCTApiDomain = @"clevertap-prod.com"; NSString *const kCTNotifViewedApiDomain = @"spiky.clevertap-prod.com"; + +NSString *CT_KIND_INT = @"integer"; +NSString *CT_KIND_FLOAT = @"float"; +NSString *CT_KIND_STRING = @"string"; +NSString *CT_KIND_BOOLEAN = @"bool"; +//NSString *CT_KIND_FILE = @"file"; +NSString *CT_KIND_DICTIONARY = @"group"; +//NSString *CT_KIND_ARRAY = @"list"; +NSString *CLEVERTAP_DEFAULTS_VARIABLES_KEY = @"__clevertap_variables"; +NSString *CLEVERTAP_DEFAULTS_VARS_JSON_KEY = @"__clevertap_variables_json"; diff --git a/CleverTapSDK/CTDeviceInfo.h b/CleverTapSDK/CTDeviceInfo.h index cf4d9f6f..54983546 100644 --- a/CleverTapSDK/CTDeviceInfo.h +++ b/CleverTapSDK/CTDeviceInfo.h @@ -32,7 +32,6 @@ - (void)forceNewDeviceID; - (void)forceUpdateCustomDeviceID:(NSString *)cleverTapID; - (BOOL)isErrorDeviceID; -- (void)setDirectCallSDKVersion: (NSString *)version; - (void)incrementLocalInAppCount; - (int)getLocalInAppCount; - (void)setSignedCallSDKVersion: (NSString *)version; diff --git a/CleverTapSDK/CTDomainFactory.h b/CleverTapSDK/CTDomainFactory.h new file mode 100644 index 00000000..40800956 --- /dev/null +++ b/CleverTapSDK/CTDomainFactory.h @@ -0,0 +1,31 @@ +// +// CTDomainFactory.h +// CleverTapSDK +// +// Created by Akash Malhotra on 19/01/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import "CleverTapInstanceConfig.h" +#if CLEVERTAP_SSL_PINNING +#import "CTPinnedNSURLSessionDelegate.h" +#endif + + +@interface CTDomainFactory : NSObject +@property (nonatomic, strong, nullable) NSString *redirectDomain; +@property (nonatomic, strong, nullable) NSString *explictEndpointDomain; +@property (nonatomic, strong, nullable) NSString *redirectNotifViewedDomain; +@property (nonatomic, strong, nullable) NSString *explictNotifViewedEndpointDomain; + +- (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig* _Nonnull)config; +- (void)persistRedirectDomain; +- (void)persistRedirectNotifViewedDomain; +- (void)clearRedirectDomain; + +#if CLEVERTAP_SSL_PINNING +- (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig* _Nonnull)config pinnedNSURLSessionDelegate: (CTPinnedNSURLSessionDelegate* _Nonnull)pinnedNSURLSessionDelegate sslCertNames:(NSArray* _Nonnull)sslCertNames; +#endif +@end + diff --git a/CleverTapSDK/CTDomainFactory.m b/CleverTapSDK/CTDomainFactory.m new file mode 100644 index 00000000..64a3615f --- /dev/null +++ b/CleverTapSDK/CTDomainFactory.m @@ -0,0 +1,137 @@ +// +// CTDomainFactory.m +// CleverTapSDK +// +// Created by Akash Malhotra on 19/01/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import "CTDomainFactory.h" +#import "CTPreferences.h" +#import "CTConstants.h" +#import "CleverTapInstanceConfigPrivate.h" + + +NSString *const REDIRECT_DOMAIN_KEY = @"CLTAP_REDIRECT_DOMAIN_KEY"; +NSString *const REDIRECT_NOTIF_VIEWED_DOMAIN_KEY = @"CLTAP_REDIRECT_NOTIF_VIEWED_DOMAIN_KEY"; + +@interface CTDomainFactory () +@property (nonatomic, strong) CleverTapInstanceConfig *config; + +#if CLEVERTAP_SSL_PINNING +@property(nonatomic, strong) CTPinnedNSURLSessionDelegate *urlSessionDelegate; +@property (nonatomic, strong) NSArray *sslCertNames; +#endif +@end + +@implementation CTDomainFactory + +- (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig* _Nonnull)config { + self = [super init]; + if (self) { + self.config = config; + self.redirectDomain = [self loadRedirectDomain]; + self.redirectNotifViewedDomain = [self loadRedirectNotifViewedDomain]; + } + return self; +} + +#if CLEVERTAP_SSL_PINNING +- (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig* _Nonnull)config pinnedNSURLSessionDelegate: (CTPinnedNSURLSessionDelegate* _Nonnull)pinnedNSURLSessionDelegate sslCertNames:(NSArray* _Nonnull)sslCertNames{ + self = [super init]; + if (self) { + self.config = config; + self.urlSessionDelegate = pinnedNSURLSessionDelegate; + self.sslCertNames = sslCertNames; + self.redirectDomain = [self loadRedirectDomain]; + self.redirectNotifViewedDomain = [self loadRedirectNotifViewedDomain]; + } + return self; +} +#endif + +- (void)clearRedirectDomain { + self.redirectDomain = nil; + self.redirectNotifViewedDomain = nil; + [self persistRedirectDomain]; // if nil persist will remove + self.redirectDomain = [self loadRedirectDomain]; // reload explicit domain if we have one else will be nil + self.redirectNotifViewedDomain = [self loadRedirectNotifViewedDomain]; // reload explicit notification viewe domain if we have one else will be nil +} + +- (NSString *)loadRedirectDomain { + NSString *region = self.config.accountRegion; + if (region) { + region = [region stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].lowercaseString; + if (region.length > 0) { + self.explictEndpointDomain = [NSString stringWithFormat:@"%@.%@", region, kCTApiDomain]; + return self.explictEndpointDomain; + } + } + NSString *proxyDomain = self.config.proxyDomain; + if (proxyDomain) { + proxyDomain = [proxyDomain stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].lowercaseString; + if (proxyDomain.length > 0) { + self.explictEndpointDomain = proxyDomain; + return self.explictEndpointDomain; + } + } + NSString *domain = nil; + if (self.config.isDefaultInstance) { + domain = [CTPreferences getStringForKey:[CTPreferences storageKeyWithSuffix:REDIRECT_DOMAIN_KEY config: self.config] withResetValue:[CTPreferences getStringForKey:REDIRECT_DOMAIN_KEY withResetValue:nil]]; + } else { + domain = [CTPreferences getStringForKey:[CTPreferences storageKeyWithSuffix:REDIRECT_DOMAIN_KEY config: self.config] withResetValue:nil]; + } + return domain; +} + +- (NSString *)loadRedirectNotifViewedDomain { + NSString *region = self.config.accountRegion; + if (region) { + region = [region stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].lowercaseString; + if (region.length > 0) { + self.explictNotifViewedEndpointDomain = [NSString stringWithFormat:@"%@-%@", region, kCTNotifViewedApiDomain]; + return self.explictNotifViewedEndpointDomain; + } + } + NSString *spikyProxyDomain = self.config.spikyProxyDomain; + if (spikyProxyDomain) { + spikyProxyDomain = [spikyProxyDomain stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].lowercaseString; + if (spikyProxyDomain.length > 0) { + self.explictNotifViewedEndpointDomain = spikyProxyDomain; + return self.explictNotifViewedEndpointDomain; + } + } + NSString *domain = nil; + if (self.config.isDefaultInstance) { + domain = [CTPreferences getStringForKey:[CTPreferences storageKeyWithSuffix:REDIRECT_NOTIF_VIEWED_DOMAIN_KEY config: self.config] withResetValue:[CTPreferences getStringForKey:REDIRECT_NOTIF_VIEWED_DOMAIN_KEY withResetValue:nil]]; + } else { + domain = [CTPreferences getStringForKey:[CTPreferences storageKeyWithSuffix:REDIRECT_NOTIF_VIEWED_DOMAIN_KEY config: self.config] withResetValue:nil]; + } + return domain; +} + +- (void)persistRedirectDomain { + if (self.redirectDomain != nil) { + [CTPreferences putString:self.redirectDomain forKey:[CTPreferences storageKeyWithSuffix:REDIRECT_DOMAIN_KEY config: self.config]]; +#if CLEVERTAP_SSL_PINNING + [self.urlSessionDelegate pinSSLCerts:self.sslCertNames forDomains:@[kCTApiDomain, self.redirectDomain]]; +#endif + } else { + [CTPreferences removeObjectForKey:REDIRECT_DOMAIN_KEY]; + [CTPreferences removeObjectForKey:[CTPreferences storageKeyWithSuffix:REDIRECT_DOMAIN_KEY config: self.config]]; + } +} + +- (void)persistRedirectNotifViewedDomain { + if (self.redirectNotifViewedDomain != nil) { + [CTPreferences putString:self.redirectNotifViewedDomain forKey:[CTPreferences storageKeyWithSuffix:REDIRECT_NOTIF_VIEWED_DOMAIN_KEY config: self.config]]; +#if CLEVERTAP_SSL_PINNING + [self.urlSessionDelegate pinSSLCerts:self.sslCertNames forDomains:@[kCTNotifViewedApiDomain, self.redirectNotifViewedDomain]]; +#endif + } else { + [CTPreferences removeObjectForKey:REDIRECT_NOTIF_VIEWED_DOMAIN_KEY]; + [CTPreferences removeObjectForKey:[CTPreferences storageKeyWithSuffix:REDIRECT_NOTIF_VIEWED_DOMAIN_KEY config: self.config]]; + } +} + +@end diff --git a/CleverTapSDK/CTLogger.h b/CleverTapSDK/CTLogger.h index 5cc2b120..b8a8160b 100644 --- a/CleverTapSDK/CTLogger.h +++ b/CleverTapSDK/CTLogger.h @@ -4,5 +4,5 @@ + (void)setDebugLevel:(int)level; + (int)getDebugLevel; - ++ (void)logInternalError:(NSException *)e; @end diff --git a/CleverTapSDK/CTLogger.m b/CleverTapSDK/CTLogger.m index 29ee6547..74f8a713 100644 --- a/CleverTapSDK/CTLogger.m +++ b/CleverTapSDK/CTLogger.m @@ -1,4 +1,5 @@ #import "CTLogger.h" +#import "CTConstants.h" @implementation CTLogger @@ -12,4 +13,8 @@ + (int)getDebugLevel { return _debugLevel; } ++ (void)logInternalError:(NSException *)e { + CleverTapLogDebug(_debugLevel, @"%@: Caught exception in code: %@\n%@", self, e, [e callStackSymbols]); +} + @end diff --git a/CleverTapSDK/CTPreferences.h b/CleverTapSDK/CTPreferences.h index 308f26b1..f859aa76 100644 --- a/CleverTapSDK/CTPreferences.h +++ b/CleverTapSDK/CTPreferences.h @@ -23,6 +23,8 @@ + (BOOL)archiveObject:(id _Nonnull)object forFileName:(NSString *_Nonnull)fileName; -+ (NSString *)storageKeyWithSuffix: (NSString *)suffix config: (CleverTapInstanceConfig*)config; ++ (NSString *_Nonnull)storageKeyWithSuffix: (NSString *_Nonnull)suffix config: (CleverTapInstanceConfig *_Nonnull)config; + ++ (NSString *_Nonnull)filePathfromFileName:(NSString *_Nonnull)filename; @end diff --git a/CleverTapSDK/CTPreferences.m b/CleverTapSDK/CTPreferences.m index daac0856..9c21ccc1 100644 --- a/CleverTapSDK/CTPreferences.m +++ b/CleverTapSDK/CTPreferences.m @@ -177,6 +177,26 @@ + (BOOL)archiveObject:(id)object forFileName:(NSString *)filename { return success; } +//+ (void)encodeAndArchiveObject:(NSDictionary *)objectMap forFileName:(NSString *)filename { +// NSMutableData *data = [[NSMutableData alloc] init]; +//#pragma clang diagnostic push +//#pragma clang diagnostic ignored "-Wdeprecated-declarations" +// NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; +//#pragma clang diagnostic pop +// +// [objectMap enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { +// [archiver encodeObject:value forKey:key]; +// }]; +// [archiver finishEncoding]; +// +// NSString *filePath = [self filePathfromFileName:filename]; +// NSError *writeError = nil; +// [data writeToFile:filePath options:NSDataWritingAtomic error:&writeError]; +// if (writeError) { +// CleverTapLogStaticInternal(@"%@ failed to write data at %@: %@", self, filePath, writeError); +// } +//} + + (NSString *)storageKeyWithSuffix: (NSString *)suffix config: (CleverTapInstanceConfig*)config { return [NSString stringWithFormat:@"%@:%@", config.accountId, suffix]; } diff --git a/CleverTapSDK/CTRequest.h b/CleverTapSDK/CTRequest.h new file mode 100644 index 00000000..bb9af88f --- /dev/null +++ b/CleverTapSDK/CTRequest.h @@ -0,0 +1,28 @@ +// +// CTRequest.h +// CleverTapSDK +// +// Created by Akash Malhotra on 09/01/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import "CleverTapInstanceConfig.h" + +typedef void (^CTNetworkResponseBlock)(NSData * _Nullable data, NSURLResponse *_Nullable response); +typedef void (^CTNetworkResponseErrorBlock)(NSError * _Nullable error); + +@interface CTRequest : NSObject + +- (CTRequest *_Nonnull)initWithHttpMethod:(NSString *_Nonnull)httpMethod config:(CleverTapInstanceConfig *_Nonnull)config params:(id _Nullable)params url:(NSString *_Nonnull)url; + +- (void)onResponse:(CTNetworkResponseBlock _Nonnull)responseBlock; +- (void)onError:(CTNetworkResponseErrorBlock _Nonnull)errorBlock; +//- (void)send:(NSURLSession *_Nonnull)urlSession; + +@property (nonatomic, strong, nonnull) NSMutableURLRequest *urlRequest; +@property (nonatomic, strong, nonnull) CTNetworkResponseBlock responseBlock; +@property (nonatomic, strong, nullable) CTNetworkResponseErrorBlock errorBlock; + +@end + diff --git a/CleverTapSDK/CTRequest.m b/CleverTapSDK/CTRequest.m new file mode 100644 index 00000000..b8734910 --- /dev/null +++ b/CleverTapSDK/CTRequest.m @@ -0,0 +1,66 @@ +// +// CTRequest.m +// CleverTapSDK +// +// Created by Akash Malhotra on 09/01/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import "CTRequest.h" +#import "CTConstants.h" +#import "CTUtils.h" + +NSString *const ACCOUNT_ID_HEADER = @"X-CleverTap-Account-Id"; +NSString *const ACCOUNT_TOKEN_HEADER = @"X-CleverTap-Token"; + +@interface CTRequest() + +@property (nonatomic, strong, nullable) id params; +@property (nonatomic, strong) NSString *httpMethod; +@property (nonatomic, strong) CleverTapInstanceConfig *config; +@property (nonatomic, strong) NSString *url; + + +@end + +@implementation CTRequest + +- (CTRequest *_Nonnull)initWithHttpMethod:(NSString *_Nonnull)httpMethod config:(CleverTapInstanceConfig *_Nonnull)config params:(id _Nullable)params url:(NSString *_Nonnull)url { + self = [super init]; + if (self) { + _httpMethod = httpMethod; + _params = params; + _config = config; + _url = url; + _urlRequest = [self createURLRequest]; + } + return self; +} + +- (NSMutableURLRequest *)createURLRequest { + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:_url]]; + NSString *accountId = self.config.accountId; + NSString *accountToken = self.config.accountToken; + if (accountId) { + [request setValue:accountId forHTTPHeaderField:ACCOUNT_ID_HEADER]; + } + if (accountToken) { + [request setValue:accountToken forHTTPHeaderField:ACCOUNT_TOKEN_HEADER]; + } + if ([_httpMethod isEqualToString:@"POST"] && _params > 0) { + NSString *jsonBody = [CTUtils jsonObjectToString:_params]; + request.HTTPBody = [jsonBody dataUsingEncoding:NSUTF8StringEncoding]; + request.HTTPMethod = @"POST"; + } + return request; +} + +- (void)onResponse:(CTNetworkResponseBlock _Nonnull)responseBlock { + _responseBlock = responseBlock; +} + +- (void)onError:(CTNetworkResponseErrorBlock _Nonnull)errorBlock { + _errorBlock = errorBlock; +} + +@end diff --git a/CleverTapSDK/CTRequestFactory.h b/CleverTapSDK/CTRequestFactory.h new file mode 100644 index 00000000..9586ba7b --- /dev/null +++ b/CleverTapSDK/CTRequestFactory.h @@ -0,0 +1,20 @@ +// +// CTRequestFactory.h +// CleverTapSDK +// +// Created by Akash Malhotra on 09/01/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import "CTRequest.h" +#import "CleverTapInstanceConfig.h" + +@interface CTRequestFactory : NSObject + ++ (CTRequest *_Nonnull)helloRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config; ++ (CTRequest *_Nonnull)eventRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config params:(id _Nullable)params url:(NSString *_Nonnull)url; ++ (CTRequest *_Nonnull)syncVarsRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config params:(id _Nullable)params url:(NSString *_Nonnull)url; +@end + + diff --git a/CleverTapSDK/CTRequestFactory.m b/CleverTapSDK/CTRequestFactory.m new file mode 100644 index 00000000..2b0d436a --- /dev/null +++ b/CleverTapSDK/CTRequestFactory.m @@ -0,0 +1,39 @@ +// +// CTRequestFactory.m +// CleverTapSDK +// +// Created by Akash Malhotra on 09/01/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import "CTRequestFactory.h" +#import "CTConstants.h" + +@implementation CTRequestFactory + ++ (CTRequest *_Nonnull)helloRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config { + + return +// [CTRequestFactory createGetForParams:params]; + [[CTRequest alloc]initWithHttpMethod:@"GET" config:config params:nil url:@"https://eu1.clevertap-prod.com/hello"]; +} + ++ (CTRequest *_Nonnull)eventRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config params:(id _Nullable)params url:(NSString *_Nonnull)url { + return [[CTRequest alloc]initWithHttpMethod:@"POST" config:config params: params url:url]; +} + ++ (CTRequest *_Nonnull)syncVarsRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config params:(id _Nullable)params url:(NSString *_Nonnull)url { + return [[CTRequest alloc]initWithHttpMethod:@"POST" config:config params: params url:url]; +} + +#pragma mark Private methods + +//+ (CTRequest *)createGetForParams:(NSDictionary *)params { +// return [CTRequest getForParams:params]; +//} +// +//+ (CTRequest *)createPostForApiMethod:(NSString *)apiMethod params:(NSDictionary *)params { +// return [CTRequest postForParams:params]; +//} + +@end diff --git a/CleverTapSDK/CTRequestSender.h b/CleverTapSDK/CTRequestSender.h new file mode 100644 index 00000000..aaf5dd50 --- /dev/null +++ b/CleverTapSDK/CTRequestSender.h @@ -0,0 +1,26 @@ +// +// CTRequestSender.h +// CleverTapSDK +// +// Created by Akash Malhotra on 11/01/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import "CTRequest.h" +//#import "CTDomainFactory.h" +#if CLEVERTAP_SSL_PINNING +#import "CTPinnedNSURLSessionDelegate.h" +#endif + +@interface CTRequestSender : NSObject +//+ (instancetype _Nullable)sharedInstance; +//- (void)setUpUrlSession; +- (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig *_Nonnull)config redirectDomain:(NSString* _Nonnull)redirectDomain; +- (void)send:(CTRequest *_Nonnull)ctRequest; + +#if CLEVERTAP_SSL_PINNING +- (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig *_Nonnull)config redirectDomain:(NSString* _Nonnull)redirectDomain pinnedNSURLSessionDelegate: (CTPinnedNSURLSessionDelegate* _Nonnull)pinnedNSURLSessionDelegate sslCertNames:(NSArray* _Nonnull)sslCertNames; +#endif +@end + diff --git a/CleverTapSDK/CTRequestSender.m b/CleverTapSDK/CTRequestSender.m new file mode 100644 index 00000000..538b23bf --- /dev/null +++ b/CleverTapSDK/CTRequestSender.m @@ -0,0 +1,118 @@ +// +// CTRequestSender.m +// CleverTapSDK +// +// Created by Akash Malhotra on 11/01/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import "CTRequestSender.h" +#import "CTConstants.h" + +#if CLEVERTAP_SSL_PINNING +#import "CTPinnedNSURLSessionDelegate.h" +#endif + +@interface CTRequestSender () +@property (nonatomic, strong) CleverTapInstanceConfig *config; +@property (nonatomic, strong) NSURLSession *urlSession; +@property (nonatomic, strong) NSString *redirectDomain; +@property (nonatomic, assign, readonly) BOOL sslPinningEnabled; + + +#if CLEVERTAP_SSL_PINNING +@property(nonatomic, strong) CTPinnedNSURLSessionDelegate *urlSessionDelegate; +@property (nonatomic, strong) NSArray *sslCertNames; +#endif +@end + +//NSURLSession *urlSession; +//BOOL sslPinningEnabled; + +@implementation CTRequestSender + +//+ (instancetype)sharedInstance { +// static CTRequestSender *instance = nil; +// static dispatch_once_t onceToken; +// dispatch_once(&onceToken, ^{ +// instance = [[self alloc] init]; +// }); +// return instance; +//} + +- (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig *_Nonnull)config redirectDomain:(NSString* _Nonnull)redirectDomain { + + if ((self = [super init])) { + self.config = config; +//#if CLEVERTAP_SSL_PINNING +// // Only pin anchor/CA certificates +// _sslCertNames = @[@"AmazonRootCA1"]; +//#endif + self.redirectDomain = redirectDomain; + [self setUpUrlSession]; + + } + return self; +} + +#if CLEVERTAP_SSL_PINNING +- (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig *_Nonnull)config redirectDomain:(NSString* _Nonnull)redirectDomain pinnedNSURLSessionDelegate: (CTPinnedNSURLSessionDelegate* _Nonnull)pinnedNSURLSessionDelegate sslCertNames:(NSArray* _Nonnull)sslCertNames { + if ((self = [super init])) { + self.config = config; + self.urlSessionDelegate = pinnedNSURLSessionDelegate; + self.sslCertNames = sslCertNames; + self.redirectDomain = redirectDomain; + [self setUpUrlSession]; + + } + return self; +} +#endif + +- (void)setUpUrlSession { + if (!_urlSession) { + NSURLSessionConfiguration *sc = [NSURLSessionConfiguration defaultSessionConfiguration]; + [sc setHTTPAdditionalHeaders:@{ + @"Content-Type" : @"application/json; charset=utf-8" + }]; + + sc.timeoutIntervalForRequest = CLTAP_REQUEST_TIME_OUT_INTERVAL; + sc.timeoutIntervalForResource = CLTAP_REQUEST_TIME_OUT_INTERVAL; + [sc setHTTPShouldSetCookies:NO]; + [sc setRequestCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; + +#if CLEVERTAP_SSL_PINNING + _sslPinningEnabled = YES; + self.urlSessionDelegate = [[CTPinnedNSURLSessionDelegate alloc] initWithConfig:self.config]; + NSMutableArray *domains = [NSMutableArray arrayWithObjects:kCTApiDomain, nil]; + if (self.redirectDomain && ![self.redirectDomain isEqualToString:kCTApiDomain]) { + [domains addObject:self.redirectDomain]; + } + // WITH SSL PINNING ENABLED AND REGION NOT SPECIFIED BY THE USER, WE WILL DEFAULT TO EU1 AND PIN THE CERT TO EU1 + else if (!self.redirectDomain) { + [domains addObject:[NSString stringWithFormat:@"eu1.%@", kCTApiDomain]]; + } + [self.urlSessionDelegate pinSSLCerts:_sslCertNames forDomains:domains]; + self.urlSession = [NSURLSession sessionWithConfiguration:sc delegate:self.urlSessionDelegate delegateQueue:nil]; +#else + _sslPinningEnabled = NO; + _urlSession = [NSURLSession sessionWithConfiguration:sc]; +#endif + } +} + +- (void)send:(CTRequest *_Nonnull)ctRequest { + NSURLSessionDataTask *task = [_urlSession + dataTaskWithRequest:ctRequest.urlRequest + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + if (error) { + ctRequest.errorBlock(error); + } + ctRequest.responseBlock(data, response); + }]; + [task resume]; +} + + + +@end diff --git a/CleverTapSDK/CTUtils.h b/CleverTapSDK/CTUtils.h index 7257855a..93656cef 100644 --- a/CleverTapSDK/CTUtils.h +++ b/CleverTapSDK/CTUtils.h @@ -7,5 +7,6 @@ + (BOOL)doesString:(NSString *)s startWith:(NSString *)prefix; + (NSString *)deviceTokenStringFromData:(NSData *)tokenData; + (double)toTwoPlaces:(double)x; - ++ (BOOL)isNullOrEmpty:(id)obj; ++ (NSString *)jsonObjectToString:(id)object; @end diff --git a/CleverTapSDK/CTUtils.m b/CleverTapSDK/CTUtils.m index 3ce9ba22..1aa74c54 100644 --- a/CleverTapSDK/CTUtils.m +++ b/CleverTapSDK/CTUtils.m @@ -80,4 +80,34 @@ + (double)toTwoPlaces:(double)x { return result; } ++ (BOOL)isNullOrEmpty:(id)obj +{ + // Need to check for NSString to support RubyMotion. + // Ruby String respondsToSelector(count) is true for count: in RubyMotion + return obj == nil + || ([obj respondsToSelector:@selector(length)] && [obj length] == 0) + || ([obj respondsToSelector:@selector(count)] + && ![obj isKindOfClass:[NSString class]] && [obj count] == 0); +} + ++ (NSString *)jsonObjectToString:(id)object { + if ([object isKindOfClass:[NSString class]]) { + return object; + } + @try { + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object + options:0 + error:&error]; + if (error) { + return @""; + } + NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + return jsonString; + } + @catch (NSException *exception) { + return @""; + } +} + @end diff --git a/CleverTapSDK/CleverTap+CTVar.h b/CleverTapSDK/CleverTap+CTVar.h new file mode 100644 index 00000000..b984ac8e --- /dev/null +++ b/CleverTapSDK/CleverTap+CTVar.h @@ -0,0 +1,60 @@ +// +// CleverTap+CTVar.h +// CleverTapSDK +// +// Created by Akash Malhotra on 18/02/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import "CleverTap.h" +@class CTVar; + +NS_ASSUME_NONNULL_BEGIN + +@interface CleverTap (Vars) + +- (CTVar *)defineVar:(NSString *)name +NS_SWIFT_NAME(defineVar(name:)); +- (CTVar *)defineVar:(NSString *)name withInt:(int)defaultValue +NS_SWIFT_NAME(defineVar(name:integer:)); +- (CTVar *)defineVar:(NSString *)name withFloat:(float)defaultValue +NS_SWIFT_NAME(defineVar(name:float:)); +- (CTVar *)defineVar:(NSString *)name withDouble:(double)defaultValue +NS_SWIFT_NAME(defineVar(name:double:)); +- (CTVar *)defineVar:(NSString *)name withCGFloat:(CGFloat)cgFloatValue +NS_SWIFT_NAME(defineVar(name:cgFloat:)); +- (CTVar *)defineVar:(NSString *)name withShort:(short)defaultValue +NS_SWIFT_NAME(defineVar(name:short:)); +//- (LPVar *)defineVar:(NSString *)name withChar:(char)defaultValue +//NS_SWIFT_NAME(defineVar(name:integer:)); +- (CTVar *)defineVar:(NSString *)name withBool:(BOOL)defaultValue +NS_SWIFT_NAME(defineVar(name:boolean:)); +- (CTVar *)defineVar:(NSString *)name withString:(nullable NSString *)defaultValue +NS_SWIFT_NAME(defineVar(name:string:)); +- (CTVar *)defineVar:(NSString *)name withNumber:(nullable NSNumber *)defaultValue +NS_SWIFT_NAME(defineVar(name:number:)); +- (CTVar *)defineVar:(NSString *)name withInteger:(NSInteger)defaultValue +NS_SWIFT_NAME(defineVar(name:NSInteger:)); +- (CTVar *)defineVar:(NSString *)name withLong:(long)defaultValue +NS_SWIFT_NAME(defineVar(name:long:)); +- (CTVar *)defineVar:(NSString *)name withLongLong:(long long)defaultValue +NS_SWIFT_NAME(defineVar(name:longLong:)); +- (CTVar *)defineVar:(NSString *)name withUnsignedChar:(unsigned char)defaultValue +NS_SWIFT_NAME(defineVar(name:unsignedChar:)); +- (CTVar *)defineVar:(NSString *)name withUnsignedInt:(unsigned int)defaultValue +NS_SWIFT_NAME(defineVar(name:unsignedInt:)); +- (CTVar *)defineVar:(NSString *)name withUnsignedInteger:(NSUInteger)defaultValue +NS_SWIFT_NAME(defineVar(name:unsignedInteger:)); +- (CTVar *)defineVar:(NSString *)name withUnsignedLong:(unsigned long)defaultValue +NS_SWIFT_NAME(defineVar(name:unsignedLong:)); +- (CTVar *)defineVar:(NSString *)name withUnsignedLongLong:(unsigned long long)defaultValue +NS_SWIFT_NAME(defineVar(name:unsignedLongLong:)); +- (CTVar *)defineVar:(NSString *)name withUnsignedShort:(unsigned short)defaultValue +NS_SWIFT_NAME(defineVar(name:UnsignedShort:)); +- (CTVar *)defineVar:(NSString *)name withDictionary:(nullable NSDictionary *)defaultValue +NS_SWIFT_NAME(defineVar(name:dictionary:)); + +@end + +NS_ASSUME_NONNULL_END diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index f824a8ed..788fc81c 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -31,6 +31,7 @@ @class CleverTapInstanceConfig; @class CleverTapFeatureFlags; @class CleverTapProductConfig; +#import "CTVar.h" #pragma clang diagnostic push #pragma ide diagnostic ignored "OCUnusedMethodInspection" @@ -1317,6 +1318,29 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; */ + (BOOL)isValidCleverTapId:(NSString *_Nullable)cleverTapID; +#pragma mark Product Experiences - Vars + +- (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; + +- (void)syncVariables; + +- (void)syncVariables:(BOOL)isProduction; + +/*! + @method + + @abstract + Forces variables to update from the server. + + @discussion + Forces variables to update from the server. If variables have changed, the appropriate callbacks will fire. Use sparingly as if the app is updated, you'll have to deal with potentially inconsistent state or user experience. + The provided callback has a boolean flag whether the update was successful or not. The callback fires regardless + of whether the variables have changed. + + @param block a callback with a boolean flag whether the update was successful. + */ +- (void)forceContentUpdateWithBlock:(CleverTapForceContentUpdateBlock _Nonnull)block; + @end #pragma clang diagnostic pop diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 370bb1b6..1b744025 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -72,6 +72,12 @@ #import "CleverTapProductConfigPrivate.h" #import "CTProductConfigController.h" +#import "CTVarCache.h" +#import "CTRequestFactory.h" +#import "CTRequestSender.h" +#import "CTDomainFactory.h" +#import "CleverTap+CTVar.h" + #import "CleverTap+SCDomain.h" #import @@ -94,8 +100,6 @@ NSString *const kREDIRECT_NOTIF_VIEWED_HEADER = @"X-WZRK-SPIKY-RD"; NSString *const kMUTE_HEADER = @"X-WZRK-MUTE"; -NSString *const kACCOUNT_ID_HEADER = @"X-CleverTap-Account-Id"; -NSString *const kACCOUNT_TOKEN_HEADER = @"X-CleverTap-Token"; NSString *const kI_KEY = @"CLTAP_I_KEY"; NSString *const kJ_KEY = @"CLTAP_J_KEY"; @@ -201,10 +205,12 @@ @interface CleverTap () { @property (nonatomic, strong) NSMutableArray *profileQueue; @property (nonatomic, strong) NSMutableArray *notificationsQueue; @property (nonatomic, strong) NSURLSession *urlSession; -@property (nonatomic, strong) NSString *redirectDomain; -@property (nonatomic, strong) NSString *explictEndpointDomain; -@property (nonatomic, strong) NSString *redirectNotifViewedDomain; -@property (nonatomic, strong) NSString *explictNotifViewedEndpointDomain; +//@property (nonatomic, strong) NSString *redirectDomain; +//@property (nonatomic, strong) NSString *explictEndpointDomain; +//@property (nonatomic, strong) NSString *redirectNotifViewedDomain; +//@property (nonatomic, strong) NSString *explictNotifViewedEndpointDomain; +@property (nonatomic, strong) CTDomainFactory *domainFactory; +@property (nonatomic, strong) CTRequestSender *requestSender; @property (nonatomic, assign) NSTimeInterval lastMutedTs; @property (nonatomic, assign) int sendQueueFails; @@ -238,7 +244,7 @@ @interface CleverTap () { @property (atomic, weak) id urlDelegate; @property (atomic, weak) id pushNotificationDelegate; @property (atomic, weak) id inAppNotificationDelegate; -@property (atomic, weak) id domainDelegate; +@property (nonatomic, weak) id domainDelegate; #if !CLEVERTAP_NO_INAPP_SUPPORT @property (atomic, weak) id pushPermissionDelegate; #endif @@ -250,6 +256,10 @@ @interface CleverTap () { @property (atomic, assign) BOOL geofenceLocation; @property (nonatomic, strong) NSString *gfSDKVersion; +@property (nonatomic, strong) CTVarCache *varCache; +@property(strong, nonatomic) NSMutableArray *variablesChangedBlocks; +@property(strong, nonatomic) CleverTapForceContentUpdateBlock forceContentUpdateBlock; + - (instancetype)init __unavailable; @end @@ -684,6 +694,11 @@ - (instancetype)initWithConfig:(CleverTapInstanceConfig*)config andCleverTapID:( [self _initProductConfig]; + self.varCache = [[CTVarCache alloc]initWithConfig:self.config deviceInfo:self.deviceInfo]; + + // ADD PE VAR CHANGED LISTENERS + [self addVarListeners]; + [self notifyUserProfileInitialized]; } @@ -802,42 +817,17 @@ - (void)initNetworking { } else { self.lastMutedTs = [CTPreferences getIntForKey:[CTPreferences storageKeyWithSuffix:kLAST_TS_KEY config: self.config] withResetValue:0]; } - self.redirectDomain = [self loadRedirectDomain]; - self.redirectNotifViewedDomain = [self loadRedirectNotifViewedDomain]; - [self setUpUrlSession]; - [self doHandshakeAsync]; -} -- (void)setUpUrlSession { - if (!self.urlSession) { - NSURLSessionConfiguration *sc = [NSURLSessionConfiguration defaultSessionConfiguration]; - [sc setHTTPAdditionalHeaders:@{ - @"Content-Type" : @"application/json; charset=utf-8" - }]; - - sc.timeoutIntervalForRequest = CLTAP_REQUEST_TIME_OUT_INTERVAL; - sc.timeoutIntervalForResource = CLTAP_REQUEST_TIME_OUT_INTERVAL; - [sc setHTTPShouldSetCookies:NO]; - [sc setRequestCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; - #if CLEVERTAP_SSL_PINNING - _sslPinningEnabled = YES; - self.urlSessionDelegate = [[CTPinnedNSURLSessionDelegate alloc] initWithConfig:self.config]; - NSMutableArray *domains = [NSMutableArray arrayWithObjects:kCTApiDomain, nil]; - if (self.redirectDomain && ![self.redirectDomain isEqualToString:kCTApiDomain]) { - [domains addObject:self.redirectDomain]; - } - // WITH SSL PINNING ENABLED AND REGION NOT SPECIFIED BY THE USER, WE WILL DEFAULT TO EU1 AND PIN THE CERT TO EU1 - else if (!self.redirectDomain) { - [domains addObject:[NSString stringWithFormat:@"eu1.%@", kCTApiDomain]]; - } - [self.urlSessionDelegate pinSSLCerts:sslCertNames forDomains:domains]; - self.urlSession = [NSURLSession sessionWithConfiguration:sc delegate:self.urlSessionDelegate delegateQueue:nil]; + self.urlSessionDelegate = [[CTPinnedNSURLSessionDelegate alloc] initWithConfig:self.config]; + self.domainFactory = [[CTDomainFactory alloc]initWithConfig:self.config pinnedNSURLSessionDelegate: self.urlSessionDelegate sslCertNames: sslCertNames]; + self.requestSender = [[CTRequestSender alloc]initWithConfig:self.config redirectDomain:self.domainFactory.redirectDomain pinnedNSURLSessionDelegate: self.urlSessionDelegate sslCertNames: sslCertNames]; #else - _sslPinningEnabled = NO; - self.urlSession = [NSURLSession sessionWithConfiguration:sc]; + self.domainFactory = [[CTDomainFactory alloc]initWithConfig:self.config]; + + self.requestSender = [[CTRequestSender alloc]initWithConfig:self.config redirectDomain:self.domainFactory.redirectDomain]; #endif - } + [self doHandshakeAsyncWithCompletion:nil]; } - (void)setUserSetLocation:(CLLocationCoordinate2D)location { @@ -859,116 +849,36 @@ - (CLLocationCoordinate2D)userSetLocation { # pragma mark - Handshake Handling -- (void)clearRedirectDomain { - self.redirectDomain = nil; - self.redirectNotifViewedDomain = nil; - [self persistRedirectDomain]; // if nil persist will remove - self.redirectDomain = [self loadRedirectDomain]; // reload explicit domain if we have one else will be nil - self.redirectNotifViewedDomain = [self loadRedirectNotifViewedDomain]; // reload explicit notification viewe domain if we have one else will be nil -} - -- (NSString *)loadRedirectDomain { - NSString *region = self.config.accountRegion; - if (region) { - region = [region stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].lowercaseString; - if (region.length > 0) { - self.explictEndpointDomain = [NSString stringWithFormat:@"%@.%@", region, kCTApiDomain]; - return self.explictEndpointDomain; - } - } - NSString *proxyDomain = self.config.proxyDomain; - if (proxyDomain) { - proxyDomain = [proxyDomain stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].lowercaseString; - if (proxyDomain.length > 0) { - self.explictEndpointDomain = proxyDomain; - return self.explictEndpointDomain; - } - } - NSString *domain = nil; - if (self.config.isDefaultInstance) { - domain = [CTPreferences getStringForKey:[CTPreferences storageKeyWithSuffix:kREDIRECT_DOMAIN_KEY config: self.config] withResetValue:[CTPreferences getStringForKey:kREDIRECT_DOMAIN_KEY withResetValue:nil]]; - } else { - domain = [CTPreferences getStringForKey:[CTPreferences storageKeyWithSuffix:kREDIRECT_DOMAIN_KEY config: self.config] withResetValue:nil]; - } - return domain; -} - -- (NSString *)loadRedirectNotifViewedDomain { - NSString *region = self.config.accountRegion; - if (region) { - region = [region stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].lowercaseString; - if (region.length > 0) { - self.explictNotifViewedEndpointDomain = [NSString stringWithFormat:@"%@-%@", region, kCTNotifViewedApiDomain]; - return self.explictNotifViewedEndpointDomain; - } - } - NSString *spikyProxyDomain = self.config.spikyProxyDomain; - if (spikyProxyDomain) { - spikyProxyDomain = [spikyProxyDomain stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].lowercaseString; - if (spikyProxyDomain.length > 0) { - self.explictNotifViewedEndpointDomain = spikyProxyDomain; - return self.explictNotifViewedEndpointDomain; - } - } - NSString *domain = nil; - if (self.config.isDefaultInstance) { - domain = [CTPreferences getStringForKey:[CTPreferences storageKeyWithSuffix:kREDIRECT_NOTIF_VIEWED_DOMAIN_KEY config: self.config] withResetValue:[CTPreferences getStringForKey:kREDIRECT_NOTIF_VIEWED_DOMAIN_KEY withResetValue:nil]]; - } else { - domain = [CTPreferences getStringForKey:[CTPreferences storageKeyWithSuffix:kREDIRECT_NOTIF_VIEWED_DOMAIN_KEY config: self.config] withResetValue:nil]; - } - return domain; -} - -- (void)persistRedirectDomain { - if (self.redirectDomain != nil) { - [CTPreferences putString:self.redirectDomain forKey:[CTPreferences storageKeyWithSuffix:kREDIRECT_DOMAIN_KEY config: self.config]]; -#if CLEVERTAP_SSL_PINNING - [self.urlSessionDelegate pinSSLCerts:sslCertNames forDomains:@[kCTApiDomain, self.redirectDomain]]; -#endif - } else { - [CTPreferences removeObjectForKey:kREDIRECT_DOMAIN_KEY]; - [CTPreferences removeObjectForKey:[CTPreferences storageKeyWithSuffix:kREDIRECT_DOMAIN_KEY config: self.config]]; - } -} - -- (void)persistRedirectNotifViewedDomain { - if (self.redirectNotifViewedDomain != nil) { - [CTPreferences putString:self.redirectNotifViewedDomain forKey:[CTPreferences storageKeyWithSuffix:kREDIRECT_NOTIF_VIEWED_DOMAIN_KEY config: self.config]]; -#if CLEVERTAP_SSL_PINNING - [self.urlSessionDelegate pinSSLCerts:sslCertNames forDomains:@[kCTNotifViewedApiDomain, self.redirectNotifViewedDomain]]; -#endif - } else { - [CTPreferences removeObjectForKey:kREDIRECT_NOTIF_VIEWED_DOMAIN_KEY]; - [CTPreferences removeObjectForKey:[CTPreferences storageKeyWithSuffix:kREDIRECT_NOTIF_VIEWED_DOMAIN_KEY config: self.config]]; - } -} - (void)persistMutedTs { self.lastMutedTs = [NSDate new].timeIntervalSince1970; [CTPreferences putInt:self.lastMutedTs forKey:[CTPreferences storageKeyWithSuffix:kMUTED_TS_KEY config: self.config]]; } - (BOOL)needHandshake { - if ([self isMuted] || self.explictEndpointDomain) { + if ([self isMuted] || self.domainFactory.explictEndpointDomain) { return NO; } - return self.redirectDomain == nil; + return self.domainFactory.redirectDomain == nil; } -- (void)doHandshakeAsync { +- (void)doHandshakeAsyncWithCompletion:(void (^ _Nullable )(void))taskBlock { [self runSerialAsync:^{ if (![self needHandshake]) { - //self.redirectDomain contains value + //self.domainFactory.redirectDomain contains value [self onDomainAvailable]; + if (taskBlock) { + taskBlock(); + } return; } CleverTapLogInternal(self.config.logLevel, @"%@: starting handshake with %@", self, kHANDSHAKE_URL); - NSMutableURLRequest *request = [self createURLRequestFromURL:[[NSURL alloc] initWithString:kHANDSHAKE_URL]]; - request.HTTPMethod = @"GET"; + // Need to simulate a synchronous request dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - NSURLSessionDataTask *task = [self.urlSession - dataTaskWithRequest:request - completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + + CTRequest *ctRequest = [CTRequestFactory helloRequestWithConfig:self.config]; + [ctRequest onResponse:^(NSData * _Nullable data, NSURLResponse * _Nullable response) { + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; if (httpResponse.statusCode == 200) { @@ -981,9 +891,17 @@ - (void)doHandshakeAsync { } else { [self onDomainUnavailable]; } + if (taskBlock) { + taskBlock(); + } + dispatch_semaphore_signal(semaphore); }]; - [task resume]; + [ctRequest onError:^(NSError * _Nullable error) { + [self onDomainUnavailable]; + dispatch_semaphore_signal(semaphore); + }]; + [self.requestSender send:ctRequest]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); }]; } @@ -994,12 +912,12 @@ - (BOOL)updateStateFromResponseHeadersShouldRedirectForNotif:(NSDictionary *)hea @try { NSString *redirectNotifViewedDomain = headers[kREDIRECT_NOTIF_VIEWED_HEADER]; if (redirectNotifViewedDomain != nil) { - NSString *currentDomain = self.redirectNotifViewedDomain; - self.redirectNotifViewedDomain = redirectNotifViewedDomain; - if (![self.redirectNotifViewedDomain isEqualToString:currentDomain]) { + NSString *currentDomain = self.domainFactory.redirectNotifViewedDomain; + self.domainFactory.redirectNotifViewedDomain = redirectNotifViewedDomain; + if (![self.domainFactory.redirectNotifViewedDomain isEqualToString:currentDomain]) { shouldRedirect = YES; - self.redirectNotifViewedDomain = redirectNotifViewedDomain; - [self persistRedirectNotifViewedDomain]; + self.domainFactory.redirectNotifViewedDomain = redirectNotifViewedDomain; + [self.domainFactory persistRedirectNotifViewedDomain]; } } NSString *mutedString = headers[kMUTE_HEADER]; @@ -1020,12 +938,12 @@ - (BOOL)updateStateFromResponseHeadersShouldRedirect:(NSDictionary *)headers { @try { NSString *redirectDomain = headers[kREDIRECT_HEADER]; if (redirectDomain != nil) { - NSString *currentDomain = self.redirectDomain; - self.redirectDomain = redirectDomain; - if (![self.redirectDomain isEqualToString:currentDomain]) { + NSString *currentDomain = self.domainFactory.redirectDomain; + self.domainFactory.redirectDomain = redirectDomain; + if (![self.domainFactory.redirectDomain isEqualToString:currentDomain]) { shouldRedirect = YES; - self.redirectDomain = redirectDomain; - [self persistRedirectDomain]; + self.domainFactory.redirectDomain = redirectDomain; + [self.domainFactory persistRedirectDomain]; //domain changed [self onDomainAvailable]; } @@ -1060,7 +978,7 @@ - (void)handleSendQueueSuccess { - (void)handleSendQueueFail { self.sendQueueFails += 1; if (self.sendQueueFails > 5) { - [self clearRedirectDomain]; + [self.domainFactory clearRedirectDomain]; self.sendQueueFails = 0; } } @@ -1068,28 +986,15 @@ - (void)handleSendQueueFail { #pragma mark - Queue/Dispatch helpers -- (NSMutableURLRequest *)createURLRequestFromURL:(NSURL *)url { - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; - NSString *accountId = self.config.accountId; - NSString *accountToken = self.config.accountToken; - if (accountId) { - [request setValue:accountId forHTTPHeaderField:kACCOUNT_ID_HEADER]; - } - if (accountToken) { - [request setValue:accountToken forHTTPHeaderField:kACCOUNT_TOKEN_HEADER]; - } - return request; -} - - (NSString *)endpointForQueue: (NSMutableArray *)queue { - if (!self.redirectDomain) return nil; + if (!self.domainFactory.redirectDomain) return nil; NSString *accountId = self.config.accountId; NSString *sdkRevision = self.deviceInfo.sdkVersion; NSString *endpointDomain; if (queue == _notificationsQueue) { - endpointDomain = self.redirectNotifViewedDomain; + endpointDomain = self.domainFactory.redirectNotifViewedDomain; } else { - endpointDomain = self.redirectDomain; + endpointDomain = self.domainFactory.redirectDomain; } NSString *endpointUrl = [[NSString alloc] initWithFormat:@"https://%@/a1?os=iOS&t=%@&z=%@", endpointDomain, sdkRevision, accountId]; currentRequestTimestamp = (int) [[[NSDate alloc] init] timeIntervalSince1970]; @@ -1252,26 +1157,6 @@ - (NSDictionary *)generateAppFields { return evtData; } -- (NSString *)jsonObjectToString:(id)object { - if ([object isKindOfClass:[NSString class]]) { - return object; - } - @try { - NSError *error; - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object - options:0 - error:&error]; - if (error) { - return @""; - } - NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; - return jsonString; - } - @catch (NSException *exception) { - return @""; - } -} - - (id)convertDataToPrimitive:(id)event { @try { if ([event isKindOfClass:[NSArray class]]) { @@ -1385,7 +1270,7 @@ - (void)applicationDidEnterBackground:(NSNotification *)notification { - (void)applicationWillEnterForeground:(NSNotificationCenter *)notification { if ([self needHandshake]) { - [self doHandshakeAsync]; + [self doHandshakeAsyncWithCompletion:nil]; } } @@ -1446,6 +1331,7 @@ - (void)_appEnteredBackground { } - (void)recordAppLaunched:(NSString *)caller { + if ([[self class] runningInsideAppExtension]) return; if (self.appLaunchProcessed) { @@ -1453,6 +1339,11 @@ - (void)recordAppLaunched:(NSString *)caller { return; } + // LOAD VARS FROM CACHE BEFORE APP LAUNCHED + [self.varCache setSilent:YES]; + [self.varCache loadDiffs]; + [self.varCache setSilent:NO]; + self.appLaunchProcessed = YES; if (self.config.disableAppLaunchedEvent) { @@ -2703,7 +2594,7 @@ - (void)processEvent:(NSDictionary *)event withType:(CleverTapEventType)eventTyp } } - CleverTapLogDebug(self.config.logLevel, @"%@: New event processed: %@", self, [self jsonObjectToString:mutableEvent]); + CleverTapLogDebug(self.config.logLevel, @"%@: New event processed: %@", self, [CTUtils jsonObjectToString:mutableEvent]); if (eventType == CleverTapEventTypeFetch) { [self flushQueue]; @@ -2726,7 +2617,7 @@ - (void)scheduleQueueFlush { - (void)flushQueue { if ([self needHandshake]) { [self runSerialAsync:^{ - [self doHandshakeAsync]; + [self doHandshakeAsyncWithCompletion:nil]; }]; } [self runSerialAsync:^{ @@ -2890,7 +2781,7 @@ - (void)sendQueue:(NSMutableArray *)queue { CleverTapLogInternal(self.config.logLevel, @"%@: Pending events batch contains: %d items", self, (int) [batch count]); @try { - NSString *jsonBody = [self jsonObjectToString:batchWithHeader]; + NSString *jsonBody = [CTUtils jsonObjectToString:batchWithHeader]; CleverTapLogDebug(self.config.logLevel, @"%@: Sending %@ to servers at %@", self, jsonBody, endpoint); @@ -2901,10 +2792,6 @@ - (void)sendQueue:(NSMutableArray *)queue { return; } - NSMutableURLRequest *request = [self createURLRequestFromURL:[[NSURL alloc] initWithString:endpoint]]; - request.HTTPBody = [jsonBody dataUsingEncoding:NSUTF8StringEncoding]; - request.HTTPMethod = @"POST"; - __block BOOL success = NO; __block NSData *responseData; @@ -2912,15 +2799,11 @@ - (void)sendQueue:(NSMutableArray *)queue { // Need to simulate a synchronous request dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - NSURLSessionDataTask *postDataTask = [self.urlSession - dataTaskWithRequest:request - completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + + CTRequest *ctRequest = [CTRequestFactory eventRequestWithConfig:self.config params:batchWithHeader url:endpoint]; + [ctRequest onResponse:^(NSData * _Nullable data, NSURLResponse * _Nullable response) { responseData = data; - if (error) { - CleverTapLogDebug(self.config.logLevel, @"%@: Network error while sending queue, will retry: %@", self, error.localizedDescription); - } - if ([response isKindOfClass:[NSHTTPURLResponse class]]) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; @@ -2937,10 +2820,20 @@ - (void)sendQueue:(NSMutableArray *)queue { CleverTapLogDebug(self.config.logLevel, @"%@: Got %lu response when sending queue, will retry", self, (long)httpResponse.statusCode); } } - + dispatch_semaphore_signal(semaphore); }]; - [postDataTask resume]; + [ctRequest onError:^(NSError * _Nullable error) { + + if (error) { + CleverTapLogDebug(self.config.logLevel, @"%@: Network error while sending queue, will retry: %@", self, error.localizedDescription); + } + if (self->_forceContentUpdateBlock) { + self->_forceContentUpdateBlock(NO); + } + dispatch_semaphore_signal(semaphore); + }]; + [self.requestSender send:ctRequest]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); if (!success) { @@ -3134,6 +3027,33 @@ - (void)parseResponse:(NSData *)responseData { } #endif + // HANDLE AND CACHE PE VARS + NSDictionary *peVarsJSON = jsonResp[CLTAP_PE_VARS_RESPONSE_KEY]; + + // TODO: REMOVE THIS STATIC JSON WHEN API IS ONLINE + peVarsJSON = @{ + @"Title.Text": @"TitleUpdated", + @"MyMap.name": @"nikoUpdated", + @"MyMap.phone": @"123Updated" + }; + NSString *varsJson = [CTUtils dictionaryToJsonString:peVarsJSON]; + + if (peVarsJSON) { + self.varCache.appLaunchedRecorded = YES; + [self.varCache + applyVariableDiffs:peVarsJSON + messages:nil + variants:nil + localCaps:nil + regions:nil + variantDebugInfo:nil + varsJson:varsJson + varsSignature:nil]; + if (_forceContentUpdateBlock) { + _forceContentUpdateBlock(YES); + } + } + // Handle events/profiles sync data @try { NSDictionary *evpr = jsonResp[@"evpr"]; @@ -3378,6 +3298,8 @@ - (void) _asyncSwitchUser:(NSDictionary *)properties withCachedGuid:(NSString *) [self _resetProductConfig]; + [self _resetVars]; + // push data on reset profile [self recordAppLaunched:action]; if (properties) { @@ -4029,13 +3951,14 @@ + (void)setCredentialsWithAccountID:(NSString *)accountID token:(NSString *)toke + (void)setCredentialsWithAccountID:(NSString *)accountID token:(NSString *)token proxyDomain:(NSString *)proxyDomain spikyProxyDomain:(NSString *)spikyProxyDomain { [self _setCredentialsWithAccountID:accountID token:token proxyDomain:proxyDomain]; + NSString *finalSpikyProxyDomain; if (spikyProxyDomain != nil && ![spikyProxyDomain isEqualToString:@""]) { - spikyProxyDomain = [spikyProxyDomain stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - if (spikyProxyDomain.length <= 0) { - spikyProxyDomain = nil; + finalSpikyProxyDomain = [spikyProxyDomain stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if (finalSpikyProxyDomain.length <= 0) { + finalSpikyProxyDomain = nil; } } - [_plistInfo setCredentialsWithAccountID:accountID token:token proxyDomain:proxyDomain spikyProxyDomain:spikyProxyDomain]; + [_plistInfo setCredentialsWithAccountID:accountID token:token proxyDomain:proxyDomain spikyProxyDomain:finalSpikyProxyDomain]; } + (void)enablePersonalization { @@ -4805,6 +4728,15 @@ - (void)_resetProductConfig { } } +// run off main +- (void)_resetVars { + if (self.config && self.deviceInfo.deviceId) { + self.varCache = [[CTVarCache alloc]initWithConfig:self.config deviceInfo:self.deviceInfo]; + self.variablesChangedBlocks = [NSMutableArray array]; + _forceContentUpdateBlock = nil; + } +} + - (NSDictionary *)_setProductConfig:(NSDictionary *)arp { if (arp) { NSMutableDictionary *configOptions = [NSMutableDictionary new]; @@ -4970,8 +4902,8 @@ - (void)onDomainUnavailable { //Updates the format of the domain - from `in1.clevertap-prod.com` to region.auth.domain (i.e. in1.auth.clevertap-prod.com) - (NSString *)getDomainString { - if (self.redirectDomain != nil) { - NSArray *listItems = [self.redirectDomain componentsSeparatedByString:@"."]; + if (self.domainFactory.redirectDomain != nil) { + NSArray *listItems = [self.domainFactory.redirectDomain componentsSeparatedByString:@"."]; NSString *domainItem = [listItems[0] stringByAppendingString:@".auth"]; for (int i = 1; i < listItems.count; i++ ) { NSString *dotString = [@"." stringByAppendingString: listItems[i]]; @@ -5129,4 +5061,287 @@ + (BOOL)isValidCleverTapId:(NSString *_Nullable)cleverTapID { return [CTValidator isValidCleverTapId:cleverTapID]; } +#pragma mark - Product Experiences + +- (void)addVarListeners { + [self.varCache onUpdate:^{ + [self triggerVariablesChanged]; + +// if ([LPFileTransferManager sharedInstance].numPendingDownloads == 0) { +// [self triggerVariablesChangedAndNoDownloadsPending]; +// } + }]; +} + + +- (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { + + if (!block) { + CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onValueChanged]."); + return; + } + + CT_TRY + if (!self.variablesChangedBlocks) { + self.variablesChangedBlocks = [NSMutableArray array]; + } + [self.variablesChangedBlocks addObject:[block copy]]; + CT_END_TRY + + if ([self.varCache hasReceivedDiffs]) { + block(); + } +} + +- (void)syncVariables { + +#if DEBUG + if ([self needHandshake]) { + [self runSerialAsync:^{ + [self doHandshakeAsyncWithCompletion:^{ + [self _syncVars]; + }]; + }]; + } + else { + [self runSerialAsync:^{ + [self _syncVars]; + }]; + } +#else + CleverTapLogDebug(_config.logLevel, @"%@: syncChanges can only be called from Debug configurations/builds", self); +#endif +} + +- (void)syncVariables:(BOOL)isProduction { + if (isProduction) { + CleverTapLogDebug(_config.logLevel, @"%@: syncChanges can only be called from Debug configurations/builds", self); + } else { + [self syncVariables]; + } +} + +- (void)_syncVars { + // META + NSDictionary *meta = [self batchHeader]; + // VARSPAYLOAD + NSDictionary *varsPayload = [self varsPayload]; + NSArray *payload = @[meta,varsPayload]; + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + CTRequest *ctRequest = [CTRequestFactory syncVarsRequestWithConfig:self.config params:payload url:[NSString stringWithFormat:@"https://%@/defineVars",self.domainFactory.redirectDomain]]; + + [ctRequest onResponse:^(NSData * _Nullable data, NSURLResponse * _Nullable response) { + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + if (httpResponse.statusCode == 200) { + CleverTapLogDebug(self->_config.logLevel, @"%@: Vars synced successfully", self); + } else { + CleverTapLogDebug(self->_config.logLevel, @"%@: error syncing vars", self); + } + } else { + CleverTapLogDebug(self->_config.logLevel, @"%@: error syncing vars", self); + } + dispatch_semaphore_signal(semaphore); + }]; + [ctRequest onError:^(NSError * _Nullable error) { + CleverTapLogDebug(self->_config.logLevel, @"%@: error syncing vars: %@", self, error.debugDescription); + dispatch_semaphore_signal(semaphore); + }]; + [self.requestSender send:ctRequest]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); +} + +- (NSDictionary*)varsPayload { + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + result[@"type"] = @"varsPayload"; + + NSMutableDictionary *allVars = [NSMutableDictionary dictionary]; + + [self.varCache.vars + enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, CTVar * _Nonnull varValue, BOOL * _Nonnull stop) { + + NSMutableDictionary *varData = [NSMutableDictionary dictionary]; + + if ([varValue.defaultValue isKindOfClass:[NSDictionary class]]) { + NSDictionary *flattenedMap = [self flatten:varValue.defaultValue varName:varValue.name]; + [allVars addEntriesFromDictionary:flattenedMap]; + } + else { + varData[@"type"] = varValue.kind; + varData[@"defaultValue"] = varValue.defaultValue; + allVars[key] = varData; + } + }]; + result[@"vars"] = allVars; + + return result; +} + +- (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName { + NSMutableDictionary *varsPayload = [NSMutableDictionary dictionary]; + + [map enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { + + if ([value isKindOfClass:[NSString class]] || + [value isKindOfClass:[NSNumber class]]) { + NSString *payloadKey = [NSString stringWithFormat:@"%@.%@",varName,key]; + varsPayload[payloadKey] = @{@"defaultValue": value}; + } + else if ([value isKindOfClass:[NSDictionary class]]) { + NSString *payloadKey = [NSString stringWithFormat:@"%@.%@",varName,key]; + + NSDictionary* flattenedMap = [self flatten:value varName:payloadKey]; + [varsPayload addEntriesFromDictionary:flattenedMap]; + } + }]; + + return varsPayload; +} + +- (void)forceContentUpdateWithBlock:(CleverTapForceContentUpdateBlock)block { + _forceContentUpdateBlock = block; + [self queueEvent:@{@"evtName": CLTAP_WZRK_FETCH_EVENT, @"evtData" : @{@"t": @4}} withType:CleverTapEventTypeFetch]; +} + +- (void)triggerVariablesChanged +{ + // TODO: CHECK IF THIS IS NEEDED +// for (NSInvocation *invocation in [LPInternalState sharedState] +// .variablesChangedResponders.copy) { +// [invocation invoke]; +// } + + for (CleverTapVariablesChangedBlock block in self.variablesChangedBlocks.copy) { + block(); + } +} + +- (CTVar *)defineVar:(NSString *)name { + return [self.varCache define:name with:nil kind:nil]; +} + +- (CTVar *)defineVar:(NSString *)name withInt:(int)defaultValue +{ + return [self.varCache define:name with:[NSNumber numberWithInt:defaultValue] kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withFloat:(float)defaultValue +{ + return [self.varCache define:name with:[NSNumber numberWithFloat:defaultValue] kind:CT_KIND_FLOAT]; +} + +- (CTVar *)defineVar:(NSString *)name withDouble:(double)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithDouble:defaultValue] + kind:CT_KIND_FLOAT]; +} + +- (CTVar *)defineVar:(NSString *)name withCGFloat:(CGFloat)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithDouble:defaultValue] + kind:CT_KIND_FLOAT]; +} + +- (CTVar *)defineVar:(NSString *)name withShort:(short)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithShort:defaultValue] + kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withChar:(char)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithChar:defaultValue] + kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withBool:(BOOL)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithBool:defaultValue] + kind:CT_KIND_BOOLEAN]; +} + +- (CTVar *)defineVar:(NSString *)name withInteger:(NSInteger)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithInteger:defaultValue] + kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withLong:(long)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithLong:defaultValue] + kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withLongLong:(long long)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithLongLong:defaultValue] + kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withUnsignedChar:(unsigned char)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithUnsignedChar:defaultValue] + kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withUnsignedInt:(unsigned int)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithUnsignedInt:defaultValue] + kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withUnsignedInteger:(NSUInteger)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithUnsignedInteger:defaultValue] + kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withUnsignedLong:(unsigned long)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithUnsignedLong:defaultValue] + kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withUnsignedLongLong:(unsigned long long)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithUnsignedLongLong:defaultValue] + kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withUnsignedShort:(unsigned short)defaultValue +{ + return [self.varCache define:name + with:[NSNumber numberWithUnsignedShort:defaultValue] + kind:CT_KIND_INT]; +} + +- (CTVar *)defineVar:(NSString *)name withString:(NSString *)defaultValue +{ + return [self.varCache define:name with:defaultValue kind:CT_KIND_STRING]; +} + +- (CTVar *)defineVar:(NSString *)name withNumber:(NSNumber *)defaultValue +{ + return [self.varCache define:name with:defaultValue kind:CT_KIND_FLOAT]; +} + +- (CTVar *)defineVar:(NSString *)name withDictionary:(NSDictionary *)defaultValue +{ + return [self.varCache define:name with:defaultValue kind:CT_KIND_DICTIONARY]; +} + @end diff --git a/CleverTapSDK/ProductConfig/controllers/CTProductConfigController.m b/CleverTapSDK/ProductConfig/controllers/CTProductConfigController.m index bfde4a3c..e1870d66 100644 --- a/CleverTapSDK/ProductConfig/controllers/CTProductConfigController.m +++ b/CleverTapSDK/ProductConfig/controllers/CTProductConfigController.m @@ -1,7 +1,6 @@ #import "CTProductConfigController.h" #import "CTConstants.h" #import "CTPreferences.h" -#import "CTPreferences.h" #import "CleverTapInstanceConfig.h" #import "CleverTapProductConfigPrivate.h" diff --git a/CleverTapSDK/ProductExperiences/CTVar-Internal.h b/CleverTapSDK/ProductExperiences/CTVar-Internal.h new file mode 100644 index 00000000..c43dc6d5 --- /dev/null +++ b/CleverTapSDK/ProductExperiences/CTVar-Internal.h @@ -0,0 +1,35 @@ +#import "CTVar.h" +@class CTVarCache; + +NS_ASSUME_NONNULL_BEGIN + +@interface CTVar () + +- (instancetype)initWithName:(NSString *)name + withComponents:(NSArray *)components + withDefaultValue:(NSObject *)defaultValue + withKind:(NSString *)kind + varCache:(CTVarCache *)cache; + +@property (readonly) BOOL isInternal; +@property (readonly, strong) NSString *name; +@property (readonly, strong) NSArray *nameComponents; +@property (readonly) BOOL hadStarted; +@property (readonly, strong) NSString *kind; +//@property (readonly, strong) NSMutableArray *fileReadyBlocks; +@property (readonly, strong) NSMutableArray *valueChangedBlocks; +@property (readonly) BOOL fileIsPending; +@property (nonatomic, unsafe_unretained, nullable) id delegate; +@property (readonly) BOOL hasChanged; + +- (void) update; +- (void) cacheComputedValues; +//- (void) triggerFileIsReady; +- (void) triggerValueChanged; + ++(BOOL)printedCallbackWarning; ++(void)setPrintedCallbackWarning:(BOOL)newPrintedCallbackWarning; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CleverTapSDK/ProductExperiences/CTVar.h b/CleverTapSDK/ProductExperiences/CTVar.h new file mode 100644 index 00000000..b634fc3a --- /dev/null +++ b/CleverTapSDK/ProductExperiences/CTVar.h @@ -0,0 +1,168 @@ +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^CleverTapVariablesChangedBlock)(void); +typedef void (^CleverTapForceContentUpdateBlock)(BOOL success); + +@class CTVar; + +/** + * Receives callbacks for {@link CTVar} + */ +NS_SWIFT_NAME(VarDelegate) +@protocol LPVarDelegate +@optional +/** + * For file variables, called when the file is ready. + */ +//- (void)fileIsReady:(CTVar *)var; +/** + * Called when the value of the variable changes. + */ +- (void)valueDidChange:(CTVar *)var; +@end + +/** + * A variable is any part of your application that can change from an experiment. + * Check out {@link Macros the macros} for defining variables more easily. + */ +NS_SWIFT_NAME(Var) +@interface CTVar : NSObject + +@property (readonly, strong, nullable) NSString *stringValue; +@property (readonly, strong, nullable) NSNumber *numberValue; +@property (readonly, strong, nullable) id value; +@property (readonly, strong, nullable) id defaultValue; + +/** + * @{ + * Defines a {@link LPVar} + */ +- (instancetype)init NS_UNAVAILABLE; + +//+ (LPVar *)define:(NSString *)name +//NS_SWIFT_NAME(init(name:)); +//+ (LPVar *)define:(NSString *)name withInt:(int)defaultValue +//NS_SWIFT_NAME(init(name:integer:)); +//+ (LPVar *)define:(NSString *)name withFloat:(float)defaultValue +//NS_SWIFT_NAME(init(name:float:)); +//+ (LPVar *)define:(NSString *)name withDouble:(double)defaultValue +//NS_SWIFT_NAME(init(name:double:)); +//+ (LPVar *)define:(NSString *)name withCGFloat:(CGFloat)cgFloatValue +//NS_SWIFT_NAME(init(name:cgFloat:)); +//+ (LPVar *)define:(NSString *)name withShort:(short)defaultValue +//NS_SWIFT_NAME(init(name:integer:)); +//+ (LPVar *)define:(NSString *)name withChar:(char)defaultValue +//NS_SWIFT_NAME(init(name:integer:)); +//+ (LPVar *)define:(NSString *)name withBool:(BOOL)defaultValue +//NS_SWIFT_NAME(init(name:boolean:)); +//+ (LPVar *)define:(NSString *)name withString:(nullable NSString *)defaultValue +//NS_SWIFT_NAME(init(name:string:)); +//+ (LPVar *)define:(NSString *)name withNumber:(nullable NSNumber *)defaultValue +//NS_SWIFT_NAME(init(name:number:)); +//+ (LPVar *)define:(NSString *)name withInteger:(NSInteger)defaultValue +//NS_SWIFT_NAME(init(name:integer:)); +//+ (LPVar *)define:(NSString *)name withLong:(long)defaultValue +//NS_SWIFT_NAME(init(name:integer:)); +//+ (LPVar *)define:(NSString *)name withLongLong:(long long)defaultValue +//NS_SWIFT_NAME(init(name:integer:)); +//+ (LPVar *)define:(NSString *)name withUnsignedChar:(unsigned char)defaultValue +//NS_SWIFT_NAME(init(name:uinteger:)); +//+ (LPVar *)define:(NSString *)name withUnsignedInt:(unsigned int)defaultValue +//NS_SWIFT_NAME(init(name:uinteger:)); +//+ (LPVar *)define:(NSString *)name withUnsignedInteger:(NSUInteger)defaultValue +//NS_SWIFT_NAME(init(name:uinteger:)); +//+ (LPVar *)define:(NSString *)name withUnsignedLong:(unsigned long)defaultValue +//NS_SWIFT_NAME(init(name:uinteger:)); +//+ (LPVar *)define:(NSString *)name withUnsignedLongLong:(unsigned long long)defaultValue +//NS_SWIFT_NAME(init(name:uinteger:)); +//+ (LPVar *)define:(NSString *)name withUnsignedShort:(unsigned short)defaultValue +//NS_SWIFT_NAME(init(name:uinteger:)); +//+ (LPVar *)define:(NSString *)name withFile:(nullable NSString *)defaultFilename +//NS_SWIFT_NAME(init(name:file:)); +//+ (LPVar *)define:(NSString *)name withDictionary:(nullable NSDictionary *)defaultValue +//NS_SWIFT_NAME(init(name:dictionary:)); +//+ (LPVar *)define:(NSString *)name withArray:(nullable NSArray *)defaultValue +//NS_SWIFT_NAME(init(name:array:)); +//+ (LPVar *)define:(NSString *)name withColor:(nullable UIColor *)defaultValue +//NS_SWIFT_NAME(init(name:color:)); +/**@}*/ + +/** + * Returns the name of the variable. + */ +- (NSString *)name; + +/** + * Returns the components of the variable's name. + */ +- (NSArray *)nameComponents; + +/** + * Returns the default value of a variable. + */ +- (nullable id)defaultValue; + +/** + * Returns the kind of the variable. + */ +- (NSString *)kind; + +/** + * Returns whether the variable has changed since the last time the app was run. + */ +- (BOOL)hasChanged; + +/** + * For file variables, called when the file is ready. + */ +//- (void)onFileReady:(CleverTapVariablesChangedBlock)block; + +/** + * Called when the value of the variable changes. + */ +- (void)onValueChanged:(CleverTapVariablesChangedBlock)block; + +/** + * Sets the delegate of the variable in order to use + * {@link LPVarDelegate::fileIsReady:} and {@link LPVarDelegate::valueDidChange:} + */ +- (void)setDelegate:(nullable id )delegate; + +/** + * @{ + * Accessess the value(s) of the variable + */ +- (id)objectForKey:(nullable NSString *)key; +- (id)objectAtIndex:(NSUInteger )index; +- (id)objectForKeyPath:(nullable id)firstComponent, ... NS_REQUIRES_NIL_TERMINATION; +- (id)objectForKeyPathComponents:(nullable NSArray *)pathComponents; +- (NSUInteger)count; + +- (nullable NSNumber *)numberValue; +- (nullable NSString *)stringValue; +//- (nullable NSString *)fileValue; +//- (nullable UIImage *)imageValue; +- (int)intValue; +- (double)doubleValue; +- (CGFloat)cgFloatValue; +- (float)floatValue; +- (short)shortValue; +- (BOOL)boolValue; +- (char)charValue; +- (long)longValue; +- (long long)longLongValue; +- (NSInteger)integerValue; +- (unsigned char)unsignedCharValue; +- (unsigned short)unsignedShortValue; +- (unsigned int)unsignedIntValue; +- (NSUInteger)unsignedIntegerValue; +- (unsigned long)unsignedLongValue; +- (unsigned long long)unsignedLongLongValue; +//- (nullable UIColor *)colorValue; +/**@}*/ +@end + +NS_ASSUME_NONNULL_END diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m new file mode 100644 index 00000000..66411ec6 --- /dev/null +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -0,0 +1,497 @@ +#import "CTVar-Internal.h" +#import "CTVarCache.h" +#import "CTConstants.h" +// TODO: commented files +//#import "LPFileManager.h" + +static BOOL LPVAR_PRINTED_CALLBACK_WARNING = NO; +CTVarCache *varCache; +@interface CTVar (PrivateProperties) + + +@property (nonatomic) BOOL isInternal; +@property (nonatomic, strong) NSString *name; +@property (nonatomic, strong) NSArray *nameComponents; +@property (nonatomic, strong) NSString *stringValue; +@property (nonatomic, strong) NSNumber *numberValue; +@property (nonatomic) BOOL hadStarted; +@property (nonatomic, strong) id value; +@property (nonatomic, strong) id defaultValue; +@property (nonatomic, strong) NSString *kind; +@property (nonatomic, strong) NSMutableArray *fileReadyBlocks; +@property (nonatomic, strong) NSMutableArray *valueChangedBlocks; +//@property (nonatomic, strong) LPVarCache *varCache; +@property (nonatomic) BOOL fileIsPending; +@property (nonatomic) BOOL hasChanged; + +@end + +@implementation CTVar + +@synthesize stringValue=_stringValue; +@synthesize numberValue=_numberValue; +@synthesize hadStarted=_hadStarted; +@synthesize hasChanged=_hasChanged; + ++(BOOL)printedCallbackWarning +{ + return LPVAR_PRINTED_CALLBACK_WARNING; +} + ++(void)setPrintedCallbackWarning:(BOOL)newPrintedCallbackWarning +{ + LPVAR_PRINTED_CALLBACK_WARNING = newPrintedCallbackWarning; +} + +- (instancetype)initWithName:(NSString *)name withComponents:(NSArray *)components + withDefaultValue:(NSNumber *)defaultValue withKind:(NSString *)kind varCache:(CTVarCache *)cache +{ + self = [super init]; + if (self) { + CT_TRY + _name = name; + varCache = cache; + _nameComponents = [varCache getNameComponents:name]; + _defaultValue = defaultValue; + _value = defaultValue; + _kind = kind; + [self cacheComputedValues]; + + [varCache registerVariable:self]; + + // TODO: Commented file types +// if ([kind isEqualToString:LP_KIND_FILE]) { // TODO: && var.stringValue) +// [[LPVarCache sharedCache] registerFile:_stringValue withDefaultValue:_defaultValue]; +// } +// if ([name hasPrefix:LP_VALUE_RESOURCES_VARIABLE]) { +// _isInternal = YES; +// } + [self update]; + CT_END_TRY + } + return self; +} + +#pragma mark Defines + +//+ (LPVar *)define:(NSString *)name +//{ +// return [[LPVarCache sharedCache] define:name with:nil kind:nil]; +//} +// +//+ (LPVar *)define:(NSString *)name withInt:(int)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name with:[NSNumber numberWithInt:defaultValue] kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withFloat:(float)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name with:[NSNumber numberWithFloat:defaultValue] kind:LP_KIND_FLOAT]; +//} +// +//+ (LPVar *)define:(NSString *)name withDouble:(double)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithDouble:defaultValue] +// kind:LP_KIND_FLOAT]; +//} +// +//+ (LPVar *)define:(NSString *)name withCGFloat:(CGFloat)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithDouble:defaultValue] +// kind:LP_KIND_FLOAT]; +//} +// +//+ (LPVar *)define:(NSString *)name withShort:(short)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithShort:defaultValue] +// kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withChar:(char)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithChar:defaultValue] +// kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withBool:(BOOL)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithBool:defaultValue] +// kind:LP_KIND_BOOLEAN]; +//} +// +//+ (LPVar *)define:(NSString *)name withInteger:(NSInteger)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithInteger:defaultValue] +// kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withLong:(long)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithLong:defaultValue] +// kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withLongLong:(long long)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithLongLong:defaultValue] +// kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withUnsignedChar:(unsigned char)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithUnsignedChar:defaultValue] +// kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withUnsignedInt:(unsigned int)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithUnsignedInt:defaultValue] +// kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withUnsignedInteger:(NSUInteger)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithUnsignedInteger:defaultValue] +// kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withUnsignedLong:(unsigned long)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithUnsignedLong:defaultValue] +// kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withUnsignedLongLong:(unsigned long long)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithUnsignedLongLong:defaultValue] +// kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withUnsignedShort:(unsigned short)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name +// with:[NSNumber numberWithUnsignedShort:defaultValue] +// kind:LP_KIND_INT]; +//} +// +//+ (LPVar *)define:(NSString *)name withString:(NSString *)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name with:defaultValue kind:LP_KIND_STRING]; +//} +// +//+ (LPVar *)define:(NSString *)name withNumber:(NSNumber *)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name with:defaultValue kind:LP_KIND_FLOAT]; +//} + +// TODO: commented files +//+ (LPVar *)define:(NSString *)name withFile:(NSString *)defaultFilename +//{ +// return [[LPVarCache sharedCache] define:name with:defaultFilename kind:LP_KIND_FILE]; +//} + +//+ (LPVar *)define:(NSString *)name withDictionary:(NSDictionary *)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name with:defaultValue kind:LP_KIND_DICTIONARY]; +//} +// +//+ (LPVar *)define:(NSString *)name withArray:(NSArray *)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name with:defaultValue kind:LP_KIND_ARRAY]; +//} + +// TODO: commented color +//+ (LPVar *)define:(NSString *)name withColor:(UIColor *)defaultValue +//{ +// return [[LPVarCache sharedCache] define:name with:@(leanplum_colorToInt(defaultValue)) kind:LP_KIND_COLOR]; +//} + +#pragma mark Updating + +- (void) cacheComputedValues +{ + // Cache computed values. + if ([_value isKindOfClass:NSString.class]) { + _stringValue = (NSString *) _value; + _numberValue = [NSNumber numberWithDouble:[_stringValue doubleValue]]; + } else if ([_value isKindOfClass:NSNumber.class]) { + _stringValue = [NSString stringWithFormat:@"%@", _value]; + _numberValue = (NSNumber *) _value; + } else { + _stringValue = nil; + _numberValue = nil; + } +} + +- (void)update +{ + NSObject *oldValue = _value; + _value = [varCache getMergedValueFromComponentArray:_nameComponents]; + + //TODO: hadStarted logic + if ([_value isEqual:oldValue] && _hadStarted) { + return; + } + [self cacheComputedValues]; + + if (![_value isEqual:oldValue]) { + _hasChanged = YES; + } + + // TODO: commented files +// if ([LPVarCache sharedCache].silent && [[self name] hasPrefix:LP_VALUE_RESOURCES_VARIABLE] +// && [_kind isEqualToString:LP_KIND_FILE] && !_fileIsPending) { +// [self triggerFileIsReady]; +// } + + if (varCache.silent) { + return; + } + + // TODO: trigger value changed callback + // TODO: Add hasStarted equivalent logic +// if ([LPInternalState sharedState].hasStarted) { + if (varCache.appLaunchedRecorded) { + [self triggerValueChanged]; + } + + // TODO: commented files + // Check if file exists, otherwise we need to download it. + // Ignore app icon. This is a special variable that only needs the filename. +// if ([_kind isEqualToString:LP_KIND_FILE]) { +// if ([LPFileManager maybeDownloadFile:_stringValue +// defaultValue:_defaultValue +// onComplete:^{[self triggerFileIsReady];}]) { +// _fileIsPending = YES; +// } +// if ([LPInternalState sharedState].hasStarted && !_fileIsPending) { +// [self triggerFileIsReady]; +// } +// } + + //TODO: hadStarted logic + // TODO: Add hasStarted equivalent logic + if (varCache.appLaunchedRecorded) { + _hadStarted = YES; + } +} + +#pragma mark Basic accessors + +- (void)triggerValueChanged +{ + + // TODO: will we provide both protocol valueDidChange method and valueChanged callback block? + if (self.delegate && + [self.delegate respondsToSelector:@selector(valueDidChange:)]) { + [self.delegate valueDidChange:self]; + } + + for (CleverTapVariablesChangedBlock block in _valueChangedBlocks.copy) { + block(); + } +} + +- (void)onValueChanged:(CleverTapVariablesChangedBlock)block +{ + if (!block) { + CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CTVar onValueChanged]."); + return; + } + + CT_TRY + if (!_valueChangedBlocks) { + _valueChangedBlocks = [NSMutableArray array]; + } + [_valueChangedBlocks addObject:[block copy]]; + + // TODO: Add hasStarted equivalent logic +// if ([LPInternalState sharedState].hasStarted) { + if (varCache.appLaunchedRecorded) { + [self triggerValueChanged]; + } + CT_END_TRY +} + +#pragma mark File handling + +// TODO: commented files +//- (void)triggerFileIsReady +//{ +// _fileIsPending = NO; +// LP_BEGIN_USER_CODE +// if (self.delegate && +// [self.delegate respondsToSelector:@selector(fileIsReady:)]) { +// [self.delegate fileIsReady:self]; +// } +// +// for (LeanplumVariablesChangedBlock block in _fileReadyBlocks.copy) { +// block(); +// } +// LP_END_USER_CODE +//} + +// TODO: Commented files +//- (void)onFileReady:(LeanplumVariablesChangedBlock)block +//{ +// if (!block) { +// [Leanplum throwError:@"[LPVar onFileReady:] Nil block parameter provided."]; +// } +// +// CT_TRY +// if (!_fileReadyBlocks) { +// _fileReadyBlocks = [NSMutableArray array]; +// } +// [_fileReadyBlocks addObject:[block copy]]; +// if ([LPInternalState sharedState].hasStarted && !_fileIsPending) { +// [self triggerFileIsReady]; +// } +// CT_END_TRY +//} + +// TODO: Check if this method is needed +- (void)setDelegate:(id)delegate +{ +// CT_TRY + _delegate = delegate; + + // TODO: commented files +// if ([LPInternalState sharedState].hasStarted && !_fileIsPending) { +// [self triggerFileIsReady]; +// } +// CT_END_TRY +} + +// TODO: decide if this method is relevant +- (void)warnIfNotStarted +{ + // TODO: Add hasStarted equivalent logic + +// if (!_isInternal && ![LPInternalState sharedState].hasStarted && ![LPVar printedCallbackWarning]) { +// LPLog(LPInfo, @"Leanplum hasn't finished retrieving values from the server. You " +// @"should use a callback to make sure the value for '%@' is ready. Otherwise, your " +// @"app may not use the most up-to-date value.", self.name); + +// [CTVar setPrintedCallbackWarning:YES]; +// } +} + +// TODO: commented fileValue and imagevalue + +//- (NSString *)fileValue +//{ +// CT_TRY +// [self warnIfNotStarted]; +// if ([_kind isEqualToString:LP_KIND_FILE]) { +// return [LPFileManager fileValue:_stringValue withDefaultValue:_defaultValue]; +// } +// CT_END_TRY +// return nil; +//} +// +//- (UIImage *)imageValue +//{ +// CT_TRY +// NSString *fileValue = [self fileValue]; +// if ([[NSFileManager defaultManager] fileExistsAtPath:fileValue]) { +// return [UIImage imageWithContentsOfFile:fileValue]; +// } +// CT_END_TRY +// return [UIImage imageNamed:_defaultValue]; +//} + +#pragma mark Dictionary handling + +- (id) objectForKey:(NSString *)key +{ + return [self objectForKeyPath:key, nil]; +} + +- (id) objectAtIndex:(NSUInteger)index +{ + return [self objectForKeyPath:@(index), nil]; +} + +- (id) objectForKeyPath:(id)firstComponent, ... +{ + CT_TRY + [self warnIfNotStarted]; + NSMutableArray *components = [_nameComponents mutableCopy]; + va_list args; + va_start(args, firstComponent); + for (id component = firstComponent; + component != nil; component = va_arg(args, id)) { + [components addObject:component]; + } + va_end(args); + return [varCache getMergedValueFromComponentArray:components]; + CT_END_TRY + return nil; +} + +- (id)objectForKeyPathComponents:(NSArray *)pathComponents +{ + CT_TRY + [self warnIfNotStarted]; + NSMutableArray *components = [_nameComponents mutableCopy]; + [components addObjectsFromArray:pathComponents]; + return [varCache getMergedValueFromComponentArray:components]; + CT_END_TRY + return nil; +} + +- (NSUInteger)count +{ + CT_TRY + return [[varCache getMergedValueFromComponentArray:_nameComponents] count]; + CT_END_TRY +} + +#pragma mark Value accessors + +- (NSNumber *)numberValue +{ + [self warnIfNotStarted]; + return _numberValue; +} + +- (NSString *)stringValue +{ + [self warnIfNotStarted]; + return _stringValue; +} + +- (int)intValue { return [[self numberValue] intValue]; } +- (double)doubleValue { return [[self numberValue] doubleValue];} +- (float)floatValue { return [[self numberValue] floatValue]; } +- (CGFloat)cgFloatValue { return [[self numberValue] doubleValue]; } +- (short)shortValue { return [[self numberValue] shortValue];} +- (BOOL)boolValue { return [[self numberValue] boolValue]; } +- (char)charValue { return [[self numberValue] charValue]; } +- (long)longValue { return [[self numberValue] longValue]; } +- (long long)longLongValue { return [[self numberValue] longLongValue]; } +- (NSInteger)integerValue { return [[self numberValue] integerValue]; } +- (unsigned char)unsignedCharValue { return [[self numberValue] unsignedCharValue]; } +- (unsigned short)unsignedShortValue { return [[self numberValue] unsignedShortValue]; } +- (unsigned int)unsignedIntValue { return [[self numberValue] unsignedIntValue]; } +- (NSUInteger)unsignedIntegerValue { return [[self numberValue] unsignedIntegerValue]; } +- (unsigned long)unsignedLongValue { return [[self numberValue] unsignedLongValue]; } +- (unsigned long long)unsignedLongLongValue { return [[self numberValue] unsignedLongLongValue]; } + +// TODO: commented color value +//- (UIColor *)colorValue { return leanplum_intToColor([self longLongValue]); } + +@end diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.h b/CleverTapSDK/ProductExperiences/CTVarCache.h new file mode 100644 index 00000000..165ba101 --- /dev/null +++ b/CleverTapSDK/ProductExperiences/CTVarCache.h @@ -0,0 +1,89 @@ +#import +//#import "LPSecuredVars.h" +#import "CTVar-Internal.h" +#import "CleverTapInstanceConfig.h" +#import "CTDeviceInfo.h" + +NS_ASSUME_NONNULL_BEGIN + +//@class LPVar; + +typedef void (^CacheUpdateBlock)(void); +typedef void (^RegionInitBlock)(NSDictionary *, NSSet *, NSSet *); + +NS_SWIFT_NAME(VarCache) +@interface CTVarCache : NSObject + +- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo; +//NS_UNAVAILABLE; + +//+(instancetype)sharedCache +//NS_SWIFT_NAME(shared()); + +// Location initialization +- (void)registerRegionInitBlock:(RegionInitBlock)block; + +// Handling variables. +- (CTVar *)define:(NSString *)name + with:(nullable NSObject *)defaultValue + kind:(nullable NSString *)kind +NS_SWIFT_NAME(define(name:value:kind:)); + +- (NSArray *)getNameComponents:(NSString *)name; +- (void)loadDiffs; +- (void)saveDiffs; + +- (void)registerVariable:(CTVar *)var; +- (nullable CTVar *)getVariable:(NSString *)name; + +// Handling values. +- (nullable id)getValueFromComponentArray:(NSArray *) components fromDict:(NSDictionary *)values; +- (nullable id)getMergedValueFromComponentArray:(NSArray *) components; +- (nullable NSDictionary *)diffs; +- (BOOL)hasReceivedDiffs; +- (void)applyVariableDiffs:(nullable NSDictionary *)diffs_ + messages:(nullable NSDictionary *)messages_ + variants:(nullable NSArray *)variants_ + localCaps:(nullable NSArray *)localCaps_ + regions:(nullable NSDictionary *)regions_ + variantDebugInfo:(nullable NSDictionary *)variantDebugInfo_ + varsJson:(nullable NSString *)varsJson_ + varsSignature:(nullable NSString *)varsSignature_; +- (void)onUpdate:(CacheUpdateBlock)block; +- (void)setSilent:(BOOL)silent; +- (BOOL)silent; +- (int)contentVersion; +- (nullable NSArray *)variants; +- (nullable NSDictionary *)regions; +- (nullable NSDictionary *)defaultKinds; + +- (nullable NSDictionary *)variantDebugInfo; +- (void)setVariantDebugInfo:(nullable NSDictionary *)variantDebugInfo; + +//- (void)clearUserContent; +// +//- (NSArray *)getLocalCaps; + +// Development mode. +//- (void)setDevModeValuesFromServer:(nullable NSDictionary *)values +// fileAttributes:(nullable NSDictionary *)fileAttributes +// actionDefinitions:(nullable NSDictionary *)actionDefinitions; +//- (BOOL)sendVariablesIfChanged; +//- (BOOL)sendActionsIfChanged; + +// Handling files. +//- (void)registerFile:(NSString *)stringValue withDefaultValue:(NSString *)defaultValue; +//- (void)maybeUploadNewFiles; +//- (nullable NSDictionary *)fileAttributes; +// +//- (nullable NSMutableDictionary *)userAttributes; +//- (void)saveUserAttributes; + +//- (LPSecuredVars *)securedVars; + +@property (strong, nonatomic) NSMutableDictionary *vars; +@property (assign, nonatomic) BOOL appLaunchedRecorded; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m new file mode 100644 index 00000000..0af01e88 --- /dev/null +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -0,0 +1,808 @@ +//#import "LPConstants.h" +//#import "LPFileManager.h" +#import "CTVarCache.h" +//#import "LPActionTriggerManager.h" +//#import "FileMD5Hash.h" +//#import "LPKeychainWrapper.h" +//#import "LPAES.h" +#import "CTUtils.h" +#import "CTConstants.h" +#import "CTPreferences.h" +#import "ContentMerger.h" +//#import "LPRequestFactory.h" +//#import "LPRequestSender.h" +//#import "LPCountAggregator.h" +//#import "LPFileTransferManager.h" +//#import + +@interface CTVarCache() +@property (strong, nonatomic) NSRegularExpression *varNameRegex; +@property (strong, nonatomic) NSMutableDictionary *filesToInspect; +@property (strong, nonatomic) NSMutableDictionary *fileAttributes; +@property (strong, nonatomic) NSMutableDictionary *valuesFromClient; +@property (readwrite, nonatomic) NSMutableDictionary *defaultKinds; +@property (strong, nonatomic) NSDictionary *diffs; +@property (strong, nonatomic) NSDictionary *devModeValuesFromServer; +@property (strong, nonatomic) NSDictionary *devModeFileAttributesFromServer; +@property (strong, nonatomic) NSArray *variants; +@property (strong, nonatomic) NSArray *localCaps; +@property (strong, nonatomic) NSDictionary *variantDebugInfo; +@property (strong, nonatomic) NSMutableDictionary *userAttributes; +@property (strong, nonatomic) NSDictionary *regions; +@property (strong, nonatomic) CacheUpdateBlock updateBlock; +@property (assign, nonatomic) BOOL hasReceivedDiffs; +@property (strong, nonatomic) id merged; +@property (assign, nonatomic) BOOL silent; +@property (assign, nonatomic) int contentVersion; +@property (assign, nonatomic) BOOL hasTooManyFiles; +@property (strong, nonatomic) RegionInitBlock regionInitBlock; +//@property (strong, nonatomic) LPCountAggregator *countAggregator; +@property (strong, nonatomic) NSString *varsJson; +@property (strong, nonatomic) NSString *varsSignature; + +@property (nonatomic, strong) CleverTapInstanceConfig *config; +@property (nonatomic, strong) CTDeviceInfo *deviceInfo; +@end + +//static CTVarCache *sharedInstance = nil; +//static dispatch_once_t leanplum_onceToken; + +@implementation CTVarCache + +//+(instancetype)sharedCache +//{ +// dispatch_once(&leanplum_onceToken, ^{ +// sharedInstance = [[self alloc] init]; +// }); +// return sharedInstance; +//} + +- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo { + if ((self = [super init])) { + self.config = config; + self.deviceInfo = deviceInfo; + [self initialize]; + } + return self; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + [self initialize]; +// _countAggregator = [LPCountAggregator sharedAggregator]; + } + return self; +} + +- (void)initialize +{ + self.vars = [NSMutableDictionary dictionary]; + self.filesToInspect = [NSMutableDictionary dictionary]; + self.fileAttributes = [NSMutableDictionary dictionary]; + self.valuesFromClient = [NSMutableDictionary dictionary]; + self.diffs = [NSMutableDictionary dictionary]; + self.defaultKinds = [NSMutableDictionary dictionary]; + self.localCaps = [NSArray array]; + self.hasReceivedDiffs = NO; + self.silent = NO; + NSError *error = NULL; + self.varNameRegex = [NSRegularExpression regularExpressionWithPattern:@"(?:[^\\.\\[.(\\\\]+|\\\\.)+" + options:NSRegularExpressionCaseInsensitive error:&error]; +} + +- (void)registerRegionInitBlock:(void (^)(NSDictionary *, NSSet *, NSSet *))block +{ + self.regionInitBlock = block; +} + +- (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString *)kind +{ + if ([CTUtils isNullOrEmpty:name]) { + CleverTapLogDebug(_config.logLevel, @"%@: Empty name provided as parameter while defining a variable.", self); + return nil; + } + + @synchronized (self.vars) { + CT_TRY + CTVar *existing = [self getVariable:name]; + if (existing) { + return existing; + } + CT_END_TRY + CTVar *var = [[CTVar alloc] initWithName:name + withComponents:[self getNameComponents:name] + withDefaultValue:defaultValue + withKind:kind + varCache:self]; + return var; + } +} + +- (NSArray *) arrayOfCaptureComponentsOfString:(NSString *)data matchedBy:(NSRegularExpression *)regExpression +{ + NSMutableArray *test = [NSMutableArray array]; + + NSArray *matches = [regExpression matchesInString:data options:0 range:NSMakeRange(0, data.length)]; + + for(NSTextCheckingResult *match in matches) { + NSMutableArray *result = [NSMutableArray arrayWithCapacity:match.numberOfRanges]; + for(NSInteger i=0; i index) { + result = arrayCollection[index]; + if (autoInsert && !result && [key isKindOfClass:NSString.class]) { + result = [NSMutableArray array]; + [collection setObject:result atIndex:index]; + } + } + } + + if ([result isKindOfClass:[NSNull class]]) { + return nil; + } + + return result; +} + +//- (void)registerFile:(NSString *)stringValue withDefaultValue:(NSString *)defaultValue +//{ +// if (stringValue.length == 0) { +// return; +// } +// NSString *path = [LPFileManager fileValue:stringValue withDefaultValue:defaultValue]; +// if (!path) { +// return; +// } +// self.filesToInspect[stringValue] = path; +//} + +//- (void)computeFileInfo +//{ +// for (NSString *fileName in self.filesToInspect) { +// NSString *path = self.filesToInspect[fileName]; +// NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; +// NSString *hash = (__bridge_transfer NSString *) Leanplum_FileMD5HashCreateWithPath( +// (__bridge CFStringRef) path, FileHashDefaultChunkSizeForReadingData); +// if (hash) { +// attributes[LP_KEY_HASH] = hash; +// } +// NSNumber *size = [[[NSFileManager defaultManager] +// attributesOfItemAtPath:path error:nil] objectForKey:NSFileSize]; +// attributes[LP_KEY_SIZE] = size; +// _fileAttributes[fileName] = @{@"": attributes}; +// } +// [self.filesToInspect removeAllObjects]; +//} + +// Updates a JSON structure of variable values, and a dictionary of variable kinds. +- (void)updateValues:(NSString *)name + nameComponents:(NSArray *)nameComponents + value:(id)value + kind:(NSString *)kind + values:(NSMutableDictionary *)values + kinds:(NSMutableDictionary *)kinds +{ + if (value) { + id valuesPtr = values; + for (int i = 0; i < nameComponents.count - 1; i++) { + valuesPtr = [self traverse:valuesPtr withKey:nameComponents[i] autoInsert:YES]; + } + + // Make the value mutable. That way, if we add a dictionary variable, + // we can still add subvariables. + if ([value isKindOfClass:NSDictionary.class] && + [value class] != [NSMutableDictionary class]) { + value = [NSMutableDictionary dictionaryWithDictionary:value]; + } + if ([value isKindOfClass:NSArray.class] && + [value class] != [NSMutableArray class]) { + value = [NSMutableArray arrayWithArray:value]; + } + [valuesPtr setObject:value forKey:nameComponents.lastObject]; + } + if (kind) { + kinds[name] = kind; + } +} + +- (void)registerVariable:(CTVar *)var +{ + [self.vars setObject:var forKey:var.name]; + [self updateValues:var.name + nameComponents:var.nameComponents + value:var.defaultValue + kind:var.kind + values:self.valuesFromClient + kinds:_defaultKinds]; +} + +- (CTVar *)getVariable:(NSString *)name +{ + return [self.vars objectForKey:name]; +} + +- (id)getValueFromComponentArray:(NSArray *) components fromDict:(NSDictionary *)values +{ + id mergedPtr = values; + for (id component in components) { + mergedPtr = [self traverse:mergedPtr withKey:component autoInsert:NO]; + } + return mergedPtr; +} + +- (id)getMergedValueFromComponentArray:(NSArray *)components +{ + return [self getValueFromComponentArray:components fromDict:self.merged ? self.merged : self.valuesFromClient]; +} + +- (void)loadDiffs +{ + @try { + NSString *fileName = [self dataArchiveFileName]; + NSString *filePath = [CTPreferences filePathfromFileName:fileName]; + NSData *encryptedDiffs = [NSData dataWithContentsOfFile:filePath];; + NSDictionary *diffs; + NSDictionary *messages; + NSArray *variants; + NSArray *localCaps; + NSDictionary *variantDebugInfo; + NSDictionary *regions; + NSString *varsJson; + NSString *varsSignature; + if (encryptedDiffs) { + NSData *diffsData = encryptedDiffs; + if (!diffsData) { + return; + } + + NSKeyedUnarchiver *unarchiver; + if (@available(iOS 12.0, *)) { + NSError *error = nil; + unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:diffsData error:&error]; + if (error != nil) { + CleverTapLogDebug(self.config.logLevel, @"%@: Error while loading variables: %@", self, error.localizedDescription); + return; + } + unarchiver.requiresSecureCoding = NO; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:diffsData]; +#pragma clang diagnostic pop + } + diffs = (NSDictionary *) [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; +// messages = (NSDictionary *) [unarchiver decodeObjectForKey:LEANPLUM_DEFAULTS_MESSAGES_KEY]; +// regions = (NSDictionary *)[unarchiver decodeObjectForKey:LP_KEY_REGIONS]; +// variants = (NSArray *)[unarchiver decodeObjectForKey:LP_KEY_VARIANTS]; +// variantDebugInfo = (NSDictionary *)[unarchiver decodeObjectForKey:LP_KEY_VARIANT_DEBUG_INFO]; + varsJson = [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARS_JSON_KEY]; +// varsSignature = [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARS_SIGNATURE_KEY]; +// NSString *deviceId = [unarchiver decodeObjectForKey:LP_PARAM_DEVICE_ID]; +// NSString *userId = [unarchiver decodeObjectForKey:LP_PARAM_USER_ID]; +// BOOL loggingEnabled = [unarchiver decodeBoolForKey:LP_KEY_LOGGING_ENABLED]; +// localCaps = [unarchiver decodeObjectForKey:LEANPLUM_DEFAULTS_LOCAL_CAPS_KEY]; +// if (deviceId) { +// [[Leanplum user] setDeviceId:deviceId]; +// } +// if (userId) { +// [[Leanplum user] setUserId:userId]; +// } +// if (loggingEnabled) { +// [LPConstantsState sharedState].loggingEnabled = YES; +// } + } + + [self applyVariableDiffs:diffs + messages:messages + variants:variants + localCaps:localCaps + regions:regions + variantDebugInfo:variantDebugInfo + varsJson:varsJson + varsSignature:varsSignature]; + } @catch (NSException *exception) { + CleverTapLogDebug(self.config.logLevel, @"%@: Error while loading variables: %@", self, exception.debugDescription); + } +// [self userAttributes]; + +// [self.countAggregator incrementCount:@"load_diffs"]; +} + +- (void)saveDiffs +{ + // Stores the variables on the device in case we don't have a connection. + // Restores next time when the app is opened. + // Diffs need to be locked incase other thread changes the diffs using + // mergeHelper:. + @synchronized (self.diffs) { + NSMutableData *diffsData = [[NSMutableData alloc] init]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:diffsData]; +#pragma clang diagnostic pop + [archiver encodeObject:self.diffs forKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; +// NSDictionary *messages = [[LPActionManager shared] messages]; +// [archiver encodeObject:messages forKey:LEANPLUM_DEFAULTS_MESSAGES_KEY]; +// [archiver encodeObject:self.variants forKey:LP_KEY_VARIANTS]; +// [archiver encodeObject:self.variantDebugInfo forKey:LP_KEY_VARIANT_DEBUG_INFO]; +// [archiver encodeObject:self.regions forKey:LP_KEY_REGIONS]; +// [archiver encodeObject:[LPConstantsState sharedState].sdkVersion forKey:LP_PARAM_SDK_VERSION]; +// [archiver encodeObject:[[Leanplum user] deviceId] forKey:LP_PARAM_DEVICE_ID]; +// [archiver encodeObject:[[Leanplum user] userId] forKey:LP_PARAM_USER_ID]; +// [archiver encodeBool:[LPConstantsState sharedState].loggingEnabled forKey:LP_KEY_LOGGING_ENABLED]; + [archiver encodeObject:self.varsJson forKey:CLEVERTAP_DEFAULTS_VARS_JSON_KEY]; +// [archiver encodeObject:self.varsSignature forKey:LEANPLUM_DEFAULTS_VARS_SIGNATURE_KEY]; +// [archiver encodeObject:self.localCaps forKey:LEANPLUM_DEFAULTS_LOCAL_CAPS_KEY]; + [archiver finishEncoding]; + +// NSData *encryptedDiffs = [LPAES encryptedDataFromData:diffsData]; + +// NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; +// +// [defaults setObject:encryptedDiffs forKey:LEANPLUM_DEFAULTS_VARIABLES_KEY]; +// +// [defaults setObject:LEANPLUM_SDK_VERSION forKey:LEANPLUM_DEFAULTS_SDK_VERSION]; +// +// [Leanplum synchronizeDefaults]; + +// [CTPreferences putObject:diffsData forKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; + NSError *writeError = nil; + NSString *fileName = [self dataArchiveFileName]; + NSString *filePath = [CTPreferences filePathfromFileName:fileName]; + [diffsData writeToFile:filePath options:NSDataWritingAtomic error:&writeError]; + if (writeError) { + CleverTapLogStaticInternal(@"%@ failed to write data at %@: %@", self, filePath, writeError); + } + } +// [self.countAggregator incrementCount:@"save_diffs"]; +} + +- (NSString*)dataArchiveFileName { + return [NSString stringWithFormat:@"clevertap-%@-%@-pe-vars.plist", _config.accountId, _deviceInfo.deviceId]; +} + +// TODO: REMOVE IRRELEVANT PARAMETERS +- (void)applyVariableDiffs:(NSDictionary *)diffs_ + messages:(NSDictionary *)messages_ + variants:(NSArray *)variants_ + localCaps:(NSArray *)localCaps_ + regions:(NSDictionary *)regions_ + variantDebugInfo:(NSDictionary *)variantDebugInfo_ + varsJson:(NSString *)varsJson_ + varsSignature:(NSString *)varsSignature_ +{ + @synchronized (self.vars) { + if (diffs_ || (!self.silent && !self.hasReceivedDiffs)) { + // Prevent overriding variables if API returns null + // If no variables are defined, API returns {} + if (diffs_ != nil && ![diffs_ isEqual:[NSNull null]]) { + self.diffs = diffs_; + + // Merger helper will mutate diffs. + // We need to lock it in case multiple threads will be accessing this. + @synchronized (self.diffs) { + self.diffs = [self convert:self.diffs]; + self.merged = [ContentMerger mergeWithVars:self.valuesFromClient diff:self.diffs]; + } + + // Update variables with new values. + // Have to extract the keys because a dictionary variable may add a new sub-variable, + // modifying the variable dictionary. + for (NSString *name in [self.vars allKeys]) { + [self.vars[name] update]; + } + } else { + CleverTapLogDebug(self.config.logLevel, @"%@: No variables received from the server", self); + } + } + +// if (regions_) { +// // Store regions. +// self.regions = regions_; +// } + +// if (messages_) { +// if (!self.silent) { +// // Save unmodified message data from API +// [[LPActionManager shared] setMessagesDataFromServer:messages_]; +// } +// // Process messages data +// [[LPActionManager shared] processMessagesAndDownloadFiles: messages_]; +// } + + // If LeanplumLocation is linked in, setup region monitoring. +// if (messages_ || regions_) { +// if (!self.regionInitBlock) { +// if ([LPConstantsState sharedState].isDevelopmentModeEnabled) { +// if ([regions_ count] > 0) { +// LPLog(LPInfo, @"Regions have been defined in dashboard, but the app is not built to handle them."); +// LPLog(LPInfo, @"Add LeanplumLocation.framework or LeanplumBeacon.framework to Build Settings -> Link Binary With Libraries."); +// LPLog(LPInfo, @"Disregard warning if there are no plans to utilize iBeacon or Geofencing within the app"); +// } +// } +// } else { +// NSSet *foregroundRegionNames; +// NSSet *backgroundRegionNames; +// [LPActionTriggerManager getForegroundRegionNames:&foregroundRegionNames +// andBackgroundRegionNames:&backgroundRegionNames]; +// self.regionInitBlock(self.regions, foregroundRegionNames, backgroundRegionNames); +// } +// } + +// if (variants_) { +// self.variants = variants_; +// } +// +// if (localCaps_) { +// self.localCaps = localCaps_; +// } +// +// if (variantDebugInfo_) { +// self.variantDebugInfo = variantDebugInfo_; +// } + + self.contentVersion++; + + if (varsJson_) { + self.varsJson = varsJson_; + self.varsSignature = varsSignature_; + } + + // DONT SAVE VARS TO CACHE IF SILENT + if (!self.silent) { + [self saveDiffs]; + + self.hasReceivedDiffs = YES; + if (self.updateBlock) { + self.updateBlock(); + } + } + } +// [self.countAggregator incrementCount:@"apply_variable_diffs"]; +} + +- (BOOL)areActionDefinitionsEqual:(NSDictionary *)a other:(NSDictionary *)b +{ + if (a.count != b.count) { + return NO; + } + for (NSString *key in a) { + NSDictionary *aItem = a[key]; + NSDictionary *bItem = b[key]; + if (!bItem) { + return NO; + } + if ((aItem[@"kind"] != bItem[@"kind"]) || + (aItem[@"values"] != bItem[@"values"]) || + (aItem[@"kinds"] != bItem[@"kinds"]) || + aItem[@"options"] != bItem[@"options"]) { + return NO; + } + } + return YES; +} + +// TODO: commented sendVariablesIfChanged, sendActionsIfChanged, sendContentIfChanged and maybeUploadNewFiles. ask if these are needed + +//- (BOOL)sendVariablesIfChanged +//{ +// return [self sendContentIfChanged:YES actions:NO]; +//} +// +//- (BOOL)sendActionsIfChanged +//{ +// return [self sendContentIfChanged:NO actions:YES]; +//} +// +//- (BOOL)sendContentIfChanged:(BOOL)variables actions:(BOOL)actions +//{ +// [self computeFileInfo]; +// +// BOOL changed = NO; +// if (variables && self.devModeValuesFromServer && +// (![self.valuesFromClient isEqualToDictionary:self.devModeValuesFromServer])) { +// changed = YES; +// } +// +// NSArray *definitions = [[LPActionManager shared] definitions]; +// NSMutableDictionary *actionDefinitions = [NSMutableDictionary dictionary]; +// for (ActionDefinition *def in definitions) { +// actionDefinitions[def.name] = def.json; +// } +// if (actions && ![self areActionDefinitionsEqual:actionDefinitions +// other:[[LPActionManager shared] actionDefinitionsFromServer]]) { +// changed = YES; +// } +// +// if (changed) { +// NSDictionary *limitedFileAttributes = self.fileAttributes; +// NSDictionary *limitedValues = self.valuesFromClient; +// if ([self.fileAttributes count] > MAX_FILES_SUPPORTED) { +// limitedValues = [self.valuesFromClient mutableCopy]; +// [(NSMutableDictionary *)limitedValues removeObjectForKey:LP_VALUE_RESOURCES_VARIABLE]; +// LPLog(LPError, @"You are trying to sync %lu files, which is more than " +// @"we support (%d). If you are calling [Leanplum syncResources], try adding " +// @"regex filters to limit the number of files you are syncing.", +// (unsigned long) self.fileAttributes.count, MAX_FILES_SUPPORTED); +// limitedFileAttributes = [NSDictionary dictionary]; +// self.hasTooManyFiles = YES; +// } +// @try { +// NSMutableDictionary *args = [NSMutableDictionary dictionary]; +// if (variables) { +// args[LP_PARAM_VARS] = [LPJSON stringFromJSON:limitedValues]; +// args[LP_PARAM_KINDS] = [LPJSON stringFromJSON:self.defaultKinds]; +// } +// if (actions) { +// NSMutableDictionary *jsonDefinitions = [NSMutableDictionary new]; +// [definitions enumerateObjectsUsingBlock:^(ActionDefinition *ac, NSUInteger idx, BOOL *stop) { +// jsonDefinitions[ac.name] = [ac json]; +// }]; +// +// args[LP_PARAM_ACTION_DEFINITIONS] = [LPJSON stringFromJSON:jsonDefinitions]; +// } +// args[LP_PARAM_FILE_ATTRIBUTES] = [LPJSON stringFromJSON:limitedFileAttributes]; +// LPRequest *request = [LPRequestFactory setVarsWithParams:args]; +// [[LPRequestSender sharedInstance] send:request]; +// return YES; +// } @catch (NSException *e) { +// [Leanplum throwError:@"Cannot serialize variable values. " +// @"Make sure your variables are JSON serializable."]; +// } +// } +// return NO; +//} +// +//- (void)maybeUploadNewFiles +//{ +// if (self.hasTooManyFiles || +// !self.devModeFileAttributesFromServer || +// ![Leanplum hasStartedAndRegisteredAsDeveloper]) { +// return; +// } +// +// NSMutableArray *filenames = [NSMutableArray array]; +// NSMutableArray *fileData = [NSMutableArray array]; +// int totalSize = 0; +// for (NSString *name in self.fileAttributes) { +// NSDictionary *variationAttributes = self.fileAttributes[name]; +// NSDictionary *localAttributes = variationAttributes[@""]; +// NSDictionary *serverAttributes = self.devModeFileAttributesFromServer[name][@""]; +// if ([LPFileManager isNewerLocally:localAttributes orRemotely:serverAttributes]) { +// NSString *hash = [localAttributes valueForKey:LP_KEY_HASH]; +// if (!hash) { +// hash = @""; +// } +// NSString *variationPath = [LPFileManager fileRelativeToAppBundle:name]; +// if ((totalSize > MAX_UPLOAD_BATCH_SIZES && +// filenames.count > 0) || filenames.count >= MAX_UPLOAD_BATCH_FILES) { +// LPRequest *request = [LPRequestFactory uploadFileWithParams:@{LP_PARAM_DATA: [LPJSON stringFromJSON:fileData]}]; +// [[LPRequestSender sharedInstance] send:request]; +// filenames = [NSMutableArray array]; +// fileData = [NSMutableArray array]; +// totalSize = 0; +// } +// NSNumber *size = [localAttributes valueForKey:LP_KEY_SIZE]; +// totalSize += [size intValue]; +// [fileData addObject:@{ +// LP_KEY_HASH: hash, +// LP_KEY_SIZE: size, +// LP_KEY_FILENAME: name +// }]; +// [filenames addObject:variationPath]; +// } +// } +// if (filenames.count > 0) { +// [[LPFileTransferManager sharedInstance] sendFilesNow:filenames fileData:fileData]; +// } +//} + +//- (void)setDevModeValuesFromServer:(NSDictionary *)values +// fileAttributes:(NSDictionary *)fileAttributes +// actionDefinitions:(NSDictionary *)actionDefinitions +//{ +// self.devModeValuesFromServer = values; +// self.devModeFileAttributesFromServer = fileAttributes; +// [[LPActionManager shared] setActionDefinitionsFromServer: actionDefinitions]; +//} +// +- (void)onUpdate:(CacheUpdateBlock) block +{ + self.updateBlock = block; + +// [self.countAggregator incrementCount:@"on_update_varcache"]; +} +// +//- (NSMutableDictionary *)userAttributes +//{ +// if (!_userAttributes) { +// @try { +// NSString *token = [[ApiConfig shared] token]; +// if (token) { +// NSData *encryptedValue = [[NSUserDefaults standardUserDefaults] dataForKey:LEANPLUM_DEFAULTS_ATTRIBUTES_KEY]; +// if (encryptedValue) { +// NSData *decryptedData = [LPAES decryptedDataFromData:encryptedValue]; +// if (decryptedData) { +// NSKeyedUnarchiver *unarchiver; +// if (@available(iOS 12.0, *)) { +// NSError *error = nil; +// unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:decryptedData error:&error]; +// if (error != nil) { +// LPLog(LPError, error.localizedDescription); +// //in case of error returning empty dictionary to avoid crash +// return [NSMutableDictionary dictionary]; +// } +// unarchiver.requiresSecureCoding = NO; +// } else { +//#pragma clang diagnostic push +//#pragma clang diagnostic ignored "-Wdeprecated-declarations" +// unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData +// :decryptedData]; +//#pragma clang diagnostic pop +// } +// self.userAttributes = [(NSDictionary *)[unarchiver decodeObjectForKey:LP_PARAM_USER_ATTRIBUTES] mutableCopy]; +// } +// } +// } +// } @catch (NSException *exception) { +// LPLog(LPError, @"Could not load user attributes: %@", exception); +// } +// } +// if (!_userAttributes) { +// _userAttributes = [NSMutableDictionary dictionary]; +// } +// return _userAttributes; +//} +// +//- (void)saveUserAttributes +//{ +// RETURN_IF_NOOP; +// NSMutableData *data = [[NSMutableData alloc] init]; +//#pragma clang diagnostic push +//#pragma clang diagnostic ignored "-Wdeprecated-declarations" +// NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; +//#pragma clang diagnostic pop +// [archiver encodeObject:self.userAttributes forKey:LP_PARAM_USER_ATTRIBUTES]; +// [archiver finishEncoding]; +// +// NSData *encryptedData = [LPAES encryptedDataFromData:data]; +// +// NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; +// +// [defaults setObject:encryptedData forKey:LEANPLUM_DEFAULTS_ATTRIBUTES_KEY]; +// [Leanplum synchronizeDefaults]; +// +// [self.countAggregator incrementCount:@"save_user_attributes"]; +//} +// +//- (LPSecuredVars *)securedVars +//{ +// if ([LPUtils isNullOrEmpty:self.varsJson] || [LPUtils isNullOrEmpty:self.varsSignature]) { +// return nil; +// } +// return [[LPSecuredVars alloc] initWithJson:self.varsJson andSignature:self.varsSignature]; +//} +// +//- (NSArray *)getLocalCaps +//{ +// return self.localCaps; +//} +// +//- (void)clearUserContent +//{ +// self.diffs = nil; +// self.variants = nil; +// self.localCaps = nil; +// self.variantDebugInfo = nil; +// self.vars = nil; +// self.userAttributes = nil; +// self.merged = nil; +// self.varsJson = nil; +// self.varsSignature = nil; +// +// self.devModeValuesFromServer = nil; +// self.devModeFileAttributesFromServer = nil; +// +// [LPActionManager shared].messages = [NSMutableDictionary dictionary]; +// [LPActionManager shared].messagesDataFromServer = [NSMutableDictionary dictionary]; +// [LPActionManager shared].actionDefinitionsFromServer = [NSMutableDictionary dictionary]; +//} +// +// Resets the VarCache to stock state. Used for testing purposes. +//- (void)reset +//{ +// [self clearUserContent]; +// +// self.filesToInspect = nil; +// self.fileAttributes = nil; +// +// self.valuesFromClient = nil; +// self.defaultKinds = nil; +// +// self.updateBlock = nil; +// self.hasReceivedDiffs = NO; +// self.silent = NO; +// self.contentVersion = 0; +// self.hasTooManyFiles = NO; +//} + +- (NSDictionary*)convert:(NSDictionary*)result { + NSMutableDictionary *varsPayload = [NSMutableDictionary dictionary]; + + [result enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { + + if ([key containsString:@"."]) { + NSArray *components = [self getNameComponents:key]; + long namePosition = components.count - 1; + NSMutableDictionary *currentMap = varsPayload; + + for (int i = 0; i < components.count; i++) { + NSString *component = components[i]; + if (i == namePosition) { + currentMap[component] = value; + } + else { + if (!currentMap[component]) { + NSMutableDictionary *nestedMap = [NSMutableDictionary dictionary]; + currentMap[component] = nestedMap; + currentMap = nestedMap; + } + else { + currentMap = ((NSMutableDictionary*)currentMap[component]); + } + } + } + } + else { + varsPayload[key] = value; + } + }]; + + return varsPayload; +} + +@end diff --git a/CleverTapSDK/ProductExperiences/ContentMerger.h b/CleverTapSDK/ProductExperiences/ContentMerger.h new file mode 100644 index 00000000..92077a76 --- /dev/null +++ b/CleverTapSDK/ProductExperiences/ContentMerger.h @@ -0,0 +1,13 @@ +// +// ContentMerger.h +// CleverTapSDK +// +// Created by Akash Malhotra on 17/02/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import + +@interface ContentMerger : NSObject ++ (id)mergeWithVars:(id)vars diff:(id)diff; +@end diff --git a/CleverTapSDK/ProductExperiences/ContentMerger.m b/CleverTapSDK/ProductExperiences/ContentMerger.m new file mode 100644 index 00000000..49b8fe62 --- /dev/null +++ b/CleverTapSDK/ProductExperiences/ContentMerger.m @@ -0,0 +1,48 @@ +// +// ContentMerger.m +// CleverTapSDK +// +// Created by Akash Malhotra on 17/02/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import "ContentMerger.h" + +@implementation ContentMerger + ++ (id)mergeWithVars:(id)vars diff:(id)diff { + + // Return the modified value if it is a `primitive` + if ([diff isKindOfClass:[NSString class]]) { + return ((NSString*)diff); + } + if ([diff isKindOfClass:[NSNumber class]]) { + return ((NSNumber*)diff); + } + if ([diff isKindOfClass:[NSNull class]]) { + return [NSNull null]; + } + + if ([vars isKindOfClass:[NSNumber class]] || [vars isKindOfClass:[NSString class]]) { + return diff; + } + + //TODO: add merging for array types from LP ContentMerger + + NSMutableDictionary *merged = [NSMutableDictionary dictionary]; + if ([vars isKindOfClass:[NSDictionary class]]) { + merged = vars; + } + + if ([diff isKindOfClass:[NSDictionary class]]) { + NSDictionary *diffDict = (NSDictionary*)diff; + [diffDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { + id defaultValue = merged[key] ?: [NSNull null]; + merged[key] = [self mergeWithVars:defaultValue diff:value]; + }]; + return merged; + } + return [NSNull null]; +} + +@end diff --git a/CleverTapSDK/ios.modulemap b/CleverTapSDK/ios.modulemap index 6c796985..7d816fc7 100644 --- a/CleverTapSDK/ios.modulemap +++ b/CleverTapSDK/ios.modulemap @@ -17,5 +17,8 @@ framework module CleverTapSDK { header "CleverTapJSInterface.h" header "CleverTap+InAppNotifications.h" header "CleverTap+SCDomain.h" + header "CTLocalInApp.h" + header "CleverTap+CTVar.h" + header "CTVar.h" export * } diff --git a/CleverTapSDK/tvos.modulemap b/CleverTapSDK/tvos.modulemap index 2f715cfb..bff7197b 100644 --- a/CleverTapSDK/tvos.modulemap +++ b/CleverTapSDK/tvos.modulemap @@ -9,5 +9,7 @@ framework module CleverTapSDK { header "CleverTapUTMDetail.h" header "CleverTapTrackedViewController.h" header "CleverTapInstanceConfig.h" + header "CleverTap+CTVar.h" + header "CTVar.h" export * } diff --git a/CleverTapWatchOS/CleverTapWatchOS.h b/CleverTapWatchOS/CleverTapWatchOS.h index 0c49405d..e6a6d277 100644 --- a/CleverTapWatchOS/CleverTapWatchOS.h +++ b/CleverTapWatchOS/CleverTapWatchOS.h @@ -5,8 +5,8 @@ @interface CleverTapWatchOS : NSObject -- (instancetype)initWithSession:(WCSession* _Nonnull)session; +- (instancetype _Nonnull)initWithSession:(WCSession* _Nonnull)session; -- (void)recordEvent:(NSString *)event withProps:(NSDictionary *)props; +- (void)recordEvent:(NSString *_Nonnull)event withProps:(NSDictionary *_Nonnull)props; @end diff --git a/CleverTapWatchOS/CleverTapWatchOS.m b/CleverTapWatchOS/CleverTapWatchOS.m index 1a5c8178..e122724d 100644 --- a/CleverTapWatchOS/CleverTapWatchOS.m +++ b/CleverTapWatchOS/CleverTapWatchOS.m @@ -9,7 +9,7 @@ @interface CleverTapWatchOS () @implementation CleverTapWatchOS -- (instancetype)initWithSession:(WCSession* _Nonnull)session { +- (instancetype _Nonnull)initWithSession:(WCSession* _Nonnull)session { if (self = [super init]) { self.session = session; } @@ -20,13 +20,12 @@ - (void)sendMessage:(NSString *)type withcontent:(NSDictionary *)content{ if (![self.session isReachable]) { return; } - NSMutableDictionary *message = [[NSMutableDictionary alloc] init]; - message = [content mutableCopy]; + NSMutableDictionary *message = [content mutableCopy]; message[@"clevertap_type"] = type; [self.session sendMessage:message replyHandler:nil errorHandler:nil]; } -- (void)recordEvent:(NSString *)event withProps:(NSDictionary *)props { +- (void)recordEvent:(NSString *_Nonnull)event withProps:(NSDictionary *_Nonnull)props { NSMutableDictionary *content = [[NSMutableDictionary alloc] init]; content[@"event"] = event; content[@"props"] = props; diff --git a/Package.swift b/Package.swift index 1749c53f..851ac0d7 100644 --- a/Package.swift +++ b/Package.swift @@ -47,7 +47,8 @@ let package = Package( .headerSearchPath("Inbox/config"), .headerSearchPath("Inbox/controllers"), .headerSearchPath("Inbox/models"), - .headerSearchPath("Inbox/views") + .headerSearchPath("Inbox/views"), + .headerSearchPath("ProductExperiences/") ], linkerSettings: [ .linkedFramework("AVFoundation"), From 4865c8f6c05ceb4727c72bcc44a6c9ca40d530d6 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Fri, 24 Feb 2023 13:21:26 +0530 Subject: [PATCH 03/86] Passed along number as a type for defineVars API --- CleverTapSDK/CleverTap.m | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 1b744025..73baaa97 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -5109,13 +5109,13 @@ - (void)syncVariables { }]; } #else - CleverTapLogDebug(_config.logLevel, @"%@: syncChanges can only be called from Debug configurations/builds", self); + CleverTapLogDebug(_config.logLevel, @"%@: syncVariables can only be called from Debug configurations/builds", self); #endif } - (void)syncVariables:(BOOL)isProduction { if (isProduction) { - CleverTapLogDebug(_config.logLevel, @"%@: syncChanges can only be called from Debug configurations/builds", self); + CleverTapLogDebug(_config.logLevel, @"%@: syncVariables can only be called from Debug configurations/builds", self); } else { [self syncVariables]; } @@ -5168,7 +5168,12 @@ - (NSDictionary*)varsPayload { [allVars addEntriesFromDictionary:flattenedMap]; } else { - varData[@"type"] = varValue.kind; + if ([varValue.kind isEqualToString:CT_KIND_INT] || [varValue.kind isEqualToString:CT_KIND_FLOAT]) { + varData[@"type"] = @"number"; + } + else { + varData[@"type"] = varValue.kind; + } varData[@"defaultValue"] = varValue.defaultValue; allVars[key] = varData; } From f559f496e3bd52e2c96b351a0f2d9d1b4550a417 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Fri, 24 Feb 2023 13:28:17 +0530 Subject: [PATCH 04/86] project config --- CleverTapSDK.xcodeproj/project.pbxproj | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/CleverTapSDK.xcodeproj/project.pbxproj b/CleverTapSDK.xcodeproj/project.pbxproj index c54405b9..87142d3d 100644 --- a/CleverTapSDK.xcodeproj/project.pbxproj +++ b/CleverTapSDK.xcodeproj/project.pbxproj @@ -222,10 +222,6 @@ 4E7929FA29799E8F00B81F3C /* CTDomainFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E7929F729799E8F00B81F3C /* CTDomainFactory.h */; }; 4E7929FB29799E8F00B81F3C /* CTDomainFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E7929F829799E8F00B81F3C /* CTDomainFactory.m */; }; 4E7929FC29799E8F00B81F3C /* CTDomainFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E7929F829799E8F00B81F3C /* CTDomainFactory.m */; }; - 4E7929FF2979CAE100B81F3C /* CTSSLPinningFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E7929FD2979CAE100B81F3C /* CTSSLPinningFactory.h */; }; - 4E792A002979CAE100B81F3C /* CTSSLPinningFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E7929FD2979CAE100B81F3C /* CTSSLPinningFactory.h */; }; - 4E792A012979CAE100B81F3C /* CTSSLPinningFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E7929FE2979CAE100B81F3C /* CTSSLPinningFactory.m */; }; - 4E792A022979CAE100B81F3C /* CTSSLPinningFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E7929FE2979CAE100B81F3C /* CTSSLPinningFactory.m */; }; 4E838C40299F419900ED0875 /* ContentMerger.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E838C3E299F419800ED0875 /* ContentMerger.h */; settings = {ATTRIBUTES = (Private, ); }; }; 4E838C41299F419900ED0875 /* ContentMerger.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E838C3E299F419800ED0875 /* ContentMerger.h */; settings = {ATTRIBUTES = (Private, ); }; }; 4E838C42299F419900ED0875 /* ContentMerger.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E838C3F299F419900ED0875 /* ContentMerger.m */; }; @@ -625,8 +621,6 @@ 4E7704B72679DCEF005222D0 /* CleverTapURLDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CleverTapURLDelegate.h; sourceTree = ""; }; 4E7929F729799E8F00B81F3C /* CTDomainFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTDomainFactory.h; sourceTree = ""; }; 4E7929F829799E8F00B81F3C /* CTDomainFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTDomainFactory.m; sourceTree = ""; }; - 4E7929FD2979CAE100B81F3C /* CTSSLPinningFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTSSLPinningFactory.h; sourceTree = ""; }; - 4E7929FE2979CAE100B81F3C /* CTSSLPinningFactory.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTSSLPinningFactory.m; sourceTree = ""; }; 4E838C3E299F419800ED0875 /* ContentMerger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContentMerger.h; sourceTree = ""; }; 4E838C3F299F419900ED0875 /* ContentMerger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContentMerger.m; sourceTree = ""; }; 4E838C4429A0C94B00ED0875 /* CleverTap+CTVar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CleverTap+CTVar.h"; sourceTree = ""; }; @@ -1255,8 +1249,6 @@ 4E6383D6296DE9A7001E83E3 /* CTRequestSender.m */, 4E7929F729799E8F00B81F3C /* CTDomainFactory.h */, 4E7929F829799E8F00B81F3C /* CTDomainFactory.m */, - 4E7929FD2979CAE100B81F3C /* CTSSLPinningFactory.h */, - 4E7929FE2979CAE100B81F3C /* CTSSLPinningFactory.m */, ); path = CleverTapSDK; sourceTree = ""; @@ -1328,7 +1320,6 @@ D0BD75AF241769E40006EE55 /* CleverTap+ProductConfig.h in Headers */, D014B8F020E2FAB1001E0780 /* CTPlistInfo.h in Headers */, D014B8EC20E2FA9D001E0780 /* CleverTapTrackedViewController.h in Headers */, - 4E792A002979CAE100B81F3C /* CTSSLPinningFactory.h in Headers */, D0BD75AC241769710006EE55 /* CleverTap+FeatureFlags.h in Headers */, 4E7929FA29799E8F00B81F3C /* CTDomainFactory.h in Headers */, 4E25E3CC278887A80008C888 /* CTFlexibleIdentityRepo.h in Headers */, @@ -1428,7 +1419,6 @@ 071EB4FD217F6427008F0FAB /* CTInAppUtils.h in Headers */, D0213D54207D93C300FE5740 /* CTConstants.h in Headers */, D0047B0A2098D45B0019C6FD /* CTLocalDataStore.h in Headers */, - 4E7929FF2979CAE100B81F3C /* CTSSLPinningFactory.h in Headers */, 071EB4F0217F6427008F0FAB /* CTInAppDisplayViewControllerPrivate.h in Headers */, 4987C668251B5F9E003E6BE8 /* CTImageInAppViewControllerPrivate.h in Headers */, D01A0899207ED98300423D6F /* CTPlistInfo.h in Headers */, @@ -1798,7 +1788,6 @@ 071EB519217F6513008F0FAB /* CTInAppFCManager.m in Sources */, 4EA64A2F296C1190001D9B22 /* CTRequest.m in Sources */, 49C189A6243B13110003E4D4 /* CleverTapFeatureFlags.m in Sources */, - 4E792A022979CAE100B81F3C /* CTSSLPinningFactory.m in Sources */, 4E41FD95294F46510001FBED /* CTVar.m in Sources */, 4E6383DA296DE9A8001E83E3 /* CTRequestSender.m in Sources */, D014B8ED20E2FAA2001E0780 /* CleverTapTrackedViewController.m in Sources */, @@ -1959,7 +1948,6 @@ 49E2B18324178E7400AD704B /* CleverTapConfigValue.m in Sources */, 07B94540219EA34300D4C542 /* CTInboxController.m in Sources */, 4E25E3C4278887A70008C888 /* CTLegacyIdentityRepo.m in Sources */, - 4E792A012979CAE100B81F3C /* CTSSLPinningFactory.m in Sources */, 078C63AA22420321001FDDB8 /* CleverTapJSInterface.m in Sources */, 4E49AE55275D24570074A774 /* CTValidationResultStack.m in Sources */, D01A0895207EC2D400423D6F /* CleverTapInstanceConfig.m in Sources */, From d93e72a0ad4f1b37cc27e75055e0b55898748e38 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Mon, 27 Feb 2023 11:26:54 +0530 Subject: [PATCH 05/86] updated sdk version. added status code handling for defineVars API --- CleverTap-iOS-SDK.podspec | 6 +++--- CleverTapSDK/CleverTap.m | 13 +++++++++---- CleverTapSDK/CleverTapBuildInfo.h | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CleverTap-iOS-SDK.podspec b/CleverTap-iOS-SDK.podspec index b3d176dc..d1a38df2 100644 --- a/CleverTap-iOS-SDK.podspec +++ b/CleverTap-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "CleverTap-iOS-SDK" -s.version = "4.2.0" +s.version = "5.0.0" s.summary = "The CleverTap iOS SDK for App Analytics and Engagement." s.homepage = "https://github.com/CleverTap/clevertap-ios-sdk" s.license = { :type => "MIT" } @@ -13,9 +13,9 @@ s.ios.dependency 'SDWebImage', '~> 5.11' s.ios.resource_bundle = {'CleverTapSDK' => ['CleverTapSDK/**/*.{png,xib}', 'CleverTapSDK/**/*.xcdatamodeld']} s.ios.deployment_target = '9.0' s.ios.source_files = 'CleverTapSDK/**/*.{h,m}' -s.ios.public_header_files = 'CleverTapSDK/CleverTap.h', 'CleverTapSDK/CleverTap+SSLPinning.h','CleverTapSDK/CleverTap+Inbox.h', 'CleverTapSDK/CleverTapInstanceConfig.h', 'CleverTapSDK/CleverTapBuildInfo.h', 'CleverTapSDK/CleverTapEventDetail.h', 'CleverTapSDK/CleverTapInAppNotificationDelegate.h', 'CleverTapSDK/CleverTapSyncDelegate.h', 'CleverTapSDK/CleverTapTrackedViewController.h', 'CleverTapSDK/CleverTapUTMDetail.h', 'CleverTapSDK/CleverTapJSInterface.h', 'CleverTapSDK/CleverTap+DisplayUnit.h', 'CleverTapSDK/CleverTap+FeatureFlags.h', 'CleverTapSDK/CleverTap+ProductConfig.h', 'CleverTapSDK/CleverTapPushNotificationDelegate.h', 'CleverTapSDK/CleverTapURLDelegate.h', 'CleverTapSDK/CleverTap+InAppNotifications.h', 'CleverTapSDK/CleverTap+SCDomain.h', 'CleverTapSDK/CleverTap+PushPermission.h', 'CleverTapSDK/InApps/CTLocalInApp.h' +s.ios.public_header_files = 'CleverTapSDK/CleverTap.h', 'CleverTapSDK/CleverTap+SSLPinning.h','CleverTapSDK/CleverTap+Inbox.h', 'CleverTapSDK/CleverTapInstanceConfig.h', 'CleverTapSDK/CleverTapBuildInfo.h', 'CleverTapSDK/CleverTapEventDetail.h', 'CleverTapSDK/CleverTapInAppNotificationDelegate.h', 'CleverTapSDK/CleverTapSyncDelegate.h', 'CleverTapSDK/CleverTapTrackedViewController.h', 'CleverTapSDK/CleverTapUTMDetail.h', 'CleverTapSDK/CleverTapJSInterface.h', 'CleverTapSDK/CleverTap+DisplayUnit.h', 'CleverTapSDK/CleverTap+FeatureFlags.h', 'CleverTapSDK/CleverTap+ProductConfig.h', 'CleverTapSDK/CleverTapPushNotificationDelegate.h', 'CleverTapSDK/CleverTapURLDelegate.h', 'CleverTapSDK/CleverTap+InAppNotifications.h', 'CleverTapSDK/CleverTap+SCDomain.h', 'CleverTapSDK/CleverTap+PushPermission.h', 'CleverTapSDK/InApps/CTLocalInApp.h', 'CleverTapSDK/CleverTap+CTVar.h', 'CleverTapSDK/ProductExperiences/CTVar.h' s.tvos.deployment_target = '9.0' s.tvos.source_files = 'CleverTapSDK/*.{h,m}', 'CleverTapSDK/ProductConfig/**/*.{h,m}', 'CleverTapSDK/FeatureFlags/**/*.{h,m}' s.tvos.exclude_files = 'CleverTapSDK/CleverTapJSInterface.{h,m}' -s.tvos.public_header_files = 'CleverTapSDK/CleverTap.h', 'CleverTapSDK/CleverTap+SSLPinning.h', 'CleverTapSDK/CleverTapInstanceConfig.h', 'CleverTapSDK/CleverTapBuildInfo.h', 'CleverTapSDK/CleverTapEventDetail.h', 'CleverTapSDK/CleverTapSyncDelegate.h', 'CleverTapSDK/CleverTapTrackedViewController.h', 'CleverTapSDK/CleverTapUTMDetail.h', 'CleverTapSDK/CleverTap+FeatureFlags.h', 'CleverTapSDK/CleverTap+ProductConfig.h' +s.tvos.public_header_files = 'CleverTapSDK/CleverTap.h', 'CleverTapSDK/CleverTap+SSLPinning.h', 'CleverTapSDK/CleverTapInstanceConfig.h', 'CleverTapSDK/CleverTapBuildInfo.h', 'CleverTapSDK/CleverTapEventDetail.h', 'CleverTapSDK/CleverTapSyncDelegate.h', 'CleverTapSDK/CleverTapTrackedViewController.h', 'CleverTapSDK/CleverTapUTMDetail.h', 'CleverTapSDK/CleverTap+FeatureFlags.h', 'CleverTapSDK/CleverTap+ProductConfig.h', 'CleverTapSDK/CleverTap+CTVar.h', 'CleverTapSDK/ProductExperiences/CTVar.h' end diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 73baaa97..112f95f1 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -5136,12 +5136,17 @@ - (void)_syncVars { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; if (httpResponse.statusCode == 200) { CleverTapLogDebug(self->_config.logLevel, @"%@: Vars synced successfully", self); - } else { - CleverTapLogDebug(self->_config.logLevel, @"%@: error syncing vars", self); } - } else { - CleverTapLogDebug(self->_config.logLevel, @"%@: error syncing vars", self); + else if (httpResponse.statusCode == 401) { + CleverTapLogDebug(self->_config.logLevel, @"%@: Unauthorized access from a non-test device", self); + } + } + CT_TRY + id jsonResp = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; + if (jsonResp[@"error"]) { + CleverTapLogDebug(self->_config.logLevel, @"%@: Error while syncing vars: %@", self, jsonResp[@"error"]); } + CT_END_TRY dispatch_semaphore_signal(semaphore); }]; [ctRequest onError:^(NSError * _Nullable error) { diff --git a/CleverTapSDK/CleverTapBuildInfo.h b/CleverTapSDK/CleverTapBuildInfo.h index ef2f044d..37644efc 100644 --- a/CleverTapSDK/CleverTapBuildInfo.h +++ b/CleverTapSDK/CleverTapBuildInfo.h @@ -1,3 +1,3 @@ -#define WR_SDK_REVISION @"40200" +#define WR_SDK_REVISION @"50000" From b448bbceec5c6f51ef871141b78e71d344e5f2fe Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Mon, 27 Feb 2023 11:49:11 +0530 Subject: [PATCH 06/86] fixed SPM related build errors --- CleverTapSDK/include/CTVar.h | 1 + CleverTapSDK/include/CleverTap+CTVar.h | 1 + 2 files changed, 2 insertions(+) create mode 120000 CleverTapSDK/include/CTVar.h create mode 120000 CleverTapSDK/include/CleverTap+CTVar.h diff --git a/CleverTapSDK/include/CTVar.h b/CleverTapSDK/include/CTVar.h new file mode 120000 index 00000000..39db957f --- /dev/null +++ b/CleverTapSDK/include/CTVar.h @@ -0,0 +1 @@ +../ProductExperiences/CTVar.h \ No newline at end of file diff --git a/CleverTapSDK/include/CleverTap+CTVar.h b/CleverTapSDK/include/CleverTap+CTVar.h new file mode 120000 index 00000000..efcc8cdb --- /dev/null +++ b/CleverTapSDK/include/CleverTap+CTVar.h @@ -0,0 +1 @@ +../CleverTap+CTVar.h \ No newline at end of file From bc29f8686b8bd3f75b83ea5398e46a93e1b24396 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Tue, 28 Feb 2023 14:09:50 +0530 Subject: [PATCH 07/86] added static defineVars endpoint for testing. TODO: remove static endpoint --- CleverTapSDK/CleverTap.m | 10 ++++++++-- CleverTapSDK/ProductExperiences/CTVar.m | 4 ++-- CleverTapSDK/ProductExperiences/ContentMerger.m | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 112f95f1..03bb641f 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -5129,7 +5129,10 @@ - (void)_syncVars { NSArray *payload = @[meta,varsPayload]; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - CTRequest *ctRequest = [CTRequestFactory syncVarsRequestWithConfig:self.config params:payload url:[NSString stringWithFormat:@"https://%@/defineVars",self.domainFactory.redirectDomain]]; + + // TODO: REMOVE STATIC ENDPOINT +// CTRequest *ctRequest = [CTRequestFactory syncVarsRequestWithConfig:self.config params:payload url:[NSString stringWithFormat:@"https://%@/defineVars",self.domainFactory.redirectDomain]]; + CTRequest *ctRequest = [CTRequestFactory syncVarsRequestWithConfig:self.config params:payload url:@"https://sk1-staging-25.clevertap-prod.com/defineVars"]; [ctRequest onResponse:^(NSData * _Nullable data, NSURLResponse * _Nullable response) { if ([response isKindOfClass:[NSHTTPURLResponse class]]) { @@ -5138,7 +5141,7 @@ - (void)_syncVars { CleverTapLogDebug(self->_config.logLevel, @"%@: Vars synced successfully", self); } else if (httpResponse.statusCode == 401) { - CleverTapLogDebug(self->_config.logLevel, @"%@: Unauthorized access from a non-test device", self); + CleverTapLogDebug(self->_config.logLevel, @"%@: Unauthorized access from a non-test profile. Please mark this profile as a test profile from the CleverTap dashboard.", self); } } CT_TRY @@ -5176,6 +5179,9 @@ - (NSDictionary*)varsPayload { if ([varValue.kind isEqualToString:CT_KIND_INT] || [varValue.kind isEqualToString:CT_KIND_FLOAT]) { varData[@"type"] = @"number"; } + else if ([varValue.kind isEqualToString:CT_KIND_BOOLEAN]) { + varData[@"type"] = @"boolean"; + } else { varData[@"type"] = varValue.kind; } diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index 66411ec6..6ccb8a29 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -241,7 +241,7 @@ - (void)update NSObject *oldValue = _value; _value = [varCache getMergedValueFromComponentArray:_nameComponents]; - //TODO: hadStarted logic + // TODO: hadStarted logic if ([_value isEqual:oldValue] && _hadStarted) { return; } @@ -282,7 +282,7 @@ - (void)update // } // } - //TODO: hadStarted logic + // TODO: hadStarted logic // TODO: Add hasStarted equivalent logic if (varCache.appLaunchedRecorded) { _hadStarted = YES; diff --git a/CleverTapSDK/ProductExperiences/ContentMerger.m b/CleverTapSDK/ProductExperiences/ContentMerger.m index 49b8fe62..2c6c8bd7 100644 --- a/CleverTapSDK/ProductExperiences/ContentMerger.m +++ b/CleverTapSDK/ProductExperiences/ContentMerger.m @@ -27,7 +27,7 @@ + (id)mergeWithVars:(id)vars diff:(id)diff { return diff; } - //TODO: add merging for array types from LP ContentMerger + // TODO: add merging for array types from LP ContentMerger NSMutableDictionary *merged = [NSMutableDictionary dictionary]; if ([vars isKindOfClass:[NSDictionary class]]) { From a5f3d9a19f5ef7f6496804bb553d5a2901a516d5 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Tue, 28 Feb 2023 15:35:38 +0530 Subject: [PATCH 08/86] refactored LPVarDelegate to CTVarDelegate. --- CleverTapSDK/ProductExperiences/CTVar-Internal.h | 2 +- CleverTapSDK/ProductExperiences/CTVar.h | 6 +++--- CleverTapSDK/ProductExperiences/CTVar.m | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVar-Internal.h b/CleverTapSDK/ProductExperiences/CTVar-Internal.h index c43dc6d5..14c952c1 100644 --- a/CleverTapSDK/ProductExperiences/CTVar-Internal.h +++ b/CleverTapSDK/ProductExperiences/CTVar-Internal.h @@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN //@property (readonly, strong) NSMutableArray *fileReadyBlocks; @property (readonly, strong) NSMutableArray *valueChangedBlocks; @property (readonly) BOOL fileIsPending; -@property (nonatomic, unsafe_unretained, nullable) id delegate; +@property (nonatomic, unsafe_unretained, nullable) id delegate; @property (readonly) BOOL hasChanged; - (void) update; diff --git a/CleverTapSDK/ProductExperiences/CTVar.h b/CleverTapSDK/ProductExperiences/CTVar.h index b634fc3a..5cbb6b7d 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.h +++ b/CleverTapSDK/ProductExperiences/CTVar.h @@ -12,7 +12,7 @@ typedef void (^CleverTapForceContentUpdateBlock)(BOOL success); * Receives callbacks for {@link CTVar} */ NS_SWIFT_NAME(VarDelegate) -@protocol LPVarDelegate +@protocol CTVarDelegate @optional /** * For file variables, called when the file is ready. @@ -127,9 +127,9 @@ NS_SWIFT_NAME(Var) /** * Sets the delegate of the variable in order to use - * {@link LPVarDelegate::fileIsReady:} and {@link LPVarDelegate::valueDidChange:} + * {@link CTVarDelegate::fileIsReady:} and {@link CTVarDelegate::valueDidChange:} */ -- (void)setDelegate:(nullable id )delegate; +- (void)setDelegate:(nullable id )delegate; /** * @{ diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index 6ccb8a29..253bbafa 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -363,7 +363,7 @@ - (void)onValueChanged:(CleverTapVariablesChangedBlock)block //} // TODO: Check if this method is needed -- (void)setDelegate:(id)delegate +- (void)setDelegate:(id)delegate { // CT_TRY _delegate = delegate; From e04f1b2f7dc3a391121e51ef219e90506602a6c5 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Fri, 3 Mar 2023 11:09:08 +0530 Subject: [PATCH 09/86] added unit tests for vars --- CleverTapSDK.xcodeproj/project.pbxproj | 10 ++ CleverTapSDK/CleverTap.m | 2 + CleverTapSDK/ProductExperiences/CTVar.h | 2 +- CleverTapSDK/ProductExperiences/CTVar.m | 12 +- CleverTapSDK/ProductExperiences/CTVarCache.h | 10 +- CleverTapSDK/ProductExperiences/CTVarCache.m | 73 ++++---- CleverTapSDKTests/CTVarCache+Tests.h | 17 ++ CleverTapSDKTests/CTVarCache+Tests.m | 21 +++ CleverTapSDKTests/CTVarTests.m | 165 +++++++++++++++++++ 9 files changed, 262 insertions(+), 50 deletions(-) create mode 100644 CleverTapSDKTests/CTVarCache+Tests.h create mode 100644 CleverTapSDKTests/CTVarCache+Tests.m create mode 100644 CleverTapSDKTests/CTVarTests.m diff --git a/CleverTapSDK.xcodeproj/project.pbxproj b/CleverTapSDK.xcodeproj/project.pbxproj index 87142d3d..8c3a2d5b 100644 --- a/CleverTapSDK.xcodeproj/project.pbxproj +++ b/CleverTapSDK.xcodeproj/project.pbxproj @@ -242,6 +242,8 @@ 4EA64A2E296C1190001D9B22 /* CTRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EA64A2B296C1190001D9B22 /* CTRequest.m */; }; 4EA64A2F296C1190001D9B22 /* CTRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EA64A2B296C1190001D9B22 /* CTRequest.m */; }; 4EC2D085278AAD8000F4DE54 /* IdentityManagementTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EC2D084278AAD8000F4DE54 /* IdentityManagementTests.m */; }; + 4EED219B29AF6368006CEA19 /* CTVarTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EED219A29AF6368006CEA19 /* CTVarTests.m */; }; + 4EF1CF9E29B076A300E3CB6A /* CTVarCache+Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EF1CF9D29B076A300E3CB6A /* CTVarCache+Tests.m */; }; 5709005327FD8E1F0011B89F /* CleverTap+SCDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = 5709005227FD8E1E0011B89F /* CleverTap+SCDomain.h */; settings = {ATTRIBUTES = (Public, ); }; }; 57D2E1C82684B1630068E45A /* CleverTap.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C7BBC8207D8837001345EF /* CleverTap.h */; settings = {ATTRIBUTES = (Public, ); }; }; 57EDC7A12683845B001DD157 /* CleverTap+InAppNotifications.h in Headers */ = {isa = PBXBuildFile; fileRef = 57EDC7A02683845B001DD157 /* CleverTap+InAppNotifications.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -634,6 +636,9 @@ 4EA64A2B296C1190001D9B22 /* CTRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTRequest.m; sourceTree = ""; }; 4EC2D084278AAD8000F4DE54 /* IdentityManagementTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IdentityManagementTests.m; sourceTree = ""; }; 4EDCDE4D278AC4DF0065E699 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 4EED219A29AF6368006CEA19 /* CTVarTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVarTests.m; sourceTree = ""; }; + 4EF1CF9C29B076A300E3CB6A /* CTVarCache+Tests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CTVarCache+Tests.h"; sourceTree = ""; }; + 4EF1CF9D29B076A300E3CB6A /* CTVarCache+Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CTVarCache+Tests.m"; sourceTree = ""; }; 5709005227FD8E1E0011B89F /* CleverTap+SCDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CleverTap+SCDomain.h"; sourceTree = ""; }; 57EDC7A02683845B001DD157 /* CleverTap+InAppNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CleverTap+InAppNotifications.h"; sourceTree = ""; }; 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CleverTapInstanceConfigTests.m; sourceTree = ""; }; @@ -1052,11 +1057,14 @@ 4E1F155A276B662C009387AE /* EventDetail.m */, D02AC2E6276402160031C1BE /* CleverTap+Tests.h */, D02AC2E8276402CF0031C1BE /* CleverTap+Tests.m */, + 4EF1CF9C29B076A300E3CB6A /* CTVarCache+Tests.h */, + 4EF1CF9D29B076A300E3CB6A /* CTVarCache+Tests.m */, D02AC2DA276044F70031C1BE /* CleverTapSDKTests.m */, 4E1F154E27691CA0009387AE /* CleverTapInstanceTests.m */, 4E1F155027692042009387AE /* EventTests.m */, 4EC2D084278AAD8000F4DE54 /* IdentityManagementTests.m */, 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */, + 4EED219A29AF6368006CEA19 /* CTVarTests.m */, ); path = CleverTapSDKTests; sourceTree = ""; @@ -1838,10 +1846,12 @@ files = ( 4E1F154F27691CA0009387AE /* CleverTapInstanceTests.m in Sources */, 4E1F155B276B662C009387AE /* EventDetail.m in Sources */, + 4EED219B29AF6368006CEA19 /* CTVarTests.m in Sources */, D02AC2DB276044F70031C1BE /* CleverTapSDKTests.m in Sources */, D02AC2EB2767F4590031C1BE /* BaseTestCase.m in Sources */, 4E1F15532769B83D009387AE /* EventTests.m in Sources */, 6A2E4C18291E8A4A00385536 /* CleverTapInstanceConfigTests.m in Sources */, + 4EF1CF9E29B076A300E3CB6A /* CTVarCache+Tests.m in Sources */, 4EC2D085278AAD8000F4DE54 /* IdentityManagementTests.m in Sources */, 4E1F155227692A11009387AE /* CleverTap+Tests.m in Sources */, ); diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 03bb641f..99e382fb 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -5233,6 +5233,8 @@ - (void)triggerVariablesChanged } } +#pragma mark - PE Vars + - (CTVar *)defineVar:(NSString *)name { return [self.varCache define:name with:nil kind:nil]; } diff --git a/CleverTapSDK/ProductExperiences/CTVar.h b/CleverTapSDK/ProductExperiences/CTVar.h index 5cbb6b7d..c3219d84 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.h +++ b/CleverTapSDK/ProductExperiences/CTVar.h @@ -139,7 +139,7 @@ NS_SWIFT_NAME(Var) - (id)objectAtIndex:(NSUInteger )index; - (id)objectForKeyPath:(nullable id)firstComponent, ... NS_REQUIRES_NIL_TERMINATION; - (id)objectForKeyPathComponents:(nullable NSArray *)pathComponents; -- (NSUInteger)count; +//- (NSUInteger)count; - (nullable NSNumber *)numberValue; - (nullable NSString *)stringValue; diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index 253bbafa..05d065fe 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -453,12 +453,12 @@ - (id)objectForKeyPathComponents:(NSArray *)pathComponents return nil; } -- (NSUInteger)count -{ - CT_TRY - return [[varCache getMergedValueFromComponentArray:_nameComponents] count]; - CT_END_TRY -} +//- (NSUInteger)count +//{ +// CT_TRY +// return [[varCache getMergedValueFromComponentArray:_nameComponents] count]; +// CT_END_TRY +//} #pragma mark Value accessors diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.h b/CleverTapSDK/ProductExperiences/CTVarCache.h index 165ba101..a1dc3892 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.h +++ b/CleverTapSDK/ProductExperiences/CTVarCache.h @@ -21,7 +21,7 @@ NS_SWIFT_NAME(VarCache) //NS_SWIFT_NAME(shared()); // Location initialization -- (void)registerRegionInitBlock:(RegionInitBlock)block; +//- (void)registerRegionInitBlock:(RegionInitBlock)block; // Handling variables. - (CTVar *)define:(NSString *)name @@ -53,12 +53,12 @@ NS_SWIFT_NAME(define(name:value:kind:)); - (void)setSilent:(BOOL)silent; - (BOOL)silent; - (int)contentVersion; -- (nullable NSArray *)variants; -- (nullable NSDictionary *)regions; +//- (nullable NSArray *)variants; +//- (nullable NSDictionary *)regions; - (nullable NSDictionary *)defaultKinds; -- (nullable NSDictionary *)variantDebugInfo; -- (void)setVariantDebugInfo:(nullable NSDictionary *)variantDebugInfo; +//- (nullable NSDictionary *)variantDebugInfo; +//- (void)setVariantDebugInfo:(nullable NSDictionary *)variantDebugInfo; //- (void)clearUserContent; // diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 0af01e88..cd1bc0e4 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -284,7 +284,10 @@ - (void)loadDiffs @try { NSString *fileName = [self dataArchiveFileName]; NSString *filePath = [CTPreferences filePathfromFileName:fileName]; - NSData *encryptedDiffs = [NSData dataWithContentsOfFile:filePath];; + NSData *diffsData = [NSData dataWithContentsOfFile:filePath]; + if (!diffsData) { + return; + } NSDictionary *diffs; NSDictionary *messages; NSArray *variants; @@ -293,49 +296,43 @@ - (void)loadDiffs NSDictionary *regions; NSString *varsJson; NSString *varsSignature; - if (encryptedDiffs) { - NSData *diffsData = encryptedDiffs; - if (!diffsData) { + + NSKeyedUnarchiver *unarchiver; + if (@available(iOS 12.0, *)) { + NSError *error = nil; + unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:diffsData error:&error]; + if (error != nil) { + CleverTapLogDebug(self.config.logLevel, @"%@: Error while loading variables: %@", self, error.localizedDescription); return; } - - NSKeyedUnarchiver *unarchiver; - if (@available(iOS 12.0, *)) { - NSError *error = nil; - unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:diffsData error:&error]; - if (error != nil) { - CleverTapLogDebug(self.config.logLevel, @"%@: Error while loading variables: %@", self, error.localizedDescription); - return; - } - unarchiver.requiresSecureCoding = NO; - } else { + unarchiver.requiresSecureCoding = NO; + } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:diffsData]; + unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:diffsData]; #pragma clang diagnostic pop - } - diffs = (NSDictionary *) [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; -// messages = (NSDictionary *) [unarchiver decodeObjectForKey:LEANPLUM_DEFAULTS_MESSAGES_KEY]; -// regions = (NSDictionary *)[unarchiver decodeObjectForKey:LP_KEY_REGIONS]; -// variants = (NSArray *)[unarchiver decodeObjectForKey:LP_KEY_VARIANTS]; -// variantDebugInfo = (NSDictionary *)[unarchiver decodeObjectForKey:LP_KEY_VARIANT_DEBUG_INFO]; - varsJson = [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARS_JSON_KEY]; -// varsSignature = [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARS_SIGNATURE_KEY]; -// NSString *deviceId = [unarchiver decodeObjectForKey:LP_PARAM_DEVICE_ID]; -// NSString *userId = [unarchiver decodeObjectForKey:LP_PARAM_USER_ID]; -// BOOL loggingEnabled = [unarchiver decodeBoolForKey:LP_KEY_LOGGING_ENABLED]; -// localCaps = [unarchiver decodeObjectForKey:LEANPLUM_DEFAULTS_LOCAL_CAPS_KEY]; -// if (deviceId) { -// [[Leanplum user] setDeviceId:deviceId]; -// } -// if (userId) { -// [[Leanplum user] setUserId:userId]; -// } -// if (loggingEnabled) { -// [LPConstantsState sharedState].loggingEnabled = YES; -// } } - + diffs = (NSDictionary *) [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; + // messages = (NSDictionary *) [unarchiver decodeObjectForKey:LEANPLUM_DEFAULTS_MESSAGES_KEY]; + // regions = (NSDictionary *)[unarchiver decodeObjectForKey:LP_KEY_REGIONS]; + // variants = (NSArray *)[unarchiver decodeObjectForKey:LP_KEY_VARIANTS]; + // variantDebugInfo = (NSDictionary *)[unarchiver decodeObjectForKey:LP_KEY_VARIANT_DEBUG_INFO]; + varsJson = [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARS_JSON_KEY]; + // varsSignature = [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARS_SIGNATURE_KEY]; + // NSString *deviceId = [unarchiver decodeObjectForKey:LP_PARAM_DEVICE_ID]; + // NSString *userId = [unarchiver decodeObjectForKey:LP_PARAM_USER_ID]; + // BOOL loggingEnabled = [unarchiver decodeBoolForKey:LP_KEY_LOGGING_ENABLED]; + // localCaps = [unarchiver decodeObjectForKey:LEANPLUM_DEFAULTS_LOCAL_CAPS_KEY]; + // if (deviceId) { + // [[Leanplum user] setDeviceId:deviceId]; + // } + // if (userId) { + // [[Leanplum user] setUserId:userId]; + // } + // if (loggingEnabled) { + // [LPConstantsState sharedState].loggingEnabled = YES; + // } + [self applyVariableDiffs:diffs messages:messages variants:variants diff --git a/CleverTapSDKTests/CTVarCache+Tests.h b/CleverTapSDKTests/CTVarCache+Tests.h new file mode 100644 index 00000000..aad78ae3 --- /dev/null +++ b/CleverTapSDKTests/CTVarCache+Tests.h @@ -0,0 +1,17 @@ +// +// CTVarCache+Tests.h +// CleverTapSDKTests +// +// Created by Akash Malhotra on 02/03/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import "CTVarCache.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface CTVarCache (Tests) +- (NSString *)getArchiveFileName; +@end + +NS_ASSUME_NONNULL_END diff --git a/CleverTapSDKTests/CTVarCache+Tests.m b/CleverTapSDKTests/CTVarCache+Tests.m new file mode 100644 index 00000000..bd7748a8 --- /dev/null +++ b/CleverTapSDKTests/CTVarCache+Tests.m @@ -0,0 +1,21 @@ +// +// CTVarCache+Tests.m +// CleverTapSDKTests +// +// Created by Akash Malhotra on 02/03/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import "CTVarCache+Tests.h" + +@interface CTVarCache (Tests) +- (NSString*)dataArchiveFileName; +@end + +@implementation CTVarCache (Tests) + +- (NSString *)getArchiveFileName { + return [self dataArchiveFileName]; +} + +@end diff --git a/CleverTapSDKTests/CTVarTests.m b/CleverTapSDKTests/CTVarTests.m new file mode 100644 index 00000000..61ffae37 --- /dev/null +++ b/CleverTapSDKTests/CTVarTests.m @@ -0,0 +1,165 @@ +// +// CTVarCacheTests.m +// CleverTapSDKTests +// +// Created by Akash Malhotra on 01/03/23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import "CTVarCache.h" +#import +#import "CTUtils.h" +#import "CTPreferences.h" +#import "CTVarCache+Tests.h" +#import "CTConstants.h" +#import +#import +#import + +@interface CTVarTests : XCTestCase + +@end + +CTVarCache *varCache; + +@implementation CTVarTests + +- (void)setUp { + CleverTapInstanceConfig *config = [[CleverTapInstanceConfig alloc] initWithAccountId:@"id" accountToken:@"token" accountRegion:@"eu"]; + config.useCustomCleverTapId = YES; + CTDeviceInfo *deviceInfo = [[CTDeviceInfo alloc]initWithConfig:config andCleverTapID:@"test"]; + varCache = [[CTVarCache alloc]initWithConfig:config deviceInfo:deviceInfo]; +} + +- (void)tearDown { + varCache = nil; +} + +- (void)testVarCacheNotNil { + XCTAssertNotNil(varCache); +} + +- (void)testVarCacheFetchesNameComponents { + NSString *component1 = @"Primary"; + NSString *component2 = @"Secondary"; + NSString *component3 = @"Tertiary"; + NSArray *nameComponents = [varCache getNameComponents:[NSString stringWithFormat:@"%@.%@.%@",component1,component2,component3]]; + XCTAssertNotNil(nameComponents); + + BOOL expression = [nameComponents containsObject:component1] && [nameComponents containsObject:component2] && [nameComponents containsObject:component3]; + XCTAssertTrue(expression); + + XCTAssertTrue(nameComponents.count == 3); +} + +- (void)testVarCacheResgitersVars { + CTVar *varMock = OCMPartialMock([varCache define:@"test" with:@"test" kind:CT_KIND_STRING]); + [varCache registerVariable:varMock]; + + XCTAssertEqual(varCache.vars[varMock.name], varMock); +} + +- (void)testVarCacheGetVarsForName { + NSString *varName = @"test"; + CTVar *var = [varCache define:varName with:@"test" kind:CT_KIND_STRING]; + CTVar *varResult = [varCache getVariable:varName]; + + XCTAssertEqual(varResult, var); +} + +- (void)testVarCacheSavesDiffs { + id varCacheMock = OCMPartialMock(varCache); + [varCacheMock define:@"Title" with:@"Hello" kind:CT_KIND_STRING]; + + NSDictionary *updatedVarsFromServer = @{ + @"Title": @"TitleUpdated", + }; + NSString *varsJson = [CTUtils dictionaryToJsonString:updatedVarsFromServer]; + [varCacheMock + applyVariableDiffs:updatedVarsFromServer + messages:nil + variants:nil + localCaps:nil + regions:nil + variantDebugInfo:nil + varsJson:varsJson + varsSignature:nil]; + OCMVerify([varCacheMock saveDiffs]); + XCTAssertTrue([varCacheMock hasReceivedDiffs]); + + NSString *fileName = [varCacheMock getArchiveFileName]; + NSString *filePath = [CTPreferences filePathfromFileName:fileName]; + NSData *diffsData = [NSData dataWithContentsOfFile:filePath]; + NSError *error = nil; + + NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:diffsData error:&error]; + XCTAssertNil(error); + unarchiver.requiresSecureCoding = NO; + NSDictionary *loadedVars = (NSDictionary *) [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; + XCTAssertTrue([updatedVarsFromServer isEqualToDictionary:loadedVars]); + + [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; +} + +- (void)testVarCacheLoadsDiffs { + NSString *varName = @"Title"; + NSString *initialVarValue = @"Hello"; + NSString *updatedVarValue = @"TitleUpdated"; + CTVarCache *varCacheMock = OCMPartialMock(varCache); + [varCacheMock define:varName with:initialVarValue kind:CT_KIND_STRING]; + + NSDictionary *updatedVarsFromServer = @{ + varName: updatedVarValue, + }; + NSString *varsJson = [CTUtils dictionaryToJsonString:updatedVarsFromServer]; + [varCacheMock + applyVariableDiffs:updatedVarsFromServer + messages:nil + variants:nil + localCaps:nil + regions:nil + variantDebugInfo:nil + varsJson:varsJson + varsSignature:nil]; + OCMVerify([varCacheMock saveDiffs]); + XCTAssertTrue([varCacheMock hasReceivedDiffs]); + + [varCacheMock setSilent:YES]; + [varCacheMock loadDiffs]; + CTVar *loadedVar = varCacheMock.vars[varName]; + XCTAssertEqualObjects(loadedVar.value, updatedVarValue); + + NSString *fileName = [varCacheMock getArchiveFileName]; + NSString *filePath = [CTPreferences filePathfromFileName:fileName]; + NSError *error = nil; + [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; +} + +- (void)testVarValues { + NSNumber *varValue = [NSNumber numberWithDouble:6.67345983745897]; + CTVar *var = [varCache define:@"MyNumber" with:varValue kind:CT_KIND_FLOAT]; + + XCTAssertEqualObjects(var.stringValue,varValue.stringValue); + XCTAssertEqual(var.floatValue,varValue.floatValue); + XCTAssertEqual(var.intValue,varValue.intValue); + XCTAssertEqual(var.integerValue,varValue.integerValue); + XCTAssertEqual(var.doubleValue,varValue.doubleValue); + XCTAssertEqual(var.boolValue,varValue.boolValue); + XCTAssertEqual(var.longValue,varValue.longValue); + XCTAssertEqual(var.longLongValue,varValue.longLongValue); + XCTAssertEqual(var.unsignedIntValue,varValue.unsignedIntValue); + XCTAssertEqual(var.unsignedLongValue,varValue.unsignedLongValue); + XCTAssertEqual(var.unsignedIntegerValue,varValue.unsignedIntegerValue); + XCTAssertEqual(var.shortValue,varValue.shortValue); + XCTAssertEqual(var.unsignedShortValue,varValue.unsignedShortValue); + XCTAssertEqual(var.unsignedLongLongValue,varValue.unsignedLongLongValue); + XCTAssertEqual(var.cgFloatValue,varValue.doubleValue); + + CTVar *mapVar = [varCache define:@"MyMap" with:@{@"MyMapNumber":varValue} kind:CT_KIND_DICTIONARY]; + XCTAssertTrue([[mapVar objectForKey:@"MyMapNumber"]isKindOfClass:[varValue class]]); + XCTAssertTrue([mapVar.value isKindOfClass:[NSDictionary class]]); + XCTAssertTrue([mapVar.defaultValue isKindOfClass:[NSDictionary class]]); +} + +@end From 4a141de5b689f8441f54f51e09ab313a9ed91c15 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Sun, 12 Mar 2023 11:15:52 +0200 Subject: [PATCH 10/86] Remove unnecessary code from VarCache --- CleverTapSDK/ProductExperiences/CTVarCache.h | 37 +- CleverTapSDK/ProductExperiences/CTVarCache.m | 389 +------------------ 2 files changed, 4 insertions(+), 422 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.h b/CleverTapSDK/ProductExperiences/CTVarCache.h index a1dc3892..605b4cf3 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.h +++ b/CleverTapSDK/ProductExperiences/CTVarCache.h @@ -20,9 +20,6 @@ NS_SWIFT_NAME(VarCache) //+(instancetype)sharedCache //NS_SWIFT_NAME(shared()); -// Location initialization -//- (void)registerRegionInitBlock:(RegionInitBlock)block; - // Handling variables. - (CTVar *)define:(NSString *)name with:(nullable NSObject *)defaultValue @@ -41,45 +38,13 @@ NS_SWIFT_NAME(define(name:value:kind:)); - (nullable id)getMergedValueFromComponentArray:(NSArray *) components; - (nullable NSDictionary *)diffs; - (BOOL)hasReceivedDiffs; -- (void)applyVariableDiffs:(nullable NSDictionary *)diffs_ - messages:(nullable NSDictionary *)messages_ - variants:(nullable NSArray *)variants_ - localCaps:(nullable NSArray *)localCaps_ - regions:(nullable NSDictionary *)regions_ - variantDebugInfo:(nullable NSDictionary *)variantDebugInfo_ - varsJson:(nullable NSString *)varsJson_ - varsSignature:(nullable NSString *)varsSignature_; +- (void)applyVariableDiffs:(nullable NSDictionary *)diffs_; - (void)onUpdate:(CacheUpdateBlock)block; - (void)setSilent:(BOOL)silent; - (BOOL)silent; -- (int)contentVersion; -//- (nullable NSArray *)variants; -//- (nullable NSDictionary *)regions; - (nullable NSDictionary *)defaultKinds; -//- (nullable NSDictionary *)variantDebugInfo; -//- (void)setVariantDebugInfo:(nullable NSDictionary *)variantDebugInfo; - //- (void)clearUserContent; -// -//- (NSArray *)getLocalCaps; - -// Development mode. -//- (void)setDevModeValuesFromServer:(nullable NSDictionary *)values -// fileAttributes:(nullable NSDictionary *)fileAttributes -// actionDefinitions:(nullable NSDictionary *)actionDefinitions; -//- (BOOL)sendVariablesIfChanged; -//- (BOOL)sendActionsIfChanged; - -// Handling files. -//- (void)registerFile:(NSString *)stringValue withDefaultValue:(NSString *)defaultValue; -//- (void)maybeUploadNewFiles; -//- (nullable NSDictionary *)fileAttributes; -// -//- (nullable NSMutableDictionary *)userAttributes; -//- (void)saveUserAttributes; - -//- (LPSecuredVars *)securedVars; @property (strong, nonatomic) NSMutableDictionary *vars; @property (assign, nonatomic) BOOL appLaunchedRecorded; diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index cd1bc0e4..4200fd6c 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -1,44 +1,19 @@ -//#import "LPConstants.h" -//#import "LPFileManager.h" #import "CTVarCache.h" -//#import "LPActionTriggerManager.h" -//#import "FileMD5Hash.h" -//#import "LPKeychainWrapper.h" -//#import "LPAES.h" #import "CTUtils.h" #import "CTConstants.h" #import "CTPreferences.h" #import "ContentMerger.h" -//#import "LPRequestFactory.h" -//#import "LPRequestSender.h" -//#import "LPCountAggregator.h" -//#import "LPFileTransferManager.h" -//#import @interface CTVarCache() @property (strong, nonatomic) NSRegularExpression *varNameRegex; -@property (strong, nonatomic) NSMutableDictionary *filesToInspect; -@property (strong, nonatomic) NSMutableDictionary *fileAttributes; @property (strong, nonatomic) NSMutableDictionary *valuesFromClient; @property (readwrite, nonatomic) NSMutableDictionary *defaultKinds; @property (strong, nonatomic) NSDictionary *diffs; -@property (strong, nonatomic) NSDictionary *devModeValuesFromServer; -@property (strong, nonatomic) NSDictionary *devModeFileAttributesFromServer; -@property (strong, nonatomic) NSArray *variants; -@property (strong, nonatomic) NSArray *localCaps; -@property (strong, nonatomic) NSDictionary *variantDebugInfo; -@property (strong, nonatomic) NSMutableDictionary *userAttributes; -@property (strong, nonatomic) NSDictionary *regions; @property (strong, nonatomic) CacheUpdateBlock updateBlock; @property (assign, nonatomic) BOOL hasReceivedDiffs; @property (strong, nonatomic) id merged; @property (assign, nonatomic) BOOL silent; -@property (assign, nonatomic) int contentVersion; -@property (assign, nonatomic) BOOL hasTooManyFiles; @property (strong, nonatomic) RegionInitBlock regionInitBlock; -//@property (strong, nonatomic) LPCountAggregator *countAggregator; -@property (strong, nonatomic) NSString *varsJson; -@property (strong, nonatomic) NSString *varsSignature; @property (nonatomic, strong) CleverTapInstanceConfig *config; @property (nonatomic, strong) CTDeviceInfo *deviceInfo; @@ -79,12 +54,9 @@ - (instancetype)init - (void)initialize { self.vars = [NSMutableDictionary dictionary]; - self.filesToInspect = [NSMutableDictionary dictionary]; - self.fileAttributes = [NSMutableDictionary dictionary]; self.valuesFromClient = [NSMutableDictionary dictionary]; self.diffs = [NSMutableDictionary dictionary]; self.defaultKinds = [NSMutableDictionary dictionary]; - self.localCaps = [NSArray array]; self.hasReceivedDiffs = NO; self.silent = NO; NSError *error = NULL; @@ -188,36 +160,6 @@ - (id)traverse:(id)collection withKey:(id)key autoInsert:(BOOL)autoInsert return result; } -//- (void)registerFile:(NSString *)stringValue withDefaultValue:(NSString *)defaultValue -//{ -// if (stringValue.length == 0) { -// return; -// } -// NSString *path = [LPFileManager fileValue:stringValue withDefaultValue:defaultValue]; -// if (!path) { -// return; -// } -// self.filesToInspect[stringValue] = path; -//} - -//- (void)computeFileInfo -//{ -// for (NSString *fileName in self.filesToInspect) { -// NSString *path = self.filesToInspect[fileName]; -// NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; -// NSString *hash = (__bridge_transfer NSString *) Leanplum_FileMD5HashCreateWithPath( -// (__bridge CFStringRef) path, FileHashDefaultChunkSizeForReadingData); -// if (hash) { -// attributes[LP_KEY_HASH] = hash; -// } -// NSNumber *size = [[[NSFileManager defaultManager] -// attributesOfItemAtPath:path error:nil] objectForKey:NSFileSize]; -// attributes[LP_KEY_SIZE] = size; -// _fileAttributes[fileName] = @{@"": attributes}; -// } -// [self.filesToInspect removeAllObjects]; -//} - // Updates a JSON structure of variable values, and a dictionary of variable kinds. - (void)updateValues:(NSString *)name nameComponents:(NSArray *)nameComponents @@ -288,15 +230,6 @@ - (void)loadDiffs if (!diffsData) { return; } - NSDictionary *diffs; - NSDictionary *messages; - NSArray *variants; - NSArray *localCaps; - NSDictionary *variantDebugInfo; - NSDictionary *regions; - NSString *varsJson; - NSString *varsSignature; - NSKeyedUnarchiver *unarchiver; if (@available(iOS 12.0, *)) { NSError *error = nil; @@ -312,41 +245,12 @@ - (void)loadDiffs unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:diffsData]; #pragma clang diagnostic pop } - diffs = (NSDictionary *) [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; - // messages = (NSDictionary *) [unarchiver decodeObjectForKey:LEANPLUM_DEFAULTS_MESSAGES_KEY]; - // regions = (NSDictionary *)[unarchiver decodeObjectForKey:LP_KEY_REGIONS]; - // variants = (NSArray *)[unarchiver decodeObjectForKey:LP_KEY_VARIANTS]; - // variantDebugInfo = (NSDictionary *)[unarchiver decodeObjectForKey:LP_KEY_VARIANT_DEBUG_INFO]; - varsJson = [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARS_JSON_KEY]; - // varsSignature = [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARS_SIGNATURE_KEY]; - // NSString *deviceId = [unarchiver decodeObjectForKey:LP_PARAM_DEVICE_ID]; - // NSString *userId = [unarchiver decodeObjectForKey:LP_PARAM_USER_ID]; - // BOOL loggingEnabled = [unarchiver decodeBoolForKey:LP_KEY_LOGGING_ENABLED]; - // localCaps = [unarchiver decodeObjectForKey:LEANPLUM_DEFAULTS_LOCAL_CAPS_KEY]; - // if (deviceId) { - // [[Leanplum user] setDeviceId:deviceId]; - // } - // if (userId) { - // [[Leanplum user] setUserId:userId]; - // } - // if (loggingEnabled) { - // [LPConstantsState sharedState].loggingEnabled = YES; - // } + NSDictionary *diffs = (NSDictionary *) [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; - [self applyVariableDiffs:diffs - messages:messages - variants:variants - localCaps:localCaps - regions:regions - variantDebugInfo:variantDebugInfo - varsJson:varsJson - varsSignature:varsSignature]; + [self applyVariableDiffs:diffs]; } @catch (NSException *exception) { CleverTapLogDebug(self.config.logLevel, @"%@: Error while loading variables: %@", self, exception.debugDescription); } -// [self userAttributes]; - -// [self.countAggregator incrementCount:@"load_diffs"]; } - (void)saveDiffs @@ -362,18 +266,6 @@ - (void)saveDiffs NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:diffsData]; #pragma clang diagnostic pop [archiver encodeObject:self.diffs forKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; -// NSDictionary *messages = [[LPActionManager shared] messages]; -// [archiver encodeObject:messages forKey:LEANPLUM_DEFAULTS_MESSAGES_KEY]; -// [archiver encodeObject:self.variants forKey:LP_KEY_VARIANTS]; -// [archiver encodeObject:self.variantDebugInfo forKey:LP_KEY_VARIANT_DEBUG_INFO]; -// [archiver encodeObject:self.regions forKey:LP_KEY_REGIONS]; -// [archiver encodeObject:[LPConstantsState sharedState].sdkVersion forKey:LP_PARAM_SDK_VERSION]; -// [archiver encodeObject:[[Leanplum user] deviceId] forKey:LP_PARAM_DEVICE_ID]; -// [archiver encodeObject:[[Leanplum user] userId] forKey:LP_PARAM_USER_ID]; -// [archiver encodeBool:[LPConstantsState sharedState].loggingEnabled forKey:LP_KEY_LOGGING_ENABLED]; - [archiver encodeObject:self.varsJson forKey:CLEVERTAP_DEFAULTS_VARS_JSON_KEY]; -// [archiver encodeObject:self.varsSignature forKey:LEANPLUM_DEFAULTS_VARS_SIGNATURE_KEY]; -// [archiver encodeObject:self.localCaps forKey:LEANPLUM_DEFAULTS_LOCAL_CAPS_KEY]; [archiver finishEncoding]; // NSData *encryptedDiffs = [LPAES encryptedDataFromData:diffsData]; @@ -395,22 +287,13 @@ - (void)saveDiffs CleverTapLogStaticInternal(@"%@ failed to write data at %@: %@", self, filePath, writeError); } } -// [self.countAggregator incrementCount:@"save_diffs"]; } - (NSString*)dataArchiveFileName { return [NSString stringWithFormat:@"clevertap-%@-%@-pe-vars.plist", _config.accountId, _deviceInfo.deviceId]; } -// TODO: REMOVE IRRELEVANT PARAMETERS - (void)applyVariableDiffs:(NSDictionary *)diffs_ - messages:(NSDictionary *)messages_ - variants:(NSArray *)variants_ - localCaps:(NSArray *)localCaps_ - regions:(NSDictionary *)regions_ - variantDebugInfo:(NSDictionary *)variantDebugInfo_ - varsJson:(NSString *)varsJson_ - varsSignature:(NSString *)varsSignature_ { @synchronized (self.vars) { if (diffs_ || (!self.silent && !self.hasReceivedDiffs)) { @@ -423,7 +306,6 @@ - (void)applyVariableDiffs:(NSDictionary *)diffs_ // We need to lock it in case multiple threads will be accessing this. @synchronized (self.diffs) { self.diffs = [self convert:self.diffs]; - self.merged = [ContentMerger mergeWithVars:self.valuesFromClient diff:self.diffs]; } // Update variables with new values. @@ -436,58 +318,6 @@ - (void)applyVariableDiffs:(NSDictionary *)diffs_ CleverTapLogDebug(self.config.logLevel, @"%@: No variables received from the server", self); } } - -// if (regions_) { -// // Store regions. -// self.regions = regions_; -// } - -// if (messages_) { -// if (!self.silent) { -// // Save unmodified message data from API -// [[LPActionManager shared] setMessagesDataFromServer:messages_]; -// } -// // Process messages data -// [[LPActionManager shared] processMessagesAndDownloadFiles: messages_]; -// } - - // If LeanplumLocation is linked in, setup region monitoring. -// if (messages_ || regions_) { -// if (!self.regionInitBlock) { -// if ([LPConstantsState sharedState].isDevelopmentModeEnabled) { -// if ([regions_ count] > 0) { -// LPLog(LPInfo, @"Regions have been defined in dashboard, but the app is not built to handle them."); -// LPLog(LPInfo, @"Add LeanplumLocation.framework or LeanplumBeacon.framework to Build Settings -> Link Binary With Libraries."); -// LPLog(LPInfo, @"Disregard warning if there are no plans to utilize iBeacon or Geofencing within the app"); -// } -// } -// } else { -// NSSet *foregroundRegionNames; -// NSSet *backgroundRegionNames; -// [LPActionTriggerManager getForegroundRegionNames:&foregroundRegionNames -// andBackgroundRegionNames:&backgroundRegionNames]; -// self.regionInitBlock(self.regions, foregroundRegionNames, backgroundRegionNames); -// } -// } - -// if (variants_) { -// self.variants = variants_; -// } -// -// if (localCaps_) { -// self.localCaps = localCaps_; -// } -// -// if (variantDebugInfo_) { -// self.variantDebugInfo = variantDebugInfo_; -// } - - self.contentVersion++; - - if (varsJson_) { - self.varsJson = varsJson_; - self.varsSignature = varsSignature_; - } // DONT SAVE VARS TO CACHE IF SILENT if (!self.silent) { @@ -499,145 +329,8 @@ - (void)applyVariableDiffs:(NSDictionary *)diffs_ } } } -// [self.countAggregator incrementCount:@"apply_variable_diffs"]; } -- (BOOL)areActionDefinitionsEqual:(NSDictionary *)a other:(NSDictionary *)b -{ - if (a.count != b.count) { - return NO; - } - for (NSString *key in a) { - NSDictionary *aItem = a[key]; - NSDictionary *bItem = b[key]; - if (!bItem) { - return NO; - } - if ((aItem[@"kind"] != bItem[@"kind"]) || - (aItem[@"values"] != bItem[@"values"]) || - (aItem[@"kinds"] != bItem[@"kinds"]) || - aItem[@"options"] != bItem[@"options"]) { - return NO; - } - } - return YES; -} - -// TODO: commented sendVariablesIfChanged, sendActionsIfChanged, sendContentIfChanged and maybeUploadNewFiles. ask if these are needed - -//- (BOOL)sendVariablesIfChanged -//{ -// return [self sendContentIfChanged:YES actions:NO]; -//} -// -//- (BOOL)sendActionsIfChanged -//{ -// return [self sendContentIfChanged:NO actions:YES]; -//} -// -//- (BOOL)sendContentIfChanged:(BOOL)variables actions:(BOOL)actions -//{ -// [self computeFileInfo]; -// -// BOOL changed = NO; -// if (variables && self.devModeValuesFromServer && -// (![self.valuesFromClient isEqualToDictionary:self.devModeValuesFromServer])) { -// changed = YES; -// } -// -// NSArray *definitions = [[LPActionManager shared] definitions]; -// NSMutableDictionary *actionDefinitions = [NSMutableDictionary dictionary]; -// for (ActionDefinition *def in definitions) { -// actionDefinitions[def.name] = def.json; -// } -// if (actions && ![self areActionDefinitionsEqual:actionDefinitions -// other:[[LPActionManager shared] actionDefinitionsFromServer]]) { -// changed = YES; -// } -// -// if (changed) { -// NSDictionary *limitedFileAttributes = self.fileAttributes; -// NSDictionary *limitedValues = self.valuesFromClient; -// if ([self.fileAttributes count] > MAX_FILES_SUPPORTED) { -// limitedValues = [self.valuesFromClient mutableCopy]; -// [(NSMutableDictionary *)limitedValues removeObjectForKey:LP_VALUE_RESOURCES_VARIABLE]; -// LPLog(LPError, @"You are trying to sync %lu files, which is more than " -// @"we support (%d). If you are calling [Leanplum syncResources], try adding " -// @"regex filters to limit the number of files you are syncing.", -// (unsigned long) self.fileAttributes.count, MAX_FILES_SUPPORTED); -// limitedFileAttributes = [NSDictionary dictionary]; -// self.hasTooManyFiles = YES; -// } -// @try { -// NSMutableDictionary *args = [NSMutableDictionary dictionary]; -// if (variables) { -// args[LP_PARAM_VARS] = [LPJSON stringFromJSON:limitedValues]; -// args[LP_PARAM_KINDS] = [LPJSON stringFromJSON:self.defaultKinds]; -// } -// if (actions) { -// NSMutableDictionary *jsonDefinitions = [NSMutableDictionary new]; -// [definitions enumerateObjectsUsingBlock:^(ActionDefinition *ac, NSUInteger idx, BOOL *stop) { -// jsonDefinitions[ac.name] = [ac json]; -// }]; -// -// args[LP_PARAM_ACTION_DEFINITIONS] = [LPJSON stringFromJSON:jsonDefinitions]; -// } -// args[LP_PARAM_FILE_ATTRIBUTES] = [LPJSON stringFromJSON:limitedFileAttributes]; -// LPRequest *request = [LPRequestFactory setVarsWithParams:args]; -// [[LPRequestSender sharedInstance] send:request]; -// return YES; -// } @catch (NSException *e) { -// [Leanplum throwError:@"Cannot serialize variable values. " -// @"Make sure your variables are JSON serializable."]; -// } -// } -// return NO; -//} -// -//- (void)maybeUploadNewFiles -//{ -// if (self.hasTooManyFiles || -// !self.devModeFileAttributesFromServer || -// ![Leanplum hasStartedAndRegisteredAsDeveloper]) { -// return; -// } -// -// NSMutableArray *filenames = [NSMutableArray array]; -// NSMutableArray *fileData = [NSMutableArray array]; -// int totalSize = 0; -// for (NSString *name in self.fileAttributes) { -// NSDictionary *variationAttributes = self.fileAttributes[name]; -// NSDictionary *localAttributes = variationAttributes[@""]; -// NSDictionary *serverAttributes = self.devModeFileAttributesFromServer[name][@""]; -// if ([LPFileManager isNewerLocally:localAttributes orRemotely:serverAttributes]) { -// NSString *hash = [localAttributes valueForKey:LP_KEY_HASH]; -// if (!hash) { -// hash = @""; -// } -// NSString *variationPath = [LPFileManager fileRelativeToAppBundle:name]; -// if ((totalSize > MAX_UPLOAD_BATCH_SIZES && -// filenames.count > 0) || filenames.count >= MAX_UPLOAD_BATCH_FILES) { -// LPRequest *request = [LPRequestFactory uploadFileWithParams:@{LP_PARAM_DATA: [LPJSON stringFromJSON:fileData]}]; -// [[LPRequestSender sharedInstance] send:request]; -// filenames = [NSMutableArray array]; -// fileData = [NSMutableArray array]; -// totalSize = 0; -// } -// NSNumber *size = [localAttributes valueForKey:LP_KEY_SIZE]; -// totalSize += [size intValue]; -// [fileData addObject:@{ -// LP_KEY_HASH: hash, -// LP_KEY_SIZE: size, -// LP_KEY_FILENAME: name -// }]; -// [filenames addObject:variationPath]; -// } -// } -// if (filenames.count > 0) { -// [[LPFileTransferManager sharedInstance] sendFilesNow:filenames fileData:fileData]; -// } -//} - //- (void)setDevModeValuesFromServer:(NSDictionary *)values // fileAttributes:(NSDictionary *)fileAttributes // actionDefinitions:(NSDictionary *)actionDefinitions @@ -650,84 +343,8 @@ - (BOOL)areActionDefinitionsEqual:(NSDictionary *)a other:(NSDictionary *)b - (void)onUpdate:(CacheUpdateBlock) block { self.updateBlock = block; - -// [self.countAggregator incrementCount:@"on_update_varcache"]; } -// -//- (NSMutableDictionary *)userAttributes -//{ -// if (!_userAttributes) { -// @try { -// NSString *token = [[ApiConfig shared] token]; -// if (token) { -// NSData *encryptedValue = [[NSUserDefaults standardUserDefaults] dataForKey:LEANPLUM_DEFAULTS_ATTRIBUTES_KEY]; -// if (encryptedValue) { -// NSData *decryptedData = [LPAES decryptedDataFromData:encryptedValue]; -// if (decryptedData) { -// NSKeyedUnarchiver *unarchiver; -// if (@available(iOS 12.0, *)) { -// NSError *error = nil; -// unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:decryptedData error:&error]; -// if (error != nil) { -// LPLog(LPError, error.localizedDescription); -// //in case of error returning empty dictionary to avoid crash -// return [NSMutableDictionary dictionary]; -// } -// unarchiver.requiresSecureCoding = NO; -// } else { -//#pragma clang diagnostic push -//#pragma clang diagnostic ignored "-Wdeprecated-declarations" -// unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData -// :decryptedData]; -//#pragma clang diagnostic pop -// } -// self.userAttributes = [(NSDictionary *)[unarchiver decodeObjectForKey:LP_PARAM_USER_ATTRIBUTES] mutableCopy]; -// } -// } -// } -// } @catch (NSException *exception) { -// LPLog(LPError, @"Could not load user attributes: %@", exception); -// } -// } -// if (!_userAttributes) { -// _userAttributes = [NSMutableDictionary dictionary]; -// } -// return _userAttributes; -//} -// -//- (void)saveUserAttributes -//{ -// RETURN_IF_NOOP; -// NSMutableData *data = [[NSMutableData alloc] init]; -//#pragma clang diagnostic push -//#pragma clang diagnostic ignored "-Wdeprecated-declarations" -// NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; -//#pragma clang diagnostic pop -// [archiver encodeObject:self.userAttributes forKey:LP_PARAM_USER_ATTRIBUTES]; -// [archiver finishEncoding]; -// -// NSData *encryptedData = [LPAES encryptedDataFromData:data]; -// -// NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; -// -// [defaults setObject:encryptedData forKey:LEANPLUM_DEFAULTS_ATTRIBUTES_KEY]; -// [Leanplum synchronizeDefaults]; -// -// [self.countAggregator incrementCount:@"save_user_attributes"]; -//} -// -//- (LPSecuredVars *)securedVars -//{ -// if ([LPUtils isNullOrEmpty:self.varsJson] || [LPUtils isNullOrEmpty:self.varsSignature]) { -// return nil; -// } -// return [[LPSecuredVars alloc] initWithJson:self.varsJson andSignature:self.varsSignature]; -//} -// -//- (NSArray *)getLocalCaps -//{ -// return self.localCaps; -//} + // //- (void)clearUserContent //{ From 2a864094022d37fe4fb9edd8bbfdaaecded0bca9 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Sun, 12 Mar 2023 20:06:06 +0200 Subject: [PATCH 11/86] ContentMerger do not mutate vars, fix vars dict, refactor --- .../ProductExperiences/ContentMerger.m | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/ContentMerger.m b/CleverTapSDK/ProductExperiences/ContentMerger.m index 2c6c8bd7..a4936c55 100644 --- a/CleverTapSDK/ProductExperiences/ContentMerger.m +++ b/CleverTapSDK/ProductExperiences/ContentMerger.m @@ -13,35 +13,37 @@ @implementation ContentMerger + (id)mergeWithVars:(id)vars diff:(id)diff { // Return the modified value if it is a `primitive` - if ([diff isKindOfClass:[NSString class]]) { - return ((NSString*)diff); - } - if ([diff isKindOfClass:[NSNumber class]]) { - return ((NSNumber*)diff); - } - if ([diff isKindOfClass:[NSNull class]]) { - return [NSNull null]; + if ([diff isKindOfClass:NSNumber.class] || + [diff isKindOfClass:NSString.class] || + [diff isKindOfClass:NSNull.class]) { + return diff; } if ([vars isKindOfClass:[NSNumber class]] || [vars isKindOfClass:[NSString class]]) { return diff; } - // TODO: add merging for array types from LP ContentMerger - NSMutableDictionary *merged = [NSMutableDictionary dictionary]; + BOOL isVarsDict = NO; if ([vars isKindOfClass:[NSDictionary class]]) { - merged = vars; + // Create new dictionary from vars + merged = [NSMutableDictionary dictionaryWithDictionary:vars]; + isVarsDict = YES; } if ([diff isKindOfClass:[NSDictionary class]]) { NSDictionary *diffDict = (NSDictionary*)diff; [diffDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { - id defaultValue = merged[key] ?: [NSNull null]; - merged[key] = [self mergeWithVars:defaultValue diff:value]; + id defaultValue = merged[key] ?: [NSNull null]; + merged[key] = [self mergeWithVars:defaultValue diff:value]; }]; + return merged; + } else if (isVarsDict) { + // vars is a dictionary but diff is not or diff is nil, return vars + return vars; } + return [NSNull null]; } From 0a47990046539a0c664fed1259c2ff221aae2d4f Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Sun, 12 Mar 2023 20:06:37 +0200 Subject: [PATCH 12/86] CTVar remove unnecessary code --- CleverTapSDK/ProductExperiences/CTVar.m | 119 +----------------------- 1 file changed, 2 insertions(+), 117 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index 05d065fe..602876b7 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -33,12 +33,12 @@ @implementation CTVar @synthesize hadStarted=_hadStarted; @synthesize hasChanged=_hasChanged; -+(BOOL)printedCallbackWarning ++ (BOOL)printedCallbackWarning { return LPVAR_PRINTED_CALLBACK_WARNING; } -+(void)setPrintedCallbackWarning:(BOOL)newPrintedCallbackWarning ++ (void)setPrintedCallbackWarning:(BOOL)newPrintedCallbackWarning { LPVAR_PRINTED_CALLBACK_WARNING = newPrintedCallbackWarning; } @@ -59,13 +59,6 @@ - (instancetype)initWithName:(NSString *)name withComponents:(NSArray *)componen [varCache registerVariable:self]; - // TODO: Commented file types -// if ([kind isEqualToString:LP_KIND_FILE]) { // TODO: && var.stringValue) -// [[LPVarCache sharedCache] registerFile:_stringValue withDefaultValue:_defaultValue]; -// } -// if ([name hasPrefix:LP_VALUE_RESOURCES_VARIABLE]) { -// _isInternal = YES; -// } [self update]; CT_END_TRY } @@ -213,12 +206,6 @@ - (instancetype)initWithName:(NSString *)name withComponents:(NSArray *)componen // return [[LPVarCache sharedCache] define:name with:defaultValue kind:LP_KIND_ARRAY]; //} -// TODO: commented color -//+ (LPVar *)define:(NSString *)name withColor:(UIColor *)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name with:@(leanplum_colorToInt(defaultValue)) kind:LP_KIND_COLOR]; -//} - #pragma mark Updating - (void) cacheComputedValues @@ -241,7 +228,6 @@ - (void)update NSObject *oldValue = _value; _value = [varCache getMergedValueFromComponentArray:_nameComponents]; - // TODO: hadStarted logic if ([_value isEqual:oldValue] && _hadStarted) { return; } @@ -251,40 +237,12 @@ - (void)update _hasChanged = YES; } - // TODO: commented files -// if ([LPVarCache sharedCache].silent && [[self name] hasPrefix:LP_VALUE_RESOURCES_VARIABLE] -// && [_kind isEqualToString:LP_KIND_FILE] && !_fileIsPending) { -// [self triggerFileIsReady]; -// } - if (varCache.silent) { return; } - // TODO: trigger value changed callback - // TODO: Add hasStarted equivalent logic -// if ([LPInternalState sharedState].hasStarted) { if (varCache.appLaunchedRecorded) { [self triggerValueChanged]; - } - - // TODO: commented files - // Check if file exists, otherwise we need to download it. - // Ignore app icon. This is a special variable that only needs the filename. -// if ([_kind isEqualToString:LP_KIND_FILE]) { -// if ([LPFileManager maybeDownloadFile:_stringValue -// defaultValue:_defaultValue -// onComplete:^{[self triggerFileIsReady];}]) { -// _fileIsPending = YES; -// } -// if ([LPInternalState sharedState].hasStarted && !_fileIsPending) { -// [self triggerFileIsReady]; -// } -// } - - // TODO: hadStarted logic - // TODO: Add hasStarted equivalent logic - if (varCache.appLaunchedRecorded) { _hadStarted = YES; } } @@ -317,51 +275,12 @@ - (void)onValueChanged:(CleverTapVariablesChangedBlock)block _valueChangedBlocks = [NSMutableArray array]; } [_valueChangedBlocks addObject:[block copy]]; - - // TODO: Add hasStarted equivalent logic -// if ([LPInternalState sharedState].hasStarted) { if (varCache.appLaunchedRecorded) { [self triggerValueChanged]; } CT_END_TRY } -#pragma mark File handling - -// TODO: commented files -//- (void)triggerFileIsReady -//{ -// _fileIsPending = NO; -// LP_BEGIN_USER_CODE -// if (self.delegate && -// [self.delegate respondsToSelector:@selector(fileIsReady:)]) { -// [self.delegate fileIsReady:self]; -// } -// -// for (LeanplumVariablesChangedBlock block in _fileReadyBlocks.copy) { -// block(); -// } -// LP_END_USER_CODE -//} - -// TODO: Commented files -//- (void)onFileReady:(LeanplumVariablesChangedBlock)block -//{ -// if (!block) { -// [Leanplum throwError:@"[LPVar onFileReady:] Nil block parameter provided."]; -// } -// -// CT_TRY -// if (!_fileReadyBlocks) { -// _fileReadyBlocks = [NSMutableArray array]; -// } -// [_fileReadyBlocks addObject:[block copy]]; -// if ([LPInternalState sharedState].hasStarted && !_fileIsPending) { -// [self triggerFileIsReady]; -// } -// CT_END_TRY -//} - // TODO: Check if this method is needed - (void)setDelegate:(id)delegate { @@ -389,30 +308,6 @@ - (void)warnIfNotStarted // } } -// TODO: commented fileValue and imagevalue - -//- (NSString *)fileValue -//{ -// CT_TRY -// [self warnIfNotStarted]; -// if ([_kind isEqualToString:LP_KIND_FILE]) { -// return [LPFileManager fileValue:_stringValue withDefaultValue:_defaultValue]; -// } -// CT_END_TRY -// return nil; -//} -// -//- (UIImage *)imageValue -//{ -// CT_TRY -// NSString *fileValue = [self fileValue]; -// if ([[NSFileManager defaultManager] fileExistsAtPath:fileValue]) { -// return [UIImage imageWithContentsOfFile:fileValue]; -// } -// CT_END_TRY -// return [UIImage imageNamed:_defaultValue]; -//} - #pragma mark Dictionary handling - (id) objectForKey:(NSString *)key @@ -453,13 +348,6 @@ - (id)objectForKeyPathComponents:(NSArray *)pathComponents return nil; } -//- (NSUInteger)count -//{ -// CT_TRY -// return [[varCache getMergedValueFromComponentArray:_nameComponents] count]; -// CT_END_TRY -//} - #pragma mark Value accessors - (NSNumber *)numberValue @@ -491,7 +379,4 @@ - (NSUInteger)unsignedIntegerValue { return [[self numberValue] unsignedIntegerV - (unsigned long)unsignedLongValue { return [[self numberValue] unsignedLongValue]; } - (unsigned long long)unsignedLongLongValue { return [[self numberValue] unsignedLongLongValue]; } -// TODO: commented color value -//- (UIColor *)colorValue { return leanplum_intToColor([self longLongValue]); } - @end From 14e7a7cd30649bc88af2819fb049b3febfe67571 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Sun, 12 Mar 2023 20:09:03 +0200 Subject: [PATCH 13/86] CTVarCache remove unnecessary code --- CleverTapSDK/ProductExperiences/CTVarCache.m | 29 +------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 4200fd6c..b6e7c325 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -19,19 +19,8 @@ @interface CTVarCache() @property (nonatomic, strong) CTDeviceInfo *deviceInfo; @end -//static CTVarCache *sharedInstance = nil; -//static dispatch_once_t leanplum_onceToken; - @implementation CTVarCache -//+(instancetype)sharedCache -//{ -// dispatch_once(&leanplum_onceToken, ^{ -// sharedInstance = [[self alloc] init]; -// }); -// return sharedInstance; -//} - - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo { if ((self = [super init])) { self.config = config; @@ -64,11 +53,6 @@ - (void)initialize options:NSRegularExpressionCaseInsensitive error:&error]; } -- (void)registerRegionInitBlock:(void (^)(NSDictionary *, NSSet *, NSSet *))block -{ - self.regionInitBlock = block; -} - - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString *)kind { if ([CTUtils isNullOrEmpty:name]) { @@ -160,13 +144,11 @@ - (id)traverse:(id)collection withKey:(id)key autoInsert:(BOOL)autoInsert return result; } -// Updates a JSON structure of variable values, and a dictionary of variable kinds. +// Updates a JSON structure of variable values - (void)updateValues:(NSString *)name nameComponents:(NSArray *)nameComponents value:(id)value - kind:(NSString *)kind values:(NSMutableDictionary *)values - kinds:(NSMutableDictionary *)kinds { if (value) { id valuesPtr = values; @@ -331,15 +313,6 @@ - (void)applyVariableDiffs:(NSDictionary *)diffs_ } } -//- (void)setDevModeValuesFromServer:(NSDictionary *)values -// fileAttributes:(NSDictionary *)fileAttributes -// actionDefinitions:(NSDictionary *)actionDefinitions -//{ -// self.devModeValuesFromServer = values; -// self.devModeFileAttributesFromServer = fileAttributes; -// [[LPActionManager shared] setActionDefinitionsFromServer: actionDefinitions]; -//} -// - (void)onUpdate:(CacheUpdateBlock) block { self.updateBlock = block; From 95b79b6187cf1362613bf1db290f9fc5e1bfb765 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Sun, 12 Mar 2023 20:09:52 +0200 Subject: [PATCH 14/86] CTVarCache merge variables --- CleverTapSDK/ProductExperiences/CTVarCache.m | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index b6e7c325..5baebb36 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -7,11 +7,12 @@ @interface CTVarCache() @property (strong, nonatomic) NSRegularExpression *varNameRegex; @property (strong, nonatomic) NSMutableDictionary *valuesFromClient; -@property (readwrite, nonatomic) NSMutableDictionary *defaultKinds; +@property (strong, nonatomic) id merged; @property (strong, nonatomic) NSDictionary *diffs; + @property (strong, nonatomic) CacheUpdateBlock updateBlock; @property (assign, nonatomic) BOOL hasReceivedDiffs; -@property (strong, nonatomic) id merged; + @property (assign, nonatomic) BOOL silent; @property (strong, nonatomic) RegionInitBlock regionInitBlock; @@ -30,22 +31,11 @@ - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CT return self; } -- (instancetype)init -{ - self = [super init]; - if (self) { - [self initialize]; -// _countAggregator = [LPCountAggregator sharedAggregator]; - } - return self; -} - - (void)initialize { self.vars = [NSMutableDictionary dictionary]; - self.valuesFromClient = [NSMutableDictionary dictionary]; self.diffs = [NSMutableDictionary dictionary]; - self.defaultKinds = [NSMutableDictionary dictionary]; + self.valuesFromClient = [NSMutableDictionary dictionary]; self.hasReceivedDiffs = NO; self.silent = NO; NSError *error = NULL; @@ -288,6 +278,7 @@ - (void)applyVariableDiffs:(NSDictionary *)diffs_ // We need to lock it in case multiple threads will be accessing this. @synchronized (self.diffs) { self.diffs = [self convert:self.diffs]; + self.merged = [ContentMerger mergeWithVars:self.valuesFromClient diff:self.diffs]; } // Update variables with new values. From 9b8e08bb350d1856663e7fbcb28324b4227677c8 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Sun, 12 Mar 2023 20:15:13 +0200 Subject: [PATCH 15/86] Add warning for existing variable --- CleverTapSDK/ProductExperiences/CTVarCache.m | 1 + 1 file changed, 1 insertion(+) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 5baebb36..65edf68e 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -54,6 +54,7 @@ - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString CT_TRY CTVar *existing = [self getVariable:name]; if (existing) { + CleverTapLogInfo(self.config.logLevel, @"%@: Variable with name: %@ already exists.", self, name); return existing; } CT_END_TRY From 09d2ba637df10a241e78d08ce95a16657640c164 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Sun, 12 Mar 2023 20:16:50 +0200 Subject: [PATCH 16/86] Remove array logic and check if variables overrides another's value --- CleverTapSDK/ProductExperiences/CTVarCache.h | 1 - CleverTapSDK/ProductExperiences/CTVarCache.m | 28 ++++++-------------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.h b/CleverTapSDK/ProductExperiences/CTVarCache.h index 605b4cf3..a88f5931 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.h +++ b/CleverTapSDK/ProductExperiences/CTVarCache.h @@ -42,7 +42,6 @@ NS_SWIFT_NAME(define(name:value:kind:)); - (void)onUpdate:(CacheUpdateBlock)block; - (void)setSilent:(BOOL)silent; - (BOOL)silent; -- (nullable NSDictionary *)defaultKinds; //- (void)clearUserContent; diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 65edf68e..b51de7db 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -67,7 +67,7 @@ - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString } } -- (NSArray *) arrayOfCaptureComponentsOfString:(NSString *)data matchedBy:(NSRegularExpression *)regExpression +- (NSArray *)arrayOfCaptureComponentsOfString:(NSString *)data matchedBy:(NSRegularExpression *)regExpression { NSMutableArray *test = [NSMutableArray array]; @@ -116,16 +116,6 @@ - (id)traverse:(id)collection withKey:(id)key autoInsert:(BOOL)autoInsert result = [NSMutableDictionary dictionary]; [collection setObject:result forKey:key]; } - } else if ([collection isKindOfClass:[NSArray class]]) { - int index = [key intValue]; - NSArray *arrayCollection = collection; - if (arrayCollection.count > index) { - result = arrayCollection[index]; - if (autoInsert && !result && [key isKindOfClass:NSString.class]) { - result = [NSMutableArray array]; - [collection setObject:result atIndex:index]; - } - } } if ([result isKindOfClass:[NSNull class]]) { @@ -153,26 +143,24 @@ - (void)updateValues:(NSString *)name [value class] != [NSMutableDictionary class]) { value = [NSMutableDictionary dictionaryWithDictionary:value]; } - if ([value isKindOfClass:NSArray.class] && - [value class] != [NSMutableArray class]) { - value = [NSMutableArray arrayWithArray:value]; + + // Check if value from another Variable will be overridden + if (valuesPtr[nameComponents.lastObject] && ![valuesPtr[nameComponents.lastObject] isEqual:value]) { + CleverTapLogInfo(self.config.logLevel, @"%@: Variable with name: %@ will override value: %@, with new value: %@.", self, name, valuesPtr[nameComponents.lastObject], value); } + [valuesPtr setObject:value forKey:nameComponents.lastObject]; } - if (kind) { - kinds[name] = kind; - } } - (void)registerVariable:(CTVar *)var { [self.vars setObject:var forKey:var.name]; + [self updateValues:var.name nameComponents:var.nameComponents value:var.defaultValue - kind:var.kind - values:self.valuesFromClient - kinds:_defaultKinds]; + values:self.valuesFromClient]; } - (CTVar *)getVariable:(NSString *)name From 0287a97ca33640e05d6fa1e3a76a150e670df95f Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Sun, 12 Mar 2023 20:17:32 +0200 Subject: [PATCH 17/86] Merge Variable again if defined after applyVariablesDiff --- CleverTapSDK/ProductExperiences/CTVarCache.m | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index b51de7db..f0c22b8f 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -153,6 +153,29 @@ - (void)updateValues:(NSString *)name } } +// Use this method to merge default variable value with VarCache.merged value +// This is neccessary if variable was registered after VarCache.applyVariableDiffs +- (void)mergeVariable:(CTVar * _Nonnull)var { + NSString *firsComponent = var.nameComponents.firstObject; + id defaultValue = [self.valuesFromClient objectForKey:firsComponent]; + id mergedValue = [self.merged objectForKey:firsComponent]; + if (![defaultValue isEqual:mergedValue]) { + id newValue = [ContentMerger mergeWithVars:defaultValue diff:mergedValue]; + [self.merged setObject:newValue forKey:firsComponent]; + + NSMutableString *name = [[NSMutableString alloc] initWithString:firsComponent]; + for (int i = 1; i < var.nameComponents.count; i++) + { + CTVar *existingVar = self.vars[name]; + if (existingVar) { + [existingVar update]; + break; + } + [name appendFormat:@".%@", var.nameComponents[i]]; + } + } +} + - (void)registerVariable:(CTVar *)var { [self.vars setObject:var forKey:var.name]; @@ -161,6 +184,8 @@ - (void)registerVariable:(CTVar *)var nameComponents:var.nameComponents value:var.defaultValue values:self.valuesFromClient]; + + [self mergeVariable:var]; } - (CTVar *)getVariable:(NSString *)name From 4cad64ed44e92dfcda0506fd97c7739b52480a44 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Sun, 12 Mar 2023 21:18:42 +0200 Subject: [PATCH 18/86] Fix tests --- CleverTapSDKTests/CTVarTests.m | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/CleverTapSDKTests/CTVarTests.m b/CleverTapSDKTests/CTVarTests.m index 61ffae37..45b3140f 100644 --- a/CleverTapSDKTests/CTVarTests.m +++ b/CleverTapSDKTests/CTVarTests.m @@ -76,15 +76,7 @@ - (void)testVarCacheSavesDiffs { @"Title": @"TitleUpdated", }; NSString *varsJson = [CTUtils dictionaryToJsonString:updatedVarsFromServer]; - [varCacheMock - applyVariableDiffs:updatedVarsFromServer - messages:nil - variants:nil - localCaps:nil - regions:nil - variantDebugInfo:nil - varsJson:varsJson - varsSignature:nil]; + [varCacheMock applyVariableDiffs:updatedVarsFromServer]; OCMVerify([varCacheMock saveDiffs]); XCTAssertTrue([varCacheMock hasReceivedDiffs]); @@ -93,6 +85,7 @@ - (void)testVarCacheSavesDiffs { NSData *diffsData = [NSData dataWithContentsOfFile:filePath]; NSError *error = nil; + // TODO: fix only available check NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:diffsData error:&error]; XCTAssertNil(error); unarchiver.requiresSecureCoding = NO; @@ -113,15 +106,7 @@ - (void)testVarCacheLoadsDiffs { varName: updatedVarValue, }; NSString *varsJson = [CTUtils dictionaryToJsonString:updatedVarsFromServer]; - [varCacheMock - applyVariableDiffs:updatedVarsFromServer - messages:nil - variants:nil - localCaps:nil - regions:nil - variantDebugInfo:nil - varsJson:varsJson - varsSignature:nil]; + [varCacheMock applyVariableDiffs:updatedVarsFromServer]; OCMVerify([varCacheMock saveDiffs]); XCTAssertTrue([varCacheMock hasReceivedDiffs]); From fac4742212d45e8165d6db694ee4e5ee6d565d3f Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Sun, 12 Mar 2023 22:31:03 +0200 Subject: [PATCH 19/86] Remove unnecessary code --- .../ProductExperiences/CTVar-Internal.h | 12 ++-- CleverTapSDK/ProductExperiences/CTVar.h | 57 ------------------- CleverTapSDK/ProductExperiences/CTVarCache.h | 7 --- CleverTapSDK/ProductExperiences/CTVarCache.m | 2 +- 4 files changed, 6 insertions(+), 72 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVar-Internal.h b/CleverTapSDK/ProductExperiences/CTVar-Internal.h index 14c952c1..8bf5e32e 100644 --- a/CleverTapSDK/ProductExperiences/CTVar-Internal.h +++ b/CleverTapSDK/ProductExperiences/CTVar-Internal.h @@ -16,19 +16,17 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly, strong) NSArray *nameComponents; @property (readonly) BOOL hadStarted; @property (readonly, strong) NSString *kind; -//@property (readonly, strong) NSMutableArray *fileReadyBlocks; @property (readonly, strong) NSMutableArray *valueChangedBlocks; @property (readonly) BOOL fileIsPending; @property (nonatomic, unsafe_unretained, nullable) id delegate; @property (readonly) BOOL hasChanged; -- (void) update; -- (void) cacheComputedValues; -//- (void) triggerFileIsReady; -- (void) triggerValueChanged; +- (void)update; +- (void)cacheComputedValues; +- (void)triggerValueChanged; -+(BOOL)printedCallbackWarning; -+(void)setPrintedCallbackWarning:(BOOL)newPrintedCallbackWarning; ++ (BOOL)printedCallbackWarning; ++ (void)setPrintedCallbackWarning:(BOOL)newPrintedCallbackWarning; @end diff --git a/CleverTapSDK/ProductExperiences/CTVar.h b/CleverTapSDK/ProductExperiences/CTVar.h index c3219d84..b28e5887 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.h +++ b/CleverTapSDK/ProductExperiences/CTVar.h @@ -42,54 +42,6 @@ NS_SWIFT_NAME(Var) */ - (instancetype)init NS_UNAVAILABLE; -//+ (LPVar *)define:(NSString *)name -//NS_SWIFT_NAME(init(name:)); -//+ (LPVar *)define:(NSString *)name withInt:(int)defaultValue -//NS_SWIFT_NAME(init(name:integer:)); -//+ (LPVar *)define:(NSString *)name withFloat:(float)defaultValue -//NS_SWIFT_NAME(init(name:float:)); -//+ (LPVar *)define:(NSString *)name withDouble:(double)defaultValue -//NS_SWIFT_NAME(init(name:double:)); -//+ (LPVar *)define:(NSString *)name withCGFloat:(CGFloat)cgFloatValue -//NS_SWIFT_NAME(init(name:cgFloat:)); -//+ (LPVar *)define:(NSString *)name withShort:(short)defaultValue -//NS_SWIFT_NAME(init(name:integer:)); -//+ (LPVar *)define:(NSString *)name withChar:(char)defaultValue -//NS_SWIFT_NAME(init(name:integer:)); -//+ (LPVar *)define:(NSString *)name withBool:(BOOL)defaultValue -//NS_SWIFT_NAME(init(name:boolean:)); -//+ (LPVar *)define:(NSString *)name withString:(nullable NSString *)defaultValue -//NS_SWIFT_NAME(init(name:string:)); -//+ (LPVar *)define:(NSString *)name withNumber:(nullable NSNumber *)defaultValue -//NS_SWIFT_NAME(init(name:number:)); -//+ (LPVar *)define:(NSString *)name withInteger:(NSInteger)defaultValue -//NS_SWIFT_NAME(init(name:integer:)); -//+ (LPVar *)define:(NSString *)name withLong:(long)defaultValue -//NS_SWIFT_NAME(init(name:integer:)); -//+ (LPVar *)define:(NSString *)name withLongLong:(long long)defaultValue -//NS_SWIFT_NAME(init(name:integer:)); -//+ (LPVar *)define:(NSString *)name withUnsignedChar:(unsigned char)defaultValue -//NS_SWIFT_NAME(init(name:uinteger:)); -//+ (LPVar *)define:(NSString *)name withUnsignedInt:(unsigned int)defaultValue -//NS_SWIFT_NAME(init(name:uinteger:)); -//+ (LPVar *)define:(NSString *)name withUnsignedInteger:(NSUInteger)defaultValue -//NS_SWIFT_NAME(init(name:uinteger:)); -//+ (LPVar *)define:(NSString *)name withUnsignedLong:(unsigned long)defaultValue -//NS_SWIFT_NAME(init(name:uinteger:)); -//+ (LPVar *)define:(NSString *)name withUnsignedLongLong:(unsigned long long)defaultValue -//NS_SWIFT_NAME(init(name:uinteger:)); -//+ (LPVar *)define:(NSString *)name withUnsignedShort:(unsigned short)defaultValue -//NS_SWIFT_NAME(init(name:uinteger:)); -//+ (LPVar *)define:(NSString *)name withFile:(nullable NSString *)defaultFilename -//NS_SWIFT_NAME(init(name:file:)); -//+ (LPVar *)define:(NSString *)name withDictionary:(nullable NSDictionary *)defaultValue -//NS_SWIFT_NAME(init(name:dictionary:)); -//+ (LPVar *)define:(NSString *)name withArray:(nullable NSArray *)defaultValue -//NS_SWIFT_NAME(init(name:array:)); -//+ (LPVar *)define:(NSString *)name withColor:(nullable UIColor *)defaultValue -//NS_SWIFT_NAME(init(name:color:)); -/**@}*/ - /** * Returns the name of the variable. */ @@ -115,11 +67,6 @@ NS_SWIFT_NAME(Var) */ - (BOOL)hasChanged; -/** - * For file variables, called when the file is ready. - */ -//- (void)onFileReady:(CleverTapVariablesChangedBlock)block; - /** * Called when the value of the variable changes. */ @@ -139,12 +86,9 @@ NS_SWIFT_NAME(Var) - (id)objectAtIndex:(NSUInteger )index; - (id)objectForKeyPath:(nullable id)firstComponent, ... NS_REQUIRES_NIL_TERMINATION; - (id)objectForKeyPathComponents:(nullable NSArray *)pathComponents; -//- (NSUInteger)count; - (nullable NSNumber *)numberValue; - (nullable NSString *)stringValue; -//- (nullable NSString *)fileValue; -//- (nullable UIImage *)imageValue; - (int)intValue; - (double)doubleValue; - (CGFloat)cgFloatValue; @@ -161,7 +105,6 @@ NS_SWIFT_NAME(Var) - (NSUInteger)unsignedIntegerValue; - (unsigned long)unsignedLongValue; - (unsigned long long)unsignedLongLongValue; -//- (nullable UIColor *)colorValue; /**@}*/ @end diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.h b/CleverTapSDK/ProductExperiences/CTVarCache.h index a88f5931..b0c6dabb 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.h +++ b/CleverTapSDK/ProductExperiences/CTVarCache.h @@ -1,13 +1,10 @@ #import -//#import "LPSecuredVars.h" #import "CTVar-Internal.h" #import "CleverTapInstanceConfig.h" #import "CTDeviceInfo.h" NS_ASSUME_NONNULL_BEGIN -//@class LPVar; - typedef void (^CacheUpdateBlock)(void); typedef void (^RegionInitBlock)(NSDictionary *, NSSet *, NSSet *); @@ -15,10 +12,6 @@ NS_SWIFT_NAME(VarCache) @interface CTVarCache : NSObject - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo; -//NS_UNAVAILABLE; - -//+(instancetype)sharedCache -//NS_SWIFT_NAME(shared()); // Handling variables. - (CTVar *)define:(NSString *)name diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index f0c22b8f..36dd1531 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -153,7 +153,7 @@ - (void)updateValues:(NSString *)name } } -// Use this method to merge default variable value with VarCache.merged value +// Merge default variable value with VarCache.merged value // This is neccessary if variable was registered after VarCache.applyVariableDiffs - (void)mergeVariable:(CTVar * _Nonnull)var { NSString *firsComponent = var.nameComponents.firstObject; From f98630ce939f328a92c231307ee27409e4122d2f Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 13 Mar 2023 00:43:20 +0200 Subject: [PATCH 20/86] Split implementation into CTVariables and CTVarCache --- CleverTapSDK.xcodeproj/project.pbxproj | 12 ++ CleverTapSDK/CleverTap.m | 192 ++++------------- CleverTapSDK/ProductExperiences/CTVarCache.h | 8 - CleverTapSDK/ProductExperiences/CTVarCache.m | 25 --- CleverTapSDK/ProductExperiences/CTVariables.h | 37 ++++ CleverTapSDK/ProductExperiences/CTVariables.m | 198 ++++++++++++++++++ CleverTapSDKTests/CTVarTests.m | 56 ++--- 7 files changed, 318 insertions(+), 210 deletions(-) create mode 100644 CleverTapSDK/ProductExperiences/CTVariables.h create mode 100644 CleverTapSDK/ProductExperiences/CTVariables.m diff --git a/CleverTapSDK.xcodeproj/project.pbxproj b/CleverTapSDK.xcodeproj/project.pbxproj index 8c3a2d5b..6b632ac2 100644 --- a/CleverTapSDK.xcodeproj/project.pbxproj +++ b/CleverTapSDK.xcodeproj/project.pbxproj @@ -250,6 +250,10 @@ 583D9573DCDA5D219016B990 /* libPods-shared-CleverTapSDKTestsApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2108AFE9090417CC69A0E3EB /* libPods-shared-CleverTapSDKTestsApp.a */; }; 63359A5791C468263B934E22 /* libPods-shared-CleverTapSDKTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 917B24847F7C2A8B0825AEC1 /* libPods-shared-CleverTapSDKTests.a */; }; 6A2E4C18291E8A4A00385536 /* CleverTapInstanceConfigTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */; }; + 6A775C3329BE78C7007790E0 /* CTVariables.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A775C3129BE78C7007790E0 /* CTVariables.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 6A775C3429BE78C7007790E0 /* CTVariables.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A775C3129BE78C7007790E0 /* CTVariables.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 6A775C3529BE78C7007790E0 /* CTVariables.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A775C3229BE78C7007790E0 /* CTVariables.m */; }; + 6A775C3629BE78C7007790E0 /* CTVariables.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A775C3229BE78C7007790E0 /* CTVariables.m */; }; D0047B0A2098D45B0019C6FD /* CTLocalDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = D0047B082098D45B0019C6FD /* CTLocalDataStore.h */; settings = {ATTRIBUTES = (Private, ); }; }; D0047B0B2098D45B0019C6FD /* CTLocalDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = D0047B092098D45B0019C6FD /* CTLocalDataStore.m */; }; D0047B0E2098E2F00019C6FD /* CTProfileBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = D0047B0C2098E2F00019C6FD /* CTProfileBuilder.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -642,6 +646,8 @@ 5709005227FD8E1E0011B89F /* CleverTap+SCDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CleverTap+SCDomain.h"; sourceTree = ""; }; 57EDC7A02683845B001DD157 /* CleverTap+InAppNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CleverTap+InAppNotifications.h"; sourceTree = ""; }; 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CleverTapInstanceConfigTests.m; sourceTree = ""; }; + 6A775C3129BE78C7007790E0 /* CTVariables.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTVariables.h; sourceTree = ""; }; + 6A775C3229BE78C7007790E0 /* CTVariables.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVariables.m; sourceTree = ""; }; 840B38DDFBBEAE05FC6C5458 /* Pods-shared-CleverTapSDKTestsApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-shared-CleverTapSDKTestsApp.release.xcconfig"; path = "Target Support Files/Pods-shared-CleverTapSDKTestsApp/Pods-shared-CleverTapSDKTestsApp.release.xcconfig"; sourceTree = ""; }; 917B24847F7C2A8B0825AEC1 /* libPods-shared-CleverTapSDKTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-shared-CleverTapSDKTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; A1765572FACEA4B081A89F82 /* Pods-shared-CleverTapSDKTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-shared-CleverTapSDKTests.debug.xcconfig"; path = "Target Support Files/Pods-shared-CleverTapSDKTests/Pods-shared-CleverTapSDKTests.debug.xcconfig"; sourceTree = ""; }; @@ -1042,6 +1048,8 @@ 4E41FD91294F46510001FBED /* CTVarCache.m */, 4E838C3E299F419800ED0875 /* ContentMerger.h */, 4E838C3F299F419900ED0875 /* ContentMerger.m */, + 6A775C3129BE78C7007790E0 /* CTVariables.h */, + 6A775C3229BE78C7007790E0 /* CTVariables.m */, ); path = ProductExperiences; sourceTree = ""; @@ -1354,6 +1362,7 @@ D0A9E9CD20EEA8630004BC6F /* CTLocationManager.h in Headers */, 4EA64A27296C115E001D9B22 /* CTRequestFactory.h in Headers */, D014B8F420E2FACA001E0780 /* CTSwizzle.h in Headers */, + 6A775C3429BE78C7007790E0 /* CTVariables.h in Headers */, D014B8E620E2FA64001E0780 /* CleverTapInstanceConfig.h in Headers */, 4E41FD99294F46510001FBED /* CTVarCache.h in Headers */, D014B8F220E2FABC001E0780 /* CTDeviceInfo.h in Headers */, @@ -1415,6 +1424,7 @@ 071EB515217F6427008F0FAB /* CTInterstitialViewController.h in Headers */, 0701E9622372C1950034AAC2 /* CTDisplayUnitController.h in Headers */, 4E7929F929799E8F00B81F3C /* CTDomainFactory.h in Headers */, + 6A775C3329BE78C7007790E0 /* CTVariables.h in Headers */, 0797132F21A2F09A0011C9A3 /* CTSwipeView.h in Headers */, D0BD75A02417690E0006EE55 /* CleverTapProductConfigPrivate.h in Headers */, D0D4C9EB2414D1C50029477E /* CTFeatureFlagsController.h in Headers */, @@ -1806,6 +1816,7 @@ 07BF4661217F80C1002E166D /* CTUIUtils.m in Sources */, 07BF465C217F7C4B002E166D /* CTInAppDisplayViewController.m in Sources */, 071EB51B217F6568008F0FAB /* CTInAppNotification.m in Sources */, + 6A775C3629BE78C7007790E0 /* CTVariables.m in Sources */, D0A9E9CE20EEA8770004BC6F /* CTLocationManager.m in Sources */, 4E838C43299F419900ED0875 /* ContentMerger.m in Sources */, 4E7929FC29799E8F00B81F3C /* CTDomainFactory.m in Sources */, @@ -1941,6 +1952,7 @@ 071EB4D4217F6427008F0FAB /* CTBaseHeaderFooterViewController.m in Sources */, D06F052921E802D400D1B6BD /* CTInboxBaseMessageCell.m in Sources */, 07B9453F219EA34300D4C542 /* CleverTapInboxStyleConfig.m in Sources */, + 6A775C3529BE78C7007790E0 /* CTVariables.m in Sources */, 4E7929FB29799E8F00B81F3C /* CTDomainFactory.m in Sources */, 0796FB6421AE5B2900FC380D /* CTCarouselMessageCell.m in Sources */, 071EB4F6217F6427008F0FAB /* CTUIUtils.m in Sources */, diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 99e382fb..5b655b29 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -73,12 +73,14 @@ #import "CTProductConfigController.h" #import "CTVarCache.h" +#import "CTVariables.h" +#import "CleverTap+CTVar.h" + #import "CTRequestFactory.h" #import "CTRequestSender.h" #import "CTDomainFactory.h" -#import "CleverTap+CTVar.h" - #import "CleverTap+SCDomain.h" + #import static const void *const kQueueKey = &kQueueKey; @@ -256,8 +258,7 @@ @interface CleverTap () { @property (atomic, assign) BOOL geofenceLocation; @property (nonatomic, strong) NSString *gfSDKVersion; -@property (nonatomic, strong) CTVarCache *varCache; -@property(strong, nonatomic) NSMutableArray *variablesChangedBlocks; +@property (nonatomic, strong) CTVariables *variables; @property(strong, nonatomic) CleverTapForceContentUpdateBlock forceContentUpdateBlock; - (instancetype)init __unavailable; @@ -694,10 +695,11 @@ - (instancetype)initWithConfig:(CleverTapInstanceConfig*)config andCleverTapID:( [self _initProductConfig]; - self.varCache = [[CTVarCache alloc]initWithConfig:self.config deviceInfo:self.deviceInfo]; - + + self.variables = [[CTVariables alloc] initWithConfig:self.config deviceInfo:self.deviceInfo]; + // TODO: check listeners here are needed // ADD PE VAR CHANGED LISTENERS - [self addVarListeners]; + [[self variables] addVarListeners]; [self notifyUserProfileInitialized]; } @@ -1340,9 +1342,9 @@ - (void)recordAppLaunched:(NSString *)caller { } // LOAD VARS FROM CACHE BEFORE APP LAUNCHED - [self.varCache setSilent:YES]; - [self.varCache loadDiffs]; - [self.varCache setSilent:NO]; + [self.variables.varCache setSilent:YES]; + [self.variables.varCache loadDiffs]; + [self.variables.varCache setSilent:NO]; self.appLaunchProcessed = YES; @@ -3027,31 +3029,10 @@ - (void)parseResponse:(NSData *)responseData { } #endif - // HANDLE AND CACHE PE VARS - NSDictionary *peVarsJSON = jsonResp[CLTAP_PE_VARS_RESPONSE_KEY]; - - // TODO: REMOVE THIS STATIC JSON WHEN API IS ONLINE - peVarsJSON = @{ - @"Title.Text": @"TitleUpdated", - @"MyMap.name": @"nikoUpdated", - @"MyMap.phone": @"123Updated" - }; - NSString *varsJson = [CTUtils dictionaryToJsonString:peVarsJSON]; - - if (peVarsJSON) { - self.varCache.appLaunchedRecorded = YES; - [self.varCache - applyVariableDiffs:peVarsJSON - messages:nil - variants:nil - localCaps:nil - regions:nil - variantDebugInfo:nil - varsJson:varsJson - varsSignature:nil]; - if (_forceContentUpdateBlock) { - _forceContentUpdateBlock(YES); - } + // Handle and Cache PE Variables + [[self variables] handleVariablesResponse: jsonResp[CLTAP_PE_VARS_RESPONSE_KEY]]; + if (self->_forceContentUpdateBlock) { + self->_forceContentUpdateBlock(YES); } // Handle events/profiles sync data @@ -4731,8 +4712,7 @@ - (void)_resetProductConfig { // run off main - (void)_resetVars { if (self.config && self.deviceInfo.deviceId) { - self.varCache = [[CTVarCache alloc]initWithConfig:self.config deviceInfo:self.deviceInfo]; - self.variablesChangedBlocks = [NSMutableArray array]; + self.variables = [[CTVariables alloc]initWithConfig:self.config deviceInfo:self.deviceInfo]; _forceContentUpdateBlock = nil; } } @@ -5063,34 +5043,8 @@ + (BOOL)isValidCleverTapId:(NSString *_Nullable)cleverTapID { #pragma mark - Product Experiences -- (void)addVarListeners { - [self.varCache onUpdate:^{ - [self triggerVariablesChanged]; - -// if ([LPFileTransferManager sharedInstance].numPendingDownloads == 0) { -// [self triggerVariablesChangedAndNoDownloadsPending]; -// } - }]; -} - - - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { - - if (!block) { - CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onValueChanged]."); - return; - } - - CT_TRY - if (!self.variablesChangedBlocks) { - self.variablesChangedBlocks = [NSMutableArray array]; - } - [self.variablesChangedBlocks addObject:[block copy]]; - CT_END_TRY - - if ([self.varCache hasReceivedDiffs]) { - block(); - } + [[self variables] onVariablesChanged:block]; } - (void)syncVariables { @@ -5125,7 +5079,7 @@ - (void)_syncVars { // META NSDictionary *meta = [self batchHeader]; // VARSPAYLOAD - NSDictionary *varsPayload = [self varsPayload]; + NSDictionary *varsPayload = [[self variables] varsPayload]; NSArray *payload = @[meta,varsPayload]; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); @@ -5160,206 +5114,138 @@ - (void)_syncVars { dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); } -- (NSDictionary*)varsPayload { - NSMutableDictionary *result = [NSMutableDictionary dictionary]; - result[@"type"] = @"varsPayload"; - - NSMutableDictionary *allVars = [NSMutableDictionary dictionary]; - - [self.varCache.vars - enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, CTVar * _Nonnull varValue, BOOL * _Nonnull stop) { - - NSMutableDictionary *varData = [NSMutableDictionary dictionary]; - - if ([varValue.defaultValue isKindOfClass:[NSDictionary class]]) { - NSDictionary *flattenedMap = [self flatten:varValue.defaultValue varName:varValue.name]; - [allVars addEntriesFromDictionary:flattenedMap]; - } - else { - if ([varValue.kind isEqualToString:CT_KIND_INT] || [varValue.kind isEqualToString:CT_KIND_FLOAT]) { - varData[@"type"] = @"number"; - } - else if ([varValue.kind isEqualToString:CT_KIND_BOOLEAN]) { - varData[@"type"] = @"boolean"; - } - else { - varData[@"type"] = varValue.kind; - } - varData[@"defaultValue"] = varValue.defaultValue; - allVars[key] = varData; - } - }]; - result[@"vars"] = allVars; - - return result; -} - -- (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName { - NSMutableDictionary *varsPayload = [NSMutableDictionary dictionary]; - - [map enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { - - if ([value isKindOfClass:[NSString class]] || - [value isKindOfClass:[NSNumber class]]) { - NSString *payloadKey = [NSString stringWithFormat:@"%@.%@",varName,key]; - varsPayload[payloadKey] = @{@"defaultValue": value}; - } - else if ([value isKindOfClass:[NSDictionary class]]) { - NSString *payloadKey = [NSString stringWithFormat:@"%@.%@",varName,key]; - - NSDictionary* flattenedMap = [self flatten:value varName:payloadKey]; - [varsPayload addEntriesFromDictionary:flattenedMap]; - } - }]; - - return varsPayload; -} - - (void)forceContentUpdateWithBlock:(CleverTapForceContentUpdateBlock)block { _forceContentUpdateBlock = block; [self queueEvent:@{@"evtName": CLTAP_WZRK_FETCH_EVENT, @"evtData" : @{@"t": @4}} withType:CleverTapEventTypeFetch]; } -- (void)triggerVariablesChanged -{ - // TODO: CHECK IF THIS IS NEEDED -// for (NSInvocation *invocation in [LPInternalState sharedState] -// .variablesChangedResponders.copy) { -// [invocation invoke]; -// } - - for (CleverTapVariablesChangedBlock block in self.variablesChangedBlocks.copy) { - block(); - } -} - #pragma mark - PE Vars - (CTVar *)defineVar:(NSString *)name { - return [self.varCache define:name with:nil kind:nil]; + return [self.variables define:name with:nil kind:nil]; } - (CTVar *)defineVar:(NSString *)name withInt:(int)defaultValue { - return [self.varCache define:name with:[NSNumber numberWithInt:defaultValue] kind:CT_KIND_INT]; + return [self.variables define:name with:[NSNumber numberWithInt:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withFloat:(float)defaultValue { - return [self.varCache define:name with:[NSNumber numberWithFloat:defaultValue] kind:CT_KIND_FLOAT]; + return [self.variables define:name with:[NSNumber numberWithFloat:defaultValue] kind:CT_KIND_FLOAT]; } - (CTVar *)defineVar:(NSString *)name withDouble:(double)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithDouble:defaultValue] kind:CT_KIND_FLOAT]; } - (CTVar *)defineVar:(NSString *)name withCGFloat:(CGFloat)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithDouble:defaultValue] kind:CT_KIND_FLOAT]; } - (CTVar *)defineVar:(NSString *)name withShort:(short)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithShort:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withChar:(char)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithChar:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withBool:(BOOL)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithBool:defaultValue] kind:CT_KIND_BOOLEAN]; } - (CTVar *)defineVar:(NSString *)name withInteger:(NSInteger)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithInteger:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withLong:(long)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithLong:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withLongLong:(long long)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithLongLong:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withUnsignedChar:(unsigned char)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithUnsignedChar:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withUnsignedInt:(unsigned int)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithUnsignedInt:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withUnsignedInteger:(NSUInteger)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithUnsignedInteger:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withUnsignedLong:(unsigned long)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithUnsignedLong:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withUnsignedLongLong:(unsigned long long)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithUnsignedLongLong:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withUnsignedShort:(unsigned short)defaultValue { - return [self.varCache define:name + return [self.variables define:name with:[NSNumber numberWithUnsignedShort:defaultValue] kind:CT_KIND_INT]; } - (CTVar *)defineVar:(NSString *)name withString:(NSString *)defaultValue { - return [self.varCache define:name with:defaultValue kind:CT_KIND_STRING]; + return [self.variables define:name with:defaultValue kind:CT_KIND_STRING]; } - (CTVar *)defineVar:(NSString *)name withNumber:(NSNumber *)defaultValue { - return [self.varCache define:name with:defaultValue kind:CT_KIND_FLOAT]; + return [self.variables define:name with:defaultValue kind:CT_KIND_FLOAT]; } - (CTVar *)defineVar:(NSString *)name withDictionary:(NSDictionary *)defaultValue { - return [self.varCache define:name with:defaultValue kind:CT_KIND_DICTIONARY]; + return [self.variables define:name with:defaultValue kind:CT_KIND_DICTIONARY]; } @end diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.h b/CleverTapSDK/ProductExperiences/CTVarCache.h index b0c6dabb..35f19427 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.h +++ b/CleverTapSDK/ProductExperiences/CTVarCache.h @@ -6,7 +6,6 @@ NS_ASSUME_NONNULL_BEGIN typedef void (^CacheUpdateBlock)(void); -typedef void (^RegionInitBlock)(NSDictionary *, NSSet *, NSSet *); NS_SWIFT_NAME(VarCache) @interface CTVarCache : NSObject @@ -14,11 +13,6 @@ NS_SWIFT_NAME(VarCache) - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo; // Handling variables. -- (CTVar *)define:(NSString *)name - with:(nullable NSObject *)defaultValue - kind:(nullable NSString *)kind -NS_SWIFT_NAME(define(name:value:kind:)); - - (NSArray *)getNameComponents:(NSString *)name; - (void)loadDiffs; - (void)saveDiffs; @@ -36,8 +30,6 @@ NS_SWIFT_NAME(define(name:value:kind:)); - (void)setSilent:(BOOL)silent; - (BOOL)silent; -//- (void)clearUserContent; - @property (strong, nonatomic) NSMutableDictionary *vars; @property (assign, nonatomic) BOOL appLaunchedRecorded; diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 36dd1531..06364d5f 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -14,7 +14,6 @@ @interface CTVarCache() @property (assign, nonatomic) BOOL hasReceivedDiffs; @property (assign, nonatomic) BOOL silent; -@property (strong, nonatomic) RegionInitBlock regionInitBlock; @property (nonatomic, strong) CleverTapInstanceConfig *config; @property (nonatomic, strong) CTDeviceInfo *deviceInfo; @@ -43,30 +42,6 @@ - (void)initialize options:NSRegularExpressionCaseInsensitive error:&error]; } -- (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString *)kind -{ - if ([CTUtils isNullOrEmpty:name]) { - CleverTapLogDebug(_config.logLevel, @"%@: Empty name provided as parameter while defining a variable.", self); - return nil; - } - - @synchronized (self.vars) { - CT_TRY - CTVar *existing = [self getVariable:name]; - if (existing) { - CleverTapLogInfo(self.config.logLevel, @"%@: Variable with name: %@ already exists.", self, name); - return existing; - } - CT_END_TRY - CTVar *var = [[CTVar alloc] initWithName:name - withComponents:[self getNameComponents:name] - withDefaultValue:defaultValue - withKind:kind - varCache:self]; - return var; - } -} - - (NSArray *)arrayOfCaptureComponentsOfString:(NSString *)data matchedBy:(NSRegularExpression *)regExpression { NSMutableArray *test = [NSMutableArray array]; diff --git a/CleverTapSDK/ProductExperiences/CTVariables.h b/CleverTapSDK/ProductExperiences/CTVariables.h new file mode 100644 index 00000000..7f55cdab --- /dev/null +++ b/CleverTapSDK/ProductExperiences/CTVariables.h @@ -0,0 +1,37 @@ +// +// CTVariables.h +// CleverTapSDK +// +// Created by Nikola Zagorchev on 12.03.23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import "CTVarCache.h" +#import "CleverTapInstanceConfig.h" +#import "CTDeviceInfo.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface CTVariables : NSObject + +@property(strong, nonatomic) NSMutableArray *variablesChangedBlocks; +@property(strong, nonatomic) CTVarCache *varCache; + +- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo; + +- (CTVar *)define:(NSString *)name + with:(nullable NSObject *)defaultValue + kind:(nullable NSString *)kind +NS_SWIFT_NAME(define(name:value:kind:)); + +- (CTVar *)getVariable:(NSString *)name; +- (void)handleVariablesResponse:(NSDictionary *)varsResponse; +- (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; +- (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName; +- (NSDictionary*)varsPayload; +- (void)addVarListeners; + +@end + +NS_ASSUME_NONNULL_END diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m new file mode 100644 index 00000000..88577774 --- /dev/null +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -0,0 +1,198 @@ +// +// CTVariables.m +// CleverTapSDK +// +// Created by Nikola Zagorchev on 12.03.23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import "CTVariables.h" +#import "CTConstants.h" +#import "CTUtils.h" +//#import "CTVarCache.h" + +@interface CTVariables() +@property (nonatomic, strong) CleverTapInstanceConfig *config; +@property (nonatomic, strong) CTDeviceInfo *deviceInfo; +@end + +@implementation CTVariables + +- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo { + if ((self = [super init])) { + self.varCache = [[CTVarCache alloc]initWithConfig:config deviceInfo:deviceInfo]; + } + return self; +} + +- (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString *)kind +{ + if ([CTUtils isNullOrEmpty:name]) { + CleverTapLogDebug(_config.logLevel, @"%@: Empty name provided as parameter while defining a variable.", self); + return nil; + } + + @synchronized (self.varCache.vars) { + CT_TRY + CTVar *existing = [self.varCache getVariable:name]; + if (existing) { + return existing; + } + CT_END_TRY + CTVar *var = [[CTVar alloc] initWithName:name + withComponents:[self.varCache getNameComponents:name] + withDefaultValue:defaultValue + withKind:kind + varCache:self.varCache]; + return var; + } +} + +- (CTVar *)getVariable:(NSString *)name +{ + CTVar *var = [self.varCache getVariable:name]; + if (!var) { + CleverTapLogDebug(self.config.logLevel, @"%@: Variable with name: %@ not found.", self, name); + } + return var; +} + +- (void)handleVariablesResponse:(NSDictionary *)varsResponse +{ + if (varsResponse) { + [[self varCache] setAppLaunchedRecorded:YES]; + [[self varCache] applyVariableDiffs:varsResponse]; + } +} + +- (void)addVarListeners { + [self.varCache onUpdate:^{ + [self triggerVariablesChanged]; + }]; +} + +- (void)triggerVariablesChanged +{ + for (CleverTapVariablesChangedBlock block in self.variablesChangedBlocks.copy) { + block(); + } +} + +- (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { + + if (!block) { + CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onValueChanged]."); + return; + } + + CT_TRY + if (!self.variablesChangedBlocks) { + self.variablesChangedBlocks = [NSMutableArray array]; + } + [self.variablesChangedBlocks addObject:[block copy]]; + CT_END_TRY + + if ([self.varCache hasReceivedDiffs]) { + block(); + } +} + +- (NSDictionary*)varsPayload { + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + result[@"type"] = @"varsPayload"; + + NSMutableDictionary *allVars = [NSMutableDictionary dictionary]; + + [self.varCache.vars + enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, CTVar * _Nonnull varValue, BOOL * _Nonnull stop) { + + NSMutableDictionary *varData = [NSMutableDictionary dictionary]; + + if ([varValue.defaultValue isKindOfClass:[NSDictionary class]]) { + NSDictionary *flattenedMap = [self flatten:varValue.defaultValue varName:varValue.name]; + [allVars addEntriesFromDictionary:flattenedMap]; + } + else { + if ([varValue.kind isEqualToString:CT_KIND_INT] || [varValue.kind isEqualToString:CT_KIND_FLOAT]) { + varData[@"type"] = @"number"; + } + else if ([varValue.kind isEqualToString:CT_KIND_BOOLEAN]) { + varData[@"type"] = @"boolean"; + } + else { + varData[@"type"] = varValue.kind; + } + varData[@"defaultValue"] = varValue.defaultValue; + allVars[key] = varData; + } + }]; + result[@"vars"] = allVars; + + return result; +} + +- (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName { + NSMutableDictionary *varsPayload = [NSMutableDictionary dictionary]; + + [map enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { + + if ([value isKindOfClass:[NSString class]] || + [value isKindOfClass:[NSNumber class]]) { + NSString *payloadKey = [NSString stringWithFormat:@"%@.%@",varName,key]; + varsPayload[payloadKey] = @{@"defaultValue": value}; + } + else if ([value isKindOfClass:[NSDictionary class]]) { + NSString *payloadKey = [NSString stringWithFormat:@"%@.%@",varName,key]; + + NSDictionary* flattenedMap = [self flatten:value varName:payloadKey]; + [varsPayload addEntriesFromDictionary:flattenedMap]; + } + }]; + + return varsPayload; +} + + + +//- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { +// +// if (!block) { +// CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onValueChanged]."); +// return; +// } +// +// CT_TRY +// if (!self.variablesChangedBlocks) { +// self.variablesChangedBlocks = [NSMutableArray array]; +// } +// [self.variablesChangedBlocks addObject:[block copy]]; +// CT_END_TRY +// +// if ([self.varCache hasReceivedDiffs]) { +// block(); +// } +//} +//+ (void)onceVariablesChangedAndNoDownloadsPending:(LeanplumVariablesChangedBlock)block +//{ +// if (!block) { +// [self throwError:@"[Leanplum onceVariablesChangedAndNoDownloadsPending:] Nil block " +// @"parameter provided."]; +// return; +// } +// +// if ([[LPVarCache sharedCache] hasReceivedDiffs] && [LPFileTransferManager sharedInstance].numPendingDownloads == 0) { +// block(); +// } else { +// LP_TRY +// static dispatch_once_t onceNoDownloadsBlocksToken; +// dispatch_once(&onceNoDownloadsBlocksToken, ^{ +// [LPInternalState sharedState].onceNoDownloadsBlocks = [NSMutableArray array]; +// }); +// @synchronized ([LPInternalState sharedState].onceNoDownloadsBlocks) { +// [[LPInternalState sharedState].onceNoDownloadsBlocks addObject:[block copy]]; +// } +// LP_END_TRY +// } +//} + +@end diff --git a/CleverTapSDKTests/CTVarTests.m b/CleverTapSDKTests/CTVarTests.m index 45b3140f..2d6bd4d0 100644 --- a/CleverTapSDKTests/CTVarTests.m +++ b/CleverTapSDKTests/CTVarTests.m @@ -7,6 +7,7 @@ // #import +#import "CTVariables.h" #import "CTVarCache.h" #import #import "CTUtils.h" @@ -21,30 +22,30 @@ @interface CTVarTests : XCTestCase @end -CTVarCache *varCache; +CTVariables *variables; @implementation CTVarTests - (void)setUp { CleverTapInstanceConfig *config = [[CleverTapInstanceConfig alloc] initWithAccountId:@"id" accountToken:@"token" accountRegion:@"eu"]; config.useCustomCleverTapId = YES; - CTDeviceInfo *deviceInfo = [[CTDeviceInfo alloc]initWithConfig:config andCleverTapID:@"test"]; - varCache = [[CTVarCache alloc]initWithConfig:config deviceInfo:deviceInfo]; + CTDeviceInfo *deviceInfo = [[CTDeviceInfo alloc] initWithConfig:config andCleverTapID:@"test"]; + variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo]; } - (void)tearDown { - varCache = nil; + variables = nil; } - (void)testVarCacheNotNil { - XCTAssertNotNil(varCache); + XCTAssertNotNil(variables); } - (void)testVarCacheFetchesNameComponents { NSString *component1 = @"Primary"; NSString *component2 = @"Secondary"; NSString *component3 = @"Tertiary"; - NSArray *nameComponents = [varCache getNameComponents:[NSString stringWithFormat:@"%@.%@.%@",component1,component2,component3]]; + NSArray *nameComponents = [[variables varCache] getNameComponents:[NSString stringWithFormat:@"%@.%@.%@",component1,component2,component3]]; XCTAssertNotNil(nameComponents); BOOL expression = [nameComponents containsObject:component1] && [nameComponents containsObject:component2] && [nameComponents containsObject:component3]; @@ -54,28 +55,28 @@ - (void)testVarCacheFetchesNameComponents { } - (void)testVarCacheResgitersVars { - CTVar *varMock = OCMPartialMock([varCache define:@"test" with:@"test" kind:CT_KIND_STRING]); - [varCache registerVariable:varMock]; + CTVar *varMock = OCMPartialMock([variables define:@"test" with:@"test" kind:CT_KIND_STRING]); + [variables.varCache registerVariable:varMock]; - XCTAssertEqual(varCache.vars[varMock.name], varMock); + XCTAssertEqual(variables.varCache.vars[varMock.name], varMock); } - (void)testVarCacheGetVarsForName { NSString *varName = @"test"; - CTVar *var = [varCache define:varName with:@"test" kind:CT_KIND_STRING]; - CTVar *varResult = [varCache getVariable:varName]; - + CTVar *var = [variables define:varName with:@"test" kind:CT_KIND_STRING]; + CTVar *varResult = [variables getVariable:varName]; + XCTAssertEqual(varResult, var); } - (void)testVarCacheSavesDiffs { - id varCacheMock = OCMPartialMock(varCache); - [varCacheMock define:@"Title" with:@"Hello" kind:CT_KIND_STRING]; + id variablesMock = OCMPartialMock(variables); + [variablesMock define:@"Title" with:@"Hello" kind:CT_KIND_STRING]; NSDictionary *updatedVarsFromServer = @{ @"Title": @"TitleUpdated", }; - NSString *varsJson = [CTUtils dictionaryToJsonString:updatedVarsFromServer]; + id varCacheMock = OCMPartialMock(variables.varCache); [varCacheMock applyVariableDiffs:updatedVarsFromServer]; OCMVerify([varCacheMock saveDiffs]); XCTAssertTrue([varCacheMock hasReceivedDiffs]); @@ -83,12 +84,18 @@ - (void)testVarCacheSavesDiffs { NSString *fileName = [varCacheMock getArchiveFileName]; NSString *filePath = [CTPreferences filePathfromFileName:fileName]; NSData *diffsData = [NSData dataWithContentsOfFile:filePath]; + NSKeyedUnarchiver *unarchiver; NSError *error = nil; - - // TODO: fix only available check - NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:diffsData error:&error]; - XCTAssertNil(error); - unarchiver.requiresSecureCoding = NO; + if (@available(iOS 12.0, *)) { + unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:diffsData error:&error]; + XCTAssertNil(error); + unarchiver.requiresSecureCoding = NO; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:diffsData]; +#pragma clang diagnostic pop + } NSDictionary *loadedVars = (NSDictionary *) [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; XCTAssertTrue([updatedVarsFromServer isEqualToDictionary:loadedVars]); @@ -99,8 +106,9 @@ - (void)testVarCacheLoadsDiffs { NSString *varName = @"Title"; NSString *initialVarValue = @"Hello"; NSString *updatedVarValue = @"TitleUpdated"; - CTVarCache *varCacheMock = OCMPartialMock(varCache); - [varCacheMock define:varName with:initialVarValue kind:CT_KIND_STRING]; + CTVariables *variablesMock = OCMPartialMock(variables); + CTVarCache *varCacheMock = OCMPartialMock(variables.varCache); + [variablesMock define:varName with:initialVarValue kind:CT_KIND_STRING]; NSDictionary *updatedVarsFromServer = @{ varName: updatedVarValue, @@ -123,7 +131,7 @@ - (void)testVarCacheLoadsDiffs { - (void)testVarValues { NSNumber *varValue = [NSNumber numberWithDouble:6.67345983745897]; - CTVar *var = [varCache define:@"MyNumber" with:varValue kind:CT_KIND_FLOAT]; + CTVar *var = [variables define:@"MyNumber" with:varValue kind:CT_KIND_FLOAT]; XCTAssertEqualObjects(var.stringValue,varValue.stringValue); XCTAssertEqual(var.floatValue,varValue.floatValue); @@ -141,7 +149,7 @@ - (void)testVarValues { XCTAssertEqual(var.unsignedLongLongValue,varValue.unsignedLongLongValue); XCTAssertEqual(var.cgFloatValue,varValue.doubleValue); - CTVar *mapVar = [varCache define:@"MyMap" with:@{@"MyMapNumber":varValue} kind:CT_KIND_DICTIONARY]; + CTVar *mapVar = [variables define:@"MyMap" with:@{@"MyMapNumber":varValue} kind:CT_KIND_DICTIONARY]; XCTAssertTrue([[mapVar objectForKey:@"MyMapNumber"]isKindOfClass:[varValue class]]); XCTAssertTrue([mapVar.value isKindOfClass:[NSDictionary class]]); XCTAssertTrue([mapVar.defaultValue isKindOfClass:[NSDictionary class]]); From 360b49cab95bc19d28190257ac908abc9f39b71e Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 13 Mar 2023 00:49:02 +0200 Subject: [PATCH 21/86] Add onceVariablesChanged --- CleverTapSDK/CleverTap.h | 2 + CleverTapSDK/CleverTap.m | 4 ++ CleverTapSDK/ProductExperiences/CTVariables.h | 2 +- CleverTapSDK/ProductExperiences/CTVariables.m | 68 +++++++------------ 4 files changed, 32 insertions(+), 44 deletions(-) diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index 788fc81c..3cf3ae13 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -1322,6 +1322,8 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; +- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; + - (void)syncVariables; - (void)syncVariables:(BOOL)isProduction; diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 5b655b29..48eb7f8e 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -5047,6 +5047,10 @@ - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { [[self variables] onVariablesChanged:block]; } +- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { + [[self variables] onceVariablesChanged:block]; +} + - (void)syncVariables { #if DEBUG diff --git a/CleverTapSDK/ProductExperiences/CTVariables.h b/CleverTapSDK/ProductExperiences/CTVariables.h index 7f55cdab..37259c3e 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.h +++ b/CleverTapSDK/ProductExperiences/CTVariables.h @@ -15,7 +15,6 @@ NS_ASSUME_NONNULL_BEGIN @interface CTVariables : NSObject -@property(strong, nonatomic) NSMutableArray *variablesChangedBlocks; @property(strong, nonatomic) CTVarCache *varCache; - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo; @@ -28,6 +27,7 @@ NS_SWIFT_NAME(define(name:value:kind:)); - (CTVar *)getVariable:(NSString *)name; - (void)handleVariablesResponse:(NSDictionary *)varsResponse; - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; +- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName; - (NSDictionary*)varsPayload; - (void)addVarListeners; diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 88577774..a8260ccb 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -14,6 +14,9 @@ @interface CTVariables() @property (nonatomic, strong) CleverTapInstanceConfig *config; @property (nonatomic, strong) CTDeviceInfo *deviceInfo; + +@property(strong, nonatomic) NSMutableArray *variablesChangedBlocks; +@property(strong, nonatomic) NSMutableArray *onceNoDownloadsBlocks; @end @implementation CTVariables @@ -81,7 +84,7 @@ - (void)triggerVariablesChanged - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { if (!block) { - CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onValueChanged]."); + CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onVariablesChanged]."); return; } @@ -152,47 +155,26 @@ - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName { return varsPayload; } - - -//- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { -// -// if (!block) { -// CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onValueChanged]."); -// return; -// } -// -// CT_TRY -// if (!self.variablesChangedBlocks) { -// self.variablesChangedBlocks = [NSMutableArray array]; -// } -// [self.variablesChangedBlocks addObject:[block copy]]; -// CT_END_TRY -// -// if ([self.varCache hasReceivedDiffs]) { -// block(); -// } -//} -//+ (void)onceVariablesChangedAndNoDownloadsPending:(LeanplumVariablesChangedBlock)block -//{ -// if (!block) { -// [self throwError:@"[Leanplum onceVariablesChangedAndNoDownloadsPending:] Nil block " -// @"parameter provided."]; -// return; -// } -// -// if ([[LPVarCache sharedCache] hasReceivedDiffs] && [LPFileTransferManager sharedInstance].numPendingDownloads == 0) { -// block(); -// } else { -// LP_TRY -// static dispatch_once_t onceNoDownloadsBlocksToken; -// dispatch_once(&onceNoDownloadsBlocksToken, ^{ -// [LPInternalState sharedState].onceNoDownloadsBlocks = [NSMutableArray array]; -// }); -// @synchronized ([LPInternalState sharedState].onceNoDownloadsBlocks) { -// [[LPInternalState sharedState].onceNoDownloadsBlocks addObject:[block copy]]; -// } -// LP_END_TRY -// } -//} +- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { + + if (!block) { + CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onceVariablesChanged]."); + return; + } + + if ([self.varCache hasReceivedDiffs]) { + block(); + } else { + CT_TRY + static dispatch_once_t onceNoDownloadsBlocksToken; + dispatch_once(&onceNoDownloadsBlocksToken, ^{ + self.onceNoDownloadsBlocks = [NSMutableArray array]; + }); + @synchronized (self.onceNoDownloadsBlocks) { + [self.onceNoDownloadsBlocks addObject:[block copy]]; + } + CT_END_TRY + } +} @end From 2a1019e693f0f6b5fd0f70eafac94b9ef001d6eb Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 13 Mar 2023 01:01:18 +0200 Subject: [PATCH 22/86] Move params to Constants --- CleverTapSDK/CTConstants.h | 7 +++++++ CleverTapSDK/CTConstants.m | 9 +++++++-- CleverTapSDK/ProductExperiences/CTVariables.m | 15 +++++++-------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/CleverTapSDK/CTConstants.h b/CleverTapSDK/CTConstants.h index cc90a889..25a84e05 100644 --- a/CleverTapSDK/CTConstants.h +++ b/CleverTapSDK/CTConstants.h @@ -11,6 +11,13 @@ extern NSString *CT_KIND_ARRAY; extern NSString *CLEVERTAP_DEFAULTS_VARIABLES_KEY; extern NSString *CLEVERTAP_DEFAULTS_VARS_JSON_KEY; +extern NSString *CT_PE_VARS_PAYLOAD_TYPE; +extern NSString *CT_PE_VARS_PAYLOAD_KEY; +extern NSString *CT_PE_VAR_TYPE; +extern NSString *CT_PE_NUMBER_TYPE; +extern NSString *CT_PE_BOOL_TYPE; +extern NSString *CT_PE_DEFAULT_VALUE; + #define CleverTapLogInfo(level, fmt, ...) if(level >= 0) { NSLog((@"%@" fmt), @"[CleverTap]: ", ##__VA_ARGS__); } #define CleverTapLogDebug(level, fmt, ...) if(level > 0) { NSLog((@"%@" fmt), @"[CleverTap]: ", ##__VA_ARGS__); } #define CleverTapLogInternal(level, fmt, ...) if (level > 1) { NSLog((@"%@" fmt), @"[CleverTap]: ", ##__VA_ARGS__); } diff --git a/CleverTapSDK/CTConstants.m b/CleverTapSDK/CTConstants.m index 49853793..2084252c 100644 --- a/CleverTapSDK/CTConstants.m +++ b/CleverTapSDK/CTConstants.m @@ -7,8 +7,13 @@ NSString *CT_KIND_FLOAT = @"float"; NSString *CT_KIND_STRING = @"string"; NSString *CT_KIND_BOOLEAN = @"bool"; -//NSString *CT_KIND_FILE = @"file"; NSString *CT_KIND_DICTIONARY = @"group"; -//NSString *CT_KIND_ARRAY = @"list"; NSString *CLEVERTAP_DEFAULTS_VARIABLES_KEY = @"__clevertap_variables"; NSString *CLEVERTAP_DEFAULTS_VARS_JSON_KEY = @"__clevertap_variables_json"; + +NSString *CT_PE_VARS_PAYLOAD_TYPE = @"varsPayload"; +NSString *CT_PE_VARS_PAYLOAD_KEY = @"vars"; +NSString *CT_PE_VAR_TYPE = @"type"; +NSString *CT_PE_NUMBER_TYPE = @"number"; +NSString *CT_PE_BOOL_TYPE = @"boolean"; +NSString *CT_PE_DEFAULT_VALUE = @"defaultValue"; diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index a8260ccb..12286c75 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -9,7 +9,6 @@ #import "CTVariables.h" #import "CTConstants.h" #import "CTUtils.h" -//#import "CTVarCache.h" @interface CTVariables() @property (nonatomic, strong) CleverTapInstanceConfig *config; @@ -102,7 +101,7 @@ - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { - (NSDictionary*)varsPayload { NSMutableDictionary *result = [NSMutableDictionary dictionary]; - result[@"type"] = @"varsPayload"; + result[@"type"] = CT_PE_VARS_PAYLOAD_TYPE; NSMutableDictionary *allVars = [NSMutableDictionary dictionary]; @@ -117,19 +116,19 @@ - (NSDictionary*)varsPayload { } else { if ([varValue.kind isEqualToString:CT_KIND_INT] || [varValue.kind isEqualToString:CT_KIND_FLOAT]) { - varData[@"type"] = @"number"; + varData[CT_PE_VAR_TYPE] = CT_PE_NUMBER_TYPE; } else if ([varValue.kind isEqualToString:CT_KIND_BOOLEAN]) { - varData[@"type"] = @"boolean"; + varData[CT_PE_VAR_TYPE] = CT_PE_BOOL_TYPE; } else { - varData[@"type"] = varValue.kind; + varData[CT_PE_VAR_TYPE] = varValue.kind; } - varData[@"defaultValue"] = varValue.defaultValue; + varData[CT_PE_DEFAULT_VALUE] = varValue.defaultValue; allVars[key] = varData; } }]; - result[@"vars"] = allVars; + result[CT_PE_VARS_PAYLOAD_KEY] = allVars; return result; } @@ -142,7 +141,7 @@ - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName { if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]]) { NSString *payloadKey = [NSString stringWithFormat:@"%@.%@",varName,key]; - varsPayload[payloadKey] = @{@"defaultValue": value}; + varsPayload[payloadKey] = @{CT_PE_DEFAULT_VALUE: value}; } else if ([value isKindOfClass:[NSDictionary class]]) { NSString *payloadKey = [NSString stringWithFormat:@"%@.%@",varName,key]; From 17f0ad5adb8058785eea640301190f0b02225b1d Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 13 Mar 2023 01:14:56 +0200 Subject: [PATCH 23/86] Refactoring --- CleverTapSDK/ProductExperiences/CTVar.m | 148 ------------------ CleverTapSDK/ProductExperiences/CTVarCache.m | 90 +---------- CleverTapSDK/ProductExperiences/CTVariables.m | 39 ++++- 3 files changed, 38 insertions(+), 239 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index 602876b7..bcf9ea51 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -1,14 +1,10 @@ #import "CTVar-Internal.h" #import "CTVarCache.h" #import "CTConstants.h" -// TODO: commented files -//#import "LPFileManager.h" static BOOL LPVAR_PRINTED_CALLBACK_WARNING = NO; CTVarCache *varCache; @interface CTVar (PrivateProperties) - - @property (nonatomic) BOOL isInternal; @property (nonatomic, strong) NSString *name; @property (nonatomic, strong) NSArray *nameComponents; @@ -20,10 +16,8 @@ @interface CTVar (PrivateProperties) @property (nonatomic, strong) NSString *kind; @property (nonatomic, strong) NSMutableArray *fileReadyBlocks; @property (nonatomic, strong) NSMutableArray *valueChangedBlocks; -//@property (nonatomic, strong) LPVarCache *varCache; @property (nonatomic) BOOL fileIsPending; @property (nonatomic) BOOL hasChanged; - @end @implementation CTVar @@ -65,147 +59,6 @@ - (instancetype)initWithName:(NSString *)name withComponents:(NSArray *)componen return self; } -#pragma mark Defines - -//+ (LPVar *)define:(NSString *)name -//{ -// return [[LPVarCache sharedCache] define:name with:nil kind:nil]; -//} -// -//+ (LPVar *)define:(NSString *)name withInt:(int)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name with:[NSNumber numberWithInt:defaultValue] kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withFloat:(float)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name with:[NSNumber numberWithFloat:defaultValue] kind:LP_KIND_FLOAT]; -//} -// -//+ (LPVar *)define:(NSString *)name withDouble:(double)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithDouble:defaultValue] -// kind:LP_KIND_FLOAT]; -//} -// -//+ (LPVar *)define:(NSString *)name withCGFloat:(CGFloat)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithDouble:defaultValue] -// kind:LP_KIND_FLOAT]; -//} -// -//+ (LPVar *)define:(NSString *)name withShort:(short)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithShort:defaultValue] -// kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withChar:(char)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithChar:defaultValue] -// kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withBool:(BOOL)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithBool:defaultValue] -// kind:LP_KIND_BOOLEAN]; -//} -// -//+ (LPVar *)define:(NSString *)name withInteger:(NSInteger)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithInteger:defaultValue] -// kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withLong:(long)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithLong:defaultValue] -// kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withLongLong:(long long)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithLongLong:defaultValue] -// kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withUnsignedChar:(unsigned char)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithUnsignedChar:defaultValue] -// kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withUnsignedInt:(unsigned int)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithUnsignedInt:defaultValue] -// kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withUnsignedInteger:(NSUInteger)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithUnsignedInteger:defaultValue] -// kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withUnsignedLong:(unsigned long)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithUnsignedLong:defaultValue] -// kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withUnsignedLongLong:(unsigned long long)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithUnsignedLongLong:defaultValue] -// kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withUnsignedShort:(unsigned short)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name -// with:[NSNumber numberWithUnsignedShort:defaultValue] -// kind:LP_KIND_INT]; -//} -// -//+ (LPVar *)define:(NSString *)name withString:(NSString *)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name with:defaultValue kind:LP_KIND_STRING]; -//} -// -//+ (LPVar *)define:(NSString *)name withNumber:(NSNumber *)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name with:defaultValue kind:LP_KIND_FLOAT]; -//} - -// TODO: commented files -//+ (LPVar *)define:(NSString *)name withFile:(NSString *)defaultFilename -//{ -// return [[LPVarCache sharedCache] define:name with:defaultFilename kind:LP_KIND_FILE]; -//} - -//+ (LPVar *)define:(NSString *)name withDictionary:(NSDictionary *)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name with:defaultValue kind:LP_KIND_DICTIONARY]; -//} -// -//+ (LPVar *)define:(NSString *)name withArray:(NSArray *)defaultValue -//{ -// return [[LPVarCache sharedCache] define:name with:defaultValue kind:LP_KIND_ARRAY]; -//} - #pragma mark Updating - (void) cacheComputedValues @@ -251,7 +104,6 @@ - (void)update - (void)triggerValueChanged { - // TODO: will we provide both protocol valueDidChange method and valueChanged callback block? if (self.delegate && [self.delegate respondsToSelector:@selector(valueDidChange:)]) { diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 06364d5f..4e16eef2 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -73,8 +73,6 @@ - (NSArray *)getNameComponents:(NSString *)name [nameComponents addObject:matchArray[0]]; } NSArray *result = [NSArray arrayWithArray:nameComponents]; - - // iOS 3.x compatability. NSRegularExpression is not available, so there will be no components. if (result.count == 0) { return @[name]; } @@ -229,17 +227,6 @@ - (void)saveDiffs [archiver encodeObject:self.diffs forKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; [archiver finishEncoding]; -// NSData *encryptedDiffs = [LPAES encryptedDataFromData:diffsData]; - -// NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; -// -// [defaults setObject:encryptedDiffs forKey:LEANPLUM_DEFAULTS_VARIABLES_KEY]; -// -// [defaults setObject:LEANPLUM_SDK_VERSION forKey:LEANPLUM_DEFAULTS_SDK_VERSION]; -// -// [Leanplum synchronizeDefaults]; - -// [CTPreferences putObject:diffsData forKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; NSError *writeError = nil; NSString *fileName = [self dataArchiveFileName]; NSString *filePath = [CTPreferences filePathfromFileName:fileName]; @@ -266,7 +253,6 @@ - (void)applyVariableDiffs:(NSDictionary *)diffs_ // Merger helper will mutate diffs. // We need to lock it in case multiple threads will be accessing this. @synchronized (self.diffs) { - self.diffs = [self convert:self.diffs]; self.merged = [ContentMerger mergeWithVars:self.valuesFromClient diff:self.diffs]; } @@ -281,7 +267,7 @@ - (void)applyVariableDiffs:(NSDictionary *)diffs_ } } - // DONT SAVE VARS TO CACHE IF SILENT + // Do NOT save vars to cache if silent if (!self.silent) { [self saveDiffs]; @@ -298,78 +284,4 @@ - (void)onUpdate:(CacheUpdateBlock) block self.updateBlock = block; } -// -//- (void)clearUserContent -//{ -// self.diffs = nil; -// self.variants = nil; -// self.localCaps = nil; -// self.variantDebugInfo = nil; -// self.vars = nil; -// self.userAttributes = nil; -// self.merged = nil; -// self.varsJson = nil; -// self.varsSignature = nil; -// -// self.devModeValuesFromServer = nil; -// self.devModeFileAttributesFromServer = nil; -// -// [LPActionManager shared].messages = [NSMutableDictionary dictionary]; -// [LPActionManager shared].messagesDataFromServer = [NSMutableDictionary dictionary]; -// [LPActionManager shared].actionDefinitionsFromServer = [NSMutableDictionary dictionary]; -//} -// -// Resets the VarCache to stock state. Used for testing purposes. -//- (void)reset -//{ -// [self clearUserContent]; -// -// self.filesToInspect = nil; -// self.fileAttributes = nil; -// -// self.valuesFromClient = nil; -// self.defaultKinds = nil; -// -// self.updateBlock = nil; -// self.hasReceivedDiffs = NO; -// self.silent = NO; -// self.contentVersion = 0; -// self.hasTooManyFiles = NO; -//} - -- (NSDictionary*)convert:(NSDictionary*)result { - NSMutableDictionary *varsPayload = [NSMutableDictionary dictionary]; - - [result enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { - - if ([key containsString:@"."]) { - NSArray *components = [self getNameComponents:key]; - long namePosition = components.count - 1; - NSMutableDictionary *currentMap = varsPayload; - - for (int i = 0; i < components.count; i++) { - NSString *component = components[i]; - if (i == namePosition) { - currentMap[component] = value; - } - else { - if (!currentMap[component]) { - NSMutableDictionary *nestedMap = [NSMutableDictionary dictionary]; - currentMap[component] = nestedMap; - currentMap = nestedMap; - } - else { - currentMap = ((NSMutableDictionary*)currentMap[component]); - } - } - } - } - else { - varsPayload[key] = value; - } - }]; - - return varsPayload; -} - @end diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 12286c75..59b2305d 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -63,7 +63,8 @@ - (void)handleVariablesResponse:(NSDictionary *)varsResponse { if (varsResponse) { [[self varCache] setAppLaunchedRecorded:YES]; - [[self varCache] applyVariableDiffs:varsResponse]; + NSDictionary *values = [self unflatten:varsResponse]; + [[self varCache] applyVariableDiffs:values]; } } @@ -137,7 +138,6 @@ - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName { NSMutableDictionary *varsPayload = [NSMutableDictionary dictionary]; [map enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { - if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]]) { NSString *payloadKey = [NSString stringWithFormat:@"%@.%@",varName,key]; @@ -154,6 +154,41 @@ - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName { return varsPayload; } +- (NSDictionary*)unflatten:(NSDictionary*)result { + NSMutableDictionary *varsPayload = [NSMutableDictionary dictionary]; + + [result enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { + + if ([key containsString:@"."]) { + NSArray *components = [self.varCache getNameComponents:key]; + long namePosition = components.count - 1; + NSMutableDictionary *currentMap = varsPayload; + + for (int i = 0; i < components.count; i++) { + NSString *component = components[i]; + if (i == namePosition) { + currentMap[component] = value; + } + else { + if (!currentMap[component]) { + NSMutableDictionary *nestedMap = [NSMutableDictionary dictionary]; + currentMap[component] = nestedMap; + currentMap = nestedMap; + } + else { + currentMap = ((NSMutableDictionary*)currentMap[component]); + } + } + } + } + else { + varsPayload[key] = value; + } + }]; + + return varsPayload; +} + - (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { if (!block) { From 613ae6ce77a19a71ff85b888c2147daeea2113f8 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 13 Mar 2023 01:19:40 +0200 Subject: [PATCH 24/86] Keep CTVarDelegate --- CleverTapSDK/ProductExperiences/CTVar.h | 6 +----- CleverTapSDK/ProductExperiences/CTVar.m | 11 ++--------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVar.h b/CleverTapSDK/ProductExperiences/CTVar.h index b28e5887..2e3ca220 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.h +++ b/CleverTapSDK/ProductExperiences/CTVar.h @@ -14,10 +14,6 @@ typedef void (^CleverTapForceContentUpdateBlock)(BOOL success); NS_SWIFT_NAME(VarDelegate) @protocol CTVarDelegate @optional -/** - * For file variables, called when the file is ready. - */ -//- (void)fileIsReady:(CTVar *)var; /** * Called when the value of the variable changes. */ @@ -74,7 +70,7 @@ NS_SWIFT_NAME(Var) /** * Sets the delegate of the variable in order to use - * {@link CTVarDelegate::fileIsReady:} and {@link CTVarDelegate::valueDidChange:} + * {@link CTVarDelegate::valueDidChange:} */ - (void)setDelegate:(nullable id )delegate; diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index bcf9ea51..2b56ba57 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -104,7 +104,6 @@ - (void)update - (void)triggerValueChanged { - // TODO: will we provide both protocol valueDidChange method and valueChanged callback block? if (self.delegate && [self.delegate respondsToSelector:@selector(valueDidChange:)]) { [self.delegate valueDidChange:self]; @@ -133,17 +132,11 @@ - (void)onValueChanged:(CleverTapVariablesChangedBlock)block CT_END_TRY } -// TODO: Check if this method is needed - (void)setDelegate:(id)delegate { -// CT_TRY + CT_TRY _delegate = delegate; - - // TODO: commented files -// if ([LPInternalState sharedState].hasStarted && !_fileIsPending) { -// [self triggerFileIsReady]; -// } -// CT_END_TRY + CT_END_TRY } // TODO: decide if this method is relevant From 8b43522235205fb5a50cc9dbd435e63342c822a5 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 13 Mar 2023 01:33:39 +0200 Subject: [PATCH 25/86] Fix onceVariablesChanged --- CleverTapSDK/ProductExperiences/CTVariables.m | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 59b2305d..5073376d 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -15,7 +15,7 @@ @interface CTVariables() @property (nonatomic, strong) CTDeviceInfo *deviceInfo; @property(strong, nonatomic) NSMutableArray *variablesChangedBlocks; -@property(strong, nonatomic) NSMutableArray *onceNoDownloadsBlocks; +@property(strong, nonatomic) NSMutableArray *onceVariablesChangedBlocks; @end @implementation CTVariables @@ -79,6 +79,15 @@ - (void)triggerVariablesChanged for (CleverTapVariablesChangedBlock block in self.variablesChangedBlocks.copy) { block(); } + + NSArray *onceBlocksCopy; + @synchronized (self.onceVariablesChangedBlocks) { + onceBlocksCopy = self.onceVariablesChangedBlocks.copy; + [self.onceVariablesChangedBlocks removeAllObjects]; + } + for (CleverTapVariablesChangedBlock block in onceBlocksCopy) { + block(); + } } - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { @@ -100,6 +109,28 @@ - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { } } +- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { + + if (!block) { + CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onceVariablesChanged]."); + return; + } + + if ([self.varCache hasReceivedDiffs]) { + block(); + } else { + CT_TRY + static dispatch_once_t onceBlocksToken; + dispatch_once(&onceBlocksToken, ^{ + self.onceVariablesChangedBlocks = [NSMutableArray array]; + }); + @synchronized (self.onceVariablesChangedBlocks) { + [self.onceVariablesChangedBlocks addObject:[block copy]]; + } + CT_END_TRY + } +} + - (NSDictionary*)varsPayload { NSMutableDictionary *result = [NSMutableDictionary dictionary]; result[@"type"] = CT_PE_VARS_PAYLOAD_TYPE; @@ -189,26 +220,4 @@ - (NSDictionary*)unflatten:(NSDictionary*)result { return varsPayload; } -- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { - - if (!block) { - CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onceVariablesChanged]."); - return; - } - - if ([self.varCache hasReceivedDiffs]) { - block(); - } else { - CT_TRY - static dispatch_once_t onceNoDownloadsBlocksToken; - dispatch_once(&onceNoDownloadsBlocksToken, ^{ - self.onceNoDownloadsBlocks = [NSMutableArray array]; - }); - @synchronized (self.onceNoDownloadsBlocks) { - [self.onceNoDownloadsBlocks addObject:[block copy]]; - } - CT_END_TRY - } -} - @end From 74ca3fc9d5a4f91524465432dcd43fb97cf4fb6b Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 13 Mar 2023 01:36:01 +0200 Subject: [PATCH 26/86] Execute callbacks on main thread --- CleverTapSDK/CleverTap.m | 16 ++++++++++++++-- CleverTapSDK/ProductExperiences/CTVariables.m | 7 +++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 48eb7f8e..5a2b0a4b 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -2831,7 +2831,13 @@ - (void)sendQueue:(NSMutableArray *)queue { CleverTapLogDebug(self.config.logLevel, @"%@: Network error while sending queue, will retry: %@", self, error.localizedDescription); } if (self->_forceContentUpdateBlock) { - self->_forceContentUpdateBlock(NO); + if (![NSThread isMainThread]) { + dispatch_async(dispatch_get_main_queue(), ^{ + self->_forceContentUpdateBlock(NO); + }); + } else { + self->_forceContentUpdateBlock(NO); + } } dispatch_semaphore_signal(semaphore); }]; @@ -3032,7 +3038,13 @@ - (void)parseResponse:(NSData *)responseData { // Handle and Cache PE Variables [[self variables] handleVariablesResponse: jsonResp[CLTAP_PE_VARS_RESPONSE_KEY]]; if (self->_forceContentUpdateBlock) { - self->_forceContentUpdateBlock(YES); + if (![NSThread isMainThread]) { + dispatch_async(dispatch_get_main_queue(), ^{ + self->_forceContentUpdateBlock(YES); + }); + } else { + self->_forceContentUpdateBlock(YES); + } } // Handle events/profiles sync data diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 5073376d..4d04dee8 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -76,6 +76,13 @@ - (void)addVarListeners { - (void)triggerVariablesChanged { + if (![NSThread isMainThread]) { + dispatch_sync(dispatch_get_main_queue(), ^{ + [self triggerVariablesChanged]; + }); + return; + } + for (CleverTapVariablesChangedBlock block in self.variablesChangedBlocks.copy) { block(); } From d3f3304cf1f0c6788262396dc741b691d85f7b9d Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Tue, 21 Mar 2023 20:28:20 +0200 Subject: [PATCH 27/86] Merge dictionaries defined from Variables --- CleverTapSDK/ProductExperiences/CTVarCache.m | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 4e16eef2..0eed5d15 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -117,9 +117,14 @@ - (void)updateValues:(NSString *)name value = [NSMutableDictionary dictionaryWithDictionary:value]; } - // Check if value from another Variable will be overridden - if (valuesPtr[nameComponents.lastObject] && ![valuesPtr[nameComponents.lastObject] isEqual:value]) { - CleverTapLogInfo(self.config.logLevel, @"%@: Variable with name: %@ will override value: %@, with new value: %@.", self, name, valuesPtr[nameComponents.lastObject], value); + // Do not override variable dictionary values. If value is dictionary and + // already registered variable value is a dictionary, merge them. + // If values are not dictionaries, check if value from another variable will be overridden and log it. + id currentValue = valuesPtr[nameComponents.lastObject]; + if (currentValue && [currentValue isKindOfClass:NSDictionary.class] && [value isKindOfClass:NSMutableDictionary.class]) { + [value addEntriesFromDictionary: currentValue]; + } else if (currentValue && ![currentValue isEqual:value]) { + CleverTapLogInfo(self.config.logLevel, @"%@: Variable with name: %@ will override value: %@, with new value: %@.", self, name, currentValue, value); } [valuesPtr setObject:value forKey:nameComponents.lastObject]; From c5b604de5410da5187db7e604313229d9563914e Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Tue, 21 Mar 2023 20:28:54 +0200 Subject: [PATCH 28/86] Allow syncing in release builds --- CleverTapSDK/CleverTap.m | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 5a2b0a4b..593893cd 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -5064,8 +5064,10 @@ - (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { } - (void)syncVariables { - -#if DEBUG + [self syncVariables:NO]; +} + +- (void)syncVariablesEnsureHandshake { if ([self needHandshake]) { [self runSerialAsync:^{ [self doHandshakeAsyncWithCompletion:^{ @@ -5078,16 +5080,22 @@ - (void)syncVariables { [self _syncVars]; }]; } -#else - CleverTapLogDebug(_config.logLevel, @"%@: syncVariables can only be called from Debug configurations/builds", self); -#endif } - + - (void)syncVariables:(BOOL)isProduction { if (isProduction) { - CleverTapLogDebug(_config.logLevel, @"%@: syncVariables can only be called from Debug configurations/builds", self); +#if DEBUG + CleverTapLogInfo(_config.logLevel, @"%@: Calling syncVariables: with isProduction:YES from Debug configuration/build. Use syncVariables in this case", self); +#else + CleverTapLogInfo(_config.logLevel, @"%@: Calling syncVariables: with isProduction:YES from Release configuration/build. Do not release this build and use with caution", self); +#endif + [self syncVariablesEnsureHandshake]; } else { - [self syncVariables]; +#if DEBUG + [self syncVariablesEnsureHandshake]; +#else + CleverTapLogInfo(_config.logLevel, @"%@: syncVariables can only be called from Debug configurations/builds", self); +#endif } } From 4d01ed25e553e3ea132027de91842d56dba4e354 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Wed, 22 Mar 2023 10:05:00 +0530 Subject: [PATCH 29/86] added unit tests --- CleverTapSDK/ProductExperiences/CTVariables.h | 1 + CleverTapSDKTests/CTVarTests.m | 67 ++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVariables.h b/CleverTapSDK/ProductExperiences/CTVariables.h index 37259c3e..5f2de05f 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.h +++ b/CleverTapSDK/ProductExperiences/CTVariables.h @@ -31,6 +31,7 @@ NS_SWIFT_NAME(define(name:value:kind:)); - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName; - (NSDictionary*)varsPayload; - (void)addVarListeners; +- (NSDictionary*)unflatten:(NSDictionary*)result; @end diff --git a/CleverTapSDKTests/CTVarTests.m b/CleverTapSDKTests/CTVarTests.m index 2d6bd4d0..5d1e2358 100644 --- a/CleverTapSDKTests/CTVarTests.m +++ b/CleverTapSDKTests/CTVarTests.m @@ -17,19 +17,22 @@ #import #import #import +#import "CTRequestFactory.h" @interface CTVarTests : XCTestCase @end +CleverTapInstanceConfig *config; +CTDeviceInfo *deviceInfo; CTVariables *variables; @implementation CTVarTests - (void)setUp { - CleverTapInstanceConfig *config = [[CleverTapInstanceConfig alloc] initWithAccountId:@"id" accountToken:@"token" accountRegion:@"eu"]; + config = [[CleverTapInstanceConfig alloc] initWithAccountId:@"id" accountToken:@"token" accountRegion:@"eu"]; config.useCustomCleverTapId = YES; - CTDeviceInfo *deviceInfo = [[CTDeviceInfo alloc] initWithConfig:config andCleverTapID:@"test"]; + deviceInfo = [[CTDeviceInfo alloc] initWithConfig:config andCleverTapID:@"test"]; variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo]; } @@ -148,6 +151,8 @@ - (void)testVarValues { XCTAssertEqual(var.unsignedShortValue,varValue.unsignedShortValue); XCTAssertEqual(var.unsignedLongLongValue,varValue.unsignedLongLongValue); XCTAssertEqual(var.cgFloatValue,varValue.doubleValue); + XCTAssertEqual(var.charValue,varValue.charValue); + XCTAssertEqual(var.unsignedCharValue,varValue.unsignedCharValue); CTVar *mapVar = [variables define:@"MyMap" with:@{@"MyMapNumber":varValue} kind:CT_KIND_DICTIONARY]; XCTAssertTrue([[mapVar objectForKey:@"MyMapNumber"]isKindOfClass:[varValue class]]); @@ -155,4 +160,62 @@ - (void)testVarValues { XCTAssertTrue([mapVar.defaultValue isKindOfClass:[NSDictionary class]]); } +- (void)testSyncVarsPayload { + NSString *varName = @"Title"; + NSString *varValue = @"Hello"; + + CTVariables *variablesMock = OCMPartialMock(variables); + CTVar *definedVar = [variablesMock define:varName with:varValue kind:CT_KIND_STRING]; + + NSDictionary *payload = [variables varsPayload]; + + XCTAssertEqualObjects(payload[@"type"],@"varsPayload"); + NSDictionary *vars = [payload objectForKey:@"vars"]; + NSDictionary *titleMap = [vars objectForKey:varName]; + XCTAssertEqualObjects(titleMap[@"defaultValue"],varValue); + XCTAssertEqualObjects(titleMap[@"type"],definedVar.kind); +} + +- (void)testVariablesFlatten { + + NSString *varName = @"EmployeeMap"; + NSDictionary *employeeMap = @{ + @"Team": @{ + @"TeamName": @"Testing", + @"Designation": @"Tester" + }, + @"Name": @"Niko", + @"EmployeeID": @123 + }; + NSDictionary *flattenedEmployeeMap = @{ + @"EmployeeMap.Team.TeamName": @{ @"defaultValue": @"Testing" }, + @"EmployeeMap.Team.Designation": @{ @"defaultValue": @"Tester" }, + @"EmployeeMap.Name": @{ @"defaultValue": @"Niko" }, + @"EmployeeMap.EmployeeID": @{ @"defaultValue": @123 } + }; + NSDictionary *result = [variables flatten:employeeMap varName:varName]; + XCTAssertEqualObjects(result,flattenedEmployeeMap); +} + +- (void)testVariablesUnflatten { + NSDictionary *flattenedEmployeeMap = @{ + @"EmployeeMap.Team.TeamName": @{ @"defaultValue": @"Testing" }, + @"EmployeeMap.Team.Designation": @{ @"defaultValue": @"Tester" }, + @"EmployeeMap.Name": @{ @"defaultValue": @"Niko" }, + @"EmployeeMap.EmployeeID": @{ @"defaultValue": @123 } + }; + NSDictionary *unflattenedEmployeeMap = @{ + @"EmployeeMap": @{ + @"Team": @{ + @"TeamName": @{ @"defaultValue": @"Testing" }, + @"Designation": @{ @"defaultValue": @"Tester" } + }, + @"Name": @{ @"defaultValue": @"Niko" }, + @"EmployeeID": @{ @"defaultValue": @123 } + } + }; + NSDictionary *result = [variables unflatten:flattenedEmployeeMap]; + XCTAssertEqualObjects(result,unflattenedEmployeeMap); +} + @end From f439aeae8dedfcd8ae9ea8cb478f170fb1ccd5d8 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Thu, 23 Mar 2023 19:17:51 +0200 Subject: [PATCH 30/86] Fix ContentMerger if diff is nil --- CleverTapSDK/ProductExperiences/ContentMerger.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CleverTapSDK/ProductExperiences/ContentMerger.m b/CleverTapSDK/ProductExperiences/ContentMerger.m index a4936c55..b44ff7e5 100644 --- a/CleverTapSDK/ProductExperiences/ContentMerger.m +++ b/CleverTapSDK/ProductExperiences/ContentMerger.m @@ -11,6 +11,9 @@ @implementation ContentMerger + (id)mergeWithVars:(id)vars diff:(id)diff { + if (diff == nil) { + return vars; + } // Return the modified value if it is a `primitive` if ([diff isKindOfClass:NSNumber.class] || @@ -19,7 +22,9 @@ + (id)mergeWithVars:(id)vars diff:(id)diff { return diff; } - if ([vars isKindOfClass:[NSNumber class]] || [vars isKindOfClass:[NSString class]]) { + if ([vars isKindOfClass:[NSNumber class]] || + [vars isKindOfClass:[NSString class]] || + [vars isKindOfClass:NSNull.class]) { return diff; } From f10d44571fe725701bd58d413b46f158a9d1e10d Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Thu, 23 Mar 2023 19:19:23 +0200 Subject: [PATCH 31/86] Move forceContentUpdate block to CTVariables --- CleverTapSDK/CleverTap.m | 30 +++++-------------- CleverTapSDK/ProductExperiences/CTVariables.h | 2 ++ CleverTapSDK/ProductExperiences/CTVariables.m | 17 +++++++++++ 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 593893cd..108fbae4 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -259,7 +259,6 @@ @interface CleverTap () { @property (nonatomic, strong) NSString *gfSDKVersion; @property (nonatomic, strong) CTVariables *variables; -@property(strong, nonatomic) CleverTapForceContentUpdateBlock forceContentUpdateBlock; - (instancetype)init __unavailable; @@ -2822,23 +2821,15 @@ - (void)sendQueue:(NSMutableArray *)queue { CleverTapLogDebug(self.config.logLevel, @"%@: Got %lu response when sending queue, will retry", self, (long)httpResponse.statusCode); } } - + dispatch_semaphore_signal(semaphore); }]; [ctRequest onError:^(NSError * _Nullable error) { - + if (error) { CleverTapLogDebug(self.config.logLevel, @"%@: Network error while sending queue, will retry: %@", self, error.localizedDescription); } - if (self->_forceContentUpdateBlock) { - if (![NSThread isMainThread]) { - dispatch_async(dispatch_get_main_queue(), ^{ - self->_forceContentUpdateBlock(NO); - }); - } else { - self->_forceContentUpdateBlock(NO); - } - } + [[self variables] triggerForceContentUpdate:NO]; dispatch_semaphore_signal(semaphore); }]; [self.requestSender send:ctRequest]; @@ -3036,15 +3027,9 @@ - (void)parseResponse:(NSData *)responseData { #endif // Handle and Cache PE Variables - [[self variables] handleVariablesResponse: jsonResp[CLTAP_PE_VARS_RESPONSE_KEY]]; - if (self->_forceContentUpdateBlock) { - if (![NSThread isMainThread]) { - dispatch_async(dispatch_get_main_queue(), ^{ - self->_forceContentUpdateBlock(YES); - }); - } else { - self->_forceContentUpdateBlock(YES); - } + NSDictionary *varsResponse = jsonResp[CLTAP_PE_VARS_RESPONSE_KEY]; + if (varsResponse) { + [[self variables] handleVariablesResponse: jsonResp[CLTAP_PE_VARS_RESPONSE_KEY]]; } // Handle events/profiles sync data @@ -4725,7 +4710,6 @@ - (void)_resetProductConfig { - (void)_resetVars { if (self.config && self.deviceInfo.deviceId) { self.variables = [[CTVariables alloc]initWithConfig:self.config deviceInfo:self.deviceInfo]; - _forceContentUpdateBlock = nil; } } @@ -5139,7 +5123,7 @@ - (void)_syncVars { } - (void)forceContentUpdateWithBlock:(CleverTapForceContentUpdateBlock)block { - _forceContentUpdateBlock = block; + [[self variables] setForceContentUpdateBlock:block]; [self queueEvent:@{@"evtName": CLTAP_WZRK_FETCH_EVENT, @"evtData" : @{@"t": @4}} withType:CleverTapEventTypeFetch]; } diff --git a/CleverTapSDK/ProductExperiences/CTVariables.h b/CleverTapSDK/ProductExperiences/CTVariables.h index 37259c3e..0eb368a8 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.h +++ b/CleverTapSDK/ProductExperiences/CTVariables.h @@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN @interface CTVariables : NSObject @property(strong, nonatomic) CTVarCache *varCache; +@property(strong, nonatomic, nullable) CleverTapForceContentUpdateBlock forceContentUpdateBlock; - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo; @@ -26,6 +27,7 @@ NS_SWIFT_NAME(define(name:value:kind:)); - (CTVar *)getVariable:(NSString *)name; - (void)handleVariablesResponse:(NSDictionary *)varsResponse; +- (void)triggerForceContentUpdate:(BOOL)success; - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; - (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName; diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 4d04dee8..0993b696 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -16,6 +16,7 @@ @interface CTVariables() @property(strong, nonatomic) NSMutableArray *variablesChangedBlocks; @property(strong, nonatomic) NSMutableArray *onceVariablesChangedBlocks; + @end @implementation CTVariables @@ -65,6 +66,22 @@ - (void)handleVariablesResponse:(NSDictionary *)varsResponse [[self varCache] setAppLaunchedRecorded:YES]; NSDictionary *values = [self unflatten:varsResponse]; [[self varCache] applyVariableDiffs:values]; + [self triggerForceContentUpdate:YES]; + } +} + +- (void)triggerForceContentUpdate:(BOOL)success { + if (self.forceContentUpdateBlock) { + CleverTapForceContentUpdateBlock block = [self.forceContentUpdateBlock copy]; + if (![NSThread isMainThread]) { + dispatch_async(dispatch_get_main_queue(), ^{ + block(success); + }); + } else { + block(success); + } + + self.forceContentUpdateBlock = nil; } } From 93ca2bad6ace39d9296d15be4f3b60cd2cda3089 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Thu, 23 Mar 2023 19:25:55 +0200 Subject: [PATCH 32/86] Allow calling FCU without block --- CleverTapSDK/CleverTap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index 3cf3ae13..dcdb1514 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -1341,7 +1341,7 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; @param block a callback with a boolean flag whether the update was successful. */ -- (void)forceContentUpdateWithBlock:(CleverTapForceContentUpdateBlock _Nonnull)block; +- (void)forceContentUpdate:(CleverTapForceContentUpdateBlock _Nullable)block; @end From 96eb209c59d394b677ef8e56fe29ed40d3951015 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Thu, 23 Mar 2023 19:43:33 +0200 Subject: [PATCH 33/86] Fix update and merge var --- CleverTapSDK/ProductExperiences/CTVarCache.m | 23 +++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 0eed5d15..8433b744 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -122,7 +122,8 @@ - (void)updateValues:(NSString *)name // If values are not dictionaries, check if value from another variable will be overridden and log it. id currentValue = valuesPtr[nameComponents.lastObject]; if (currentValue && [currentValue isKindOfClass:NSDictionary.class] && [value isKindOfClass:NSMutableDictionary.class]) { - [value addEntriesFromDictionary: currentValue]; + // Merge all entries from both dictionaries. NSMutableDictionary addEntriesFromDictionary: will not work for nested dictionaries. + value = [ContentMerger mergeWithVars:value diff:currentValue]; } else if (currentValue && ![currentValue isEqual:value]) { CleverTapLogInfo(self.config.logLevel, @"%@: Variable with name: %@ will override value: %@, with new value: %@.", self, name, currentValue, value); } @@ -134,14 +135,20 @@ - (void)updateValues:(NSString *)name // Merge default variable value with VarCache.merged value // This is neccessary if variable was registered after VarCache.applyVariableDiffs - (void)mergeVariable:(CTVar * _Nonnull)var { - NSString *firsComponent = var.nameComponents.firstObject; - id defaultValue = [self.valuesFromClient objectForKey:firsComponent]; - id mergedValue = [self.merged objectForKey:firsComponent]; - if (![defaultValue isEqual:mergedValue]) { + NSString *firstComponent = var.nameComponents.firstObject; + id defaultValue = [self.valuesFromClient objectForKey:firstComponent]; + id mergedValue = [self.merged objectForKey:firstComponent]; + + BOOL shouldMerge = (!defaultValue && mergedValue) || + (defaultValue && ![defaultValue isEqual:mergedValue]); + if (shouldMerge) { id newValue = [ContentMerger mergeWithVars:defaultValue diff:mergedValue]; - [self.merged setObject:newValue forKey:firsComponent]; - - NSMutableString *name = [[NSMutableString alloc] initWithString:firsComponent]; + if (newValue == nil) { + return; + } + [self.merged setObject:newValue forKey:firstComponent]; + + NSMutableString *name = [[NSMutableString alloc] initWithString:firstComponent]; for (int i = 1; i < var.nameComponents.count; i++) { CTVar *existingVar = self.vars[name]; From 3e42e1d4c3dd9972dff10ddfd5b8a387fc262a8a Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Fri, 24 Mar 2023 22:12:18 +0200 Subject: [PATCH 34/86] Refactor ContentMerger and add tests --- CleverTapSDK.xcodeproj/project.pbxproj | 4 + .../ProductExperiences/ContentMerger.m | 55 +-- CleverTapSDKTests/ContentMergerTest.m | 413 ++++++++++++++++++ 3 files changed, 447 insertions(+), 25 deletions(-) create mode 100644 CleverTapSDKTests/ContentMergerTest.m diff --git a/CleverTapSDK.xcodeproj/project.pbxproj b/CleverTapSDK.xcodeproj/project.pbxproj index 6b632ac2..41f8349d 100644 --- a/CleverTapSDK.xcodeproj/project.pbxproj +++ b/CleverTapSDK.xcodeproj/project.pbxproj @@ -249,6 +249,7 @@ 57EDC7A12683845B001DD157 /* CleverTap+InAppNotifications.h in Headers */ = {isa = PBXBuildFile; fileRef = 57EDC7A02683845B001DD157 /* CleverTap+InAppNotifications.h */; settings = {ATTRIBUTES = (Public, ); }; }; 583D9573DCDA5D219016B990 /* libPods-shared-CleverTapSDKTestsApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2108AFE9090417CC69A0E3EB /* libPods-shared-CleverTapSDKTestsApp.a */; }; 63359A5791C468263B934E22 /* libPods-shared-CleverTapSDKTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 917B24847F7C2A8B0825AEC1 /* libPods-shared-CleverTapSDKTests.a */; }; + 6A2E0B9129CCCC8600FCEA5F /* ContentMergerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */; }; 6A2E4C18291E8A4A00385536 /* CleverTapInstanceConfigTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */; }; 6A775C3329BE78C7007790E0 /* CTVariables.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A775C3129BE78C7007790E0 /* CTVariables.h */; settings = {ATTRIBUTES = (Private, ); }; }; 6A775C3429BE78C7007790E0 /* CTVariables.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A775C3129BE78C7007790E0 /* CTVariables.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -645,6 +646,7 @@ 4EF1CF9D29B076A300E3CB6A /* CTVarCache+Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CTVarCache+Tests.m"; sourceTree = ""; }; 5709005227FD8E1E0011B89F /* CleverTap+SCDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CleverTap+SCDomain.h"; sourceTree = ""; }; 57EDC7A02683845B001DD157 /* CleverTap+InAppNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CleverTap+InAppNotifications.h"; sourceTree = ""; }; + 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContentMergerTest.m; sourceTree = ""; }; 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CleverTapInstanceConfigTests.m; sourceTree = ""; }; 6A775C3129BE78C7007790E0 /* CTVariables.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTVariables.h; sourceTree = ""; }; 6A775C3229BE78C7007790E0 /* CTVariables.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVariables.m; sourceTree = ""; }; @@ -1073,6 +1075,7 @@ 4EC2D084278AAD8000F4DE54 /* IdentityManagementTests.m */, 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */, 4EED219A29AF6368006CEA19 /* CTVarTests.m */, + 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */, ); path = CleverTapSDKTests; sourceTree = ""; @@ -1858,6 +1861,7 @@ 4E1F154F27691CA0009387AE /* CleverTapInstanceTests.m in Sources */, 4E1F155B276B662C009387AE /* EventDetail.m in Sources */, 4EED219B29AF6368006CEA19 /* CTVarTests.m in Sources */, + 6A2E0B9129CCCC8600FCEA5F /* ContentMergerTest.m in Sources */, D02AC2DB276044F70031C1BE /* CleverTapSDKTests.m in Sources */, D02AC2EB2767F4590031C1BE /* BaseTestCase.m in Sources */, 4E1F15532769B83D009387AE /* EventTests.m in Sources */, diff --git a/CleverTapSDK/ProductExperiences/ContentMerger.m b/CleverTapSDK/ProductExperiences/ContentMerger.m index b44ff7e5..be2eb049 100644 --- a/CleverTapSDK/ProductExperiences/ContentMerger.m +++ b/CleverTapSDK/ProductExperiences/ContentMerger.m @@ -11,45 +11,50 @@ @implementation ContentMerger + (id)mergeWithVars:(id)vars diff:(id)diff { - if (diff == nil) { + if (!diff) { return vars; } - + // Return the modified value if it is a `primitive` - if ([diff isKindOfClass:NSNumber.class] || - [diff isKindOfClass:NSString.class] || - [diff isKindOfClass:NSNull.class]) { + if ([diff isKindOfClass:[NSNumber class]] || + [diff isKindOfClass:[NSString class]] || + [diff isKindOfClass:[NSNull class]]) { return diff; } - if ([vars isKindOfClass:[NSNumber class]] || [vars isKindOfClass:[NSString class]] || - [vars isKindOfClass:NSNull.class]) { + [vars isKindOfClass:[NSNull class]]) { return diff; } - NSMutableDictionary *merged = [NSMutableDictionary dictionary]; - BOOL isVarsDict = NO; - if ([vars isKindOfClass:[NSDictionary class]]) { - // Create new dictionary from vars - merged = [NSMutableDictionary dictionaryWithDictionary:vars]; - isVarsDict = YES; + // Return nil if neither vars nor diff is dictionary. + // Use isKindOfClass: to check first. Note that (NSDictionary *) cast will succeed if object is NSArray*. + if (![vars isKindOfClass:[NSDictionary class]] && ![diff isKindOfClass:[NSDictionary class]]) { + return nil; } - if ([diff isKindOfClass:[NSDictionary class]]) { - NSDictionary *diffDict = (NSDictionary*)diff; - [diffDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { - id defaultValue = merged[key] ?: [NSNull null]; - merged[key] = [self mergeWithVars:defaultValue diff:value]; - }]; - - return merged; - } else if (isVarsDict) { - // vars is a dictionary but diff is not or diff is nil, return vars - return vars; + if (![vars isKindOfClass:[NSDictionary class]]) { + // diff is dictionary + return diff; } - return [NSNull null]; + // vars is dictionary + NSMutableDictionary *merged = [NSMutableDictionary dictionaryWithDictionary:vars]; + if (![diff isKindOfClass:[NSDictionary class]]) { + return merged; + } + + // vars and diff are dictionary + NSDictionary *diffDict = (NSDictionary *)diff; + [diffDict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { + id defaultValue = merged[key]; + id mergedValue = [self mergeWithVars:defaultValue diff:value]; + if (mergedValue) { + merged[key] = mergedValue; + } + }]; + + return merged; } @end diff --git a/CleverTapSDKTests/ContentMergerTest.m b/CleverTapSDKTests/ContentMergerTest.m new file mode 100644 index 00000000..46ddfda2 --- /dev/null +++ b/CleverTapSDKTests/ContentMergerTest.m @@ -0,0 +1,413 @@ +// +// ContentMergerTest.m +// CleverTapSDKTests +// +// Created by Nikola Zagorchev on 23.03.23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import +#import "ContentMerger.h" + +@interface ContentMergerTest : XCTestCase + +@end + +@implementation ContentMergerTest + +- (void)testMergePrimitive { + NSString *vars = @"a"; + NSNumber *diff = @4; + NSNumber *result = (NSNumber *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertEqualObjects(diff, result); +} + +- (void)testMergeBool { + NSNumber *vars = @YES; + NSNumber *diff = @NO; + NSNumber *result = (NSNumber *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertEqualObjects(diff, result); + XCTAssertFalse([result boolValue]); +} + +- (void)testMergeBoolYes { + NSNumber *vars = @NO; + NSNumber *diff = @YES; + NSNumber *result = (NSNumber *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertEqualObjects(diff, result); + XCTAssertTrue([result boolValue]); +} + +- (void)testMergeMapWithPrimitive { + NSDictionary *vars = @{ + @"abc": @"qwe", + @"1": @123 + }; + NSString *diff = @"diff"; + NSString *result = (NSString *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertEqualObjects(diff, result); +} + +- (void)testMergeValues { + NSString *resultString = (NSString *)[ContentMerger mergeWithVars:@"defaultValue" diff:@"newValue"]; + XCTAssertEqualObjects(@"newValue", resultString); + + NSNumber *resultNumber = (NSNumber *)[ContentMerger mergeWithVars:@199 diff:@123456789]; + XCTAssertEqualObjects(@123456789, resultNumber); + + NSString *resultNull = (NSString *)[ContentMerger mergeWithVars:[NSNull null] diff:@"newValue"]; + XCTAssertEqualObjects(@"newValue", resultNull); +} + +- (void)testMergeValuesComplex { + NSDictionary *vars = @{ + @"messageId1": @{ + @"vars": @{ + @"myNumber": @0, + @"myString": @"defaultValue" + } + } + }; + + NSDictionary *diff = @{ + @"messageId1": @{ + @"vars": @{ + @"myNumber": @1, + @"myString": @"newValue" + } + } + }; + + NSDictionary *expected = diff; + NSDictionary *result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([result isEqualToDictionary:expected]); +} + +- (void)testMergeValuesIncludeDefaults { + NSDictionary *vars = @{ + @"messageId1": @{ + @"vars": @{ + @"myNumber": @0, + @"myString": @"defaultValue" + } + } + }; + + NSDictionary *diff = @{ + @"messageId1": @{ + @"vars": @{ + @"myString": @"newValue" + } + } + }; + + NSDictionary *expected = @{ + @"messageId1": @{ + @"vars": @{ + @"myNumber": @0, + @"myString": @"newValue" + } + } + }; + + NSDictionary *result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([result isEqualToDictionary:expected]); +} + +- (void)testMergeDictionaries { + NSDictionary *vars = @{ + @"abc": @"qwe", + @"nested2": @{ + @"a": @"a", + @"c": [NSNull null], + @"d": @4444 + } + }; + + NSDictionary *diff = @{ + @"abc": @"rty", + @"nested2": @{ + @"a": @"a", + @"c": @"value", + @"d": @555 + } + }; + + NSDictionary *expected = @{ + @"abc": @"rty", + @"nested2": @{ + @"a": @"a", + @"c": @"value", + @"d": @555 + } + }; + + id result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([expected isEqualToDictionary:result]); +} + +- (void)testMergeDictionariesWithNull { + NSDictionary *vars = @{ + @"a": [NSNull null], + @"b": [NSNull null] + }; + + NSDictionary *diff = @{ + @"a": @"text", + @"c": [NSNull null] + }; + + NSDictionary *expected = @{ + @"a": @"text", + @"b": [NSNull null], + @"c": [NSNull null] + }; + + id result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([expected isEqualToDictionary:result]); +} + +- (void)testMergeDictionariesIncludeDefaults { + NSDictionary *vars = @{ + @"abc": @"qwe", + @"nested": @{ + @"abc": @"qwe", + @"1": @123 + }, + @"nested2": @{ + @"a": @"a", + @"b": @[@1, @2, @3, @4], + @"c": [NSNull null], + @"d": @4444 + } + }; + + NSDictionary *diff = @{ + @"nested": @{ + @"abc": @"abc", + @"qwerty": @"qwerty" + }, + @"nested2": @{ + @"a": @"b", + @"d": @111, + @"e": @999 + } + }; + + NSDictionary *expected = @{ + @"abc": @"qwe", + @"nested": @{ + @"abc": @"abc", + @"1": @123, + @"qwerty": @"qwerty" + }, + @"nested2": @{ + @"a": @"b", + @"b": @[@1, @2, @3, @4], + @"c": [NSNull null], + @"d": @111, + @"e": @999 + } + }; + + id result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([expected isEqualToDictionary:result]); +} + +- (void)testMergeDictionariesIncludeDiffs { + NSDictionary *vars = @{ + @"abc": @"qwe", + @"nested": @{ + @"abc": @"qwe", + @"1": @123 + } + }; + + NSDictionary *diff = @{ + @"nested": @{ + @"qwerty": @"qwerty", + @"nested2": @{ + @"a": @"b" + } + } + }; + + NSDictionary *expected = @{ + @"abc": @"qwe", + @"nested": @{ + @"abc": @"qwe", + @"1": @123, + @"qwerty": @"qwerty", + @"nested2": @{ + @"a": @"b" + } + } + }; + + id result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([expected isEqualToDictionary:result]); +} + +- (void)testMergeWithEmpty { + NSDictionary *vars = @{ + @"abc": @"qwe", + @"nested": @{ + @"abc": @"qwe", + @"1": @123 + } + }; + + NSDictionary *diff = @{}; + + id result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([vars isEqualToDictionary:result]); +} + +- (void)testMergeEmpty { + NSDictionary *vars = @{}; + + NSDictionary *diff = @{ + @"abc": @"qwe", + @"nested": @{ + @"abc": @"qwe", + @"1": @123 + } + }; + + id result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([diff isEqualToDictionary:result]); +} + +- (void)testMergeNull { + NSNull *vars = [NSNull null]; + + NSDictionary *diff = @{ + @"abc": @"qwe", + @"nested": @{ + @"abc": @"qwe", + @"1": @123 + } + }; + + id result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([diff isEqualToDictionary:result]); +} + +- (void)testMergeWithNull { + NSDictionary *vars = @{}; + + NSNull *diff = [NSNull null]; + + id result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([[NSNull null] isEqual:result]); +} + +- (void)testMergeDifferentTypes { + NSDictionary *vars = @{ + @"k1": @20, + @"k2": @"hi", + @"k3": @YES, + @"k4": @4.3 + }; + NSDictionary *diff = @{ + @"k1": @21, + @"k3": @NO, + @"k4": @-4.8 + }; + NSDictionary *expected = @{ + @"k1": @21, + @"k2": @"hi", + @"k3": @NO, + @"k4": @-4.8 + }; + NSDictionary *result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([result isEqualToDictionary:expected]); +} + +- (void)testMergeNestedDictionaries { + NSDictionary *vars = @{ + @"k2": @{ + @"m1": @(1), + @"m2": @"hello", + @"m3": @(NO) + }, + @"k3": @{ + @"m1": @(1), + @"m2": @"hello", + @"m3": @(NO) + }, + @"k4": @{ + @"m1": @(1), + @"m2": @"hello", + @"m3": @(NO) + }, + @"k5": @(4.3) + }; + + NSDictionary *diffs = @{ + @"k2": @{ + @"m1": @(1), + @"m2": @"hello", + @"m3": @(NO) + }, + @"k3": @{ + @"m1": @(2), + @"m2": @"bye", + @"m3": @(YES) + }, + @"k4": @{ + @"m1": @(2), + @"m3": @(YES), + @"m4": @"new key" + } + }; + + NSDictionary *expected = @{ + @"k2": @{ + @"m1": @(1), + @"m2": @"hello", + @"m3": @(NO) + }, + @"k3": @{ + @"m1": @(2), + @"m2": @"bye", + @"m3": @(YES) + }, + @"k4": @{ + @"m1": @(2), + @"m2": @"hello", + @"m3": @(YES), + @"m4": @"new key" + }, + @"k5": @(4.3) + }; + + NSDictionary *result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diffs]; + XCTAssertEqualObjects(result, expected); +} + +- (void)testMergeArr { + NSArray *vars = @[@1, @2, @3, @4]; + NSArray *diff = @[@1, @2, @3, @6]; + + // ContentMerger does not support merging arrays + id result = (NSArray *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertNil(result); +} + +- (void)testMergeDictionariesArr { + NSDictionary *vars = @{ + @"arr": @[@1, @2, @3, @4], + }; + + NSDictionary *diff = @{ + @"arr": @[@1, @2, @3, @5], + }; + + // ContentMerger does not support merging arrays, expect vars dictionary + id result = (NSDictionary *)[ContentMerger mergeWithVars:vars diff:diff]; + XCTAssertTrue([vars isEqualToDictionary:result]); +} + +@end From 2f9b23123252ff82af180bd1974a5829e96725b4 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Fri, 24 Mar 2023 22:12:38 +0200 Subject: [PATCH 35/86] Ensure merged can be modified --- CleverTapSDK/ProductExperiences/CTVarCache.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 8433b744..438da4ef 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -135,6 +135,10 @@ - (void)updateValues:(NSString *)name // Merge default variable value with VarCache.merged value // This is neccessary if variable was registered after VarCache.applyVariableDiffs - (void)mergeVariable:(CTVar * _Nonnull)var { + if (!self.merged || ![self.merged isKindOfClass:[NSMutableDictionary class]]) { + return; + } + NSString *firstComponent = var.nameComponents.firstObject; id defaultValue = [self.valuesFromClient objectForKey:firstComponent]; id mergedValue = [self.merged objectForKey:firstComponent]; From 99d97669e275f4235392fb703df4a53b07666120 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 27 Mar 2023 14:45:46 +0300 Subject: [PATCH 36/86] Fix FCU name --- CleverTapSDK/CleverTap.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 108fbae4..bd7c0c64 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -5122,7 +5122,7 @@ - (void)_syncVars { dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); } -- (void)forceContentUpdateWithBlock:(CleverTapForceContentUpdateBlock)block { +- (void)forceContentUpdate:(CleverTapForceContentUpdateBlock)block { [[self variables] setForceContentUpdateBlock:block]; [self queueEvent:@{@"evtName": CLTAP_WZRK_FETCH_EVENT, @"evtData" : @{@"t": @4}} withType:CleverTapEventTypeFetch]; } From 0387e7f44f8ed589276d299c68e7dc44ef286394 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 27 Mar 2023 18:42:26 +0300 Subject: [PATCH 37/86] Refactor flatten and unflatten --- CleverTapSDK/ProductExperiences/CTVariables.m | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 0993b696..1851dade 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -23,7 +23,7 @@ @implementation CTVariables - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo { if ((self = [super init])) { - self.varCache = [[CTVarCache alloc]initWithConfig:config deviceInfo:deviceInfo]; + self.varCache = [[CTVarCache alloc] initWithConfig:config deviceInfo:deviceInfo]; } return self; } @@ -156,37 +156,37 @@ - (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { } - (NSDictionary*)varsPayload { - NSMutableDictionary *result = [NSMutableDictionary dictionary]; - result[@"type"] = CT_PE_VARS_PAYLOAD_TYPE; + NSMutableDictionary *payload = [NSMutableDictionary dictionary]; + payload[@"type"] = CT_PE_VARS_PAYLOAD_TYPE; NSMutableDictionary *allVars = [NSMutableDictionary dictionary]; [self.varCache.vars - enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, CTVar * _Nonnull varValue, BOOL * _Nonnull stop) { + enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, CTVar * _Nonnull variable, BOOL * _Nonnull stop) { NSMutableDictionary *varData = [NSMutableDictionary dictionary]; - if ([varValue.defaultValue isKindOfClass:[NSDictionary class]]) { - NSDictionary *flattenedMap = [self flatten:varValue.defaultValue varName:varValue.name]; + if ([variable.defaultValue isKindOfClass:[NSDictionary class]]) { + NSDictionary *flattenedMap = [self flatten:variable.defaultValue varName:variable.name]; [allVars addEntriesFromDictionary:flattenedMap]; } else { - if ([varValue.kind isEqualToString:CT_KIND_INT] || [varValue.kind isEqualToString:CT_KIND_FLOAT]) { + if ([variable.kind isEqualToString:CT_KIND_INT] || [variable.kind isEqualToString:CT_KIND_FLOAT]) { varData[CT_PE_VAR_TYPE] = CT_PE_NUMBER_TYPE; } - else if ([varValue.kind isEqualToString:CT_KIND_BOOLEAN]) { + else if ([variable.kind isEqualToString:CT_KIND_BOOLEAN]) { varData[CT_PE_VAR_TYPE] = CT_PE_BOOL_TYPE; } else { - varData[CT_PE_VAR_TYPE] = varValue.kind; + varData[CT_PE_VAR_TYPE] = variable.kind; } - varData[CT_PE_DEFAULT_VALUE] = varValue.defaultValue; + varData[CT_PE_DEFAULT_VALUE] = variable.defaultValue; allVars[key] = varData; } }]; - result[CT_PE_VARS_PAYLOAD_KEY] = allVars; + payload[CT_PE_VARS_PAYLOAD_KEY] = allVars; - return result; + return payload; } - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName { @@ -195,13 +195,11 @@ - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName { [map enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]]) { - NSString *payloadKey = [NSString stringWithFormat:@"%@.%@",varName,key]; - varsPayload[payloadKey] = @{CT_PE_DEFAULT_VALUE: value}; - } - else if ([value isKindOfClass:[NSDictionary class]]) { - NSString *payloadKey = [NSString stringWithFormat:@"%@.%@",varName,key]; - - NSDictionary* flattenedMap = [self flatten:value varName:payloadKey]; + NSString *flatKey = [NSString stringWithFormat:@"%@.%@", varName, key]; + varsPayload[flatKey] = @{ CT_PE_DEFAULT_VALUE: value }; + } else if ([value isKindOfClass:[NSDictionary class]]) { + NSString *flatKey = [NSString stringWithFormat:@"%@.%@", varName, key]; + NSDictionary* flattenedMap = [self flatten:value varName:flatKey]; [varsPayload addEntriesFromDictionary:flattenedMap]; } }]; @@ -209,15 +207,17 @@ - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName { return varsPayload; } -- (NSDictionary*)unflatten:(NSDictionary*)result { - NSMutableDictionary *varsPayload = [NSMutableDictionary dictionary]; +- (NSDictionary*)unflatten:(NSDictionary*)flatDictionary { + if (!flatDictionary) { + return nil; + } - [result enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { - + NSMutableDictionary *unflattenVars = [NSMutableDictionary dictionary]; + [flatDictionary enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { if ([key containsString:@"."]) { NSArray *components = [self.varCache getNameComponents:key]; long namePosition = components.count - 1; - NSMutableDictionary *currentMap = varsPayload; + NSMutableDictionary *currentMap = unflattenVars; for (int i = 0; i < components.count; i++) { NSString *component = components[i]; @@ -237,11 +237,11 @@ - (NSDictionary*)unflatten:(NSDictionary*)result { } } else { - varsPayload[key] = value; + unflattenVars[key] = value; } }]; - return varsPayload; + return unflattenVars; } @end From 08eb028a87e4da6da61e9df25a93d6bae6ed6ccb Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Tue, 28 Mar 2023 15:37:45 +0530 Subject: [PATCH 38/86] removed irrelevant code --- CleverTapSDK/CTConstants.h | 1 + CleverTapSDK/CTConstants.m | 1 + CleverTapSDK/CTRequest.h | 1 - CleverTapSDK/CTRequestFactory.m | 15 +-------------- CleverTapSDK/CTRequestSender.h | 2 -- CleverTapSDK/CTRequestSender.m | 13 ------------- CleverTapSDK/CleverTap.m | 2 -- 7 files changed, 3 insertions(+), 32 deletions(-) diff --git a/CleverTapSDK/CTConstants.h b/CleverTapSDK/CTConstants.h index 25a84e05..26ea3272 100644 --- a/CleverTapSDK/CTConstants.h +++ b/CleverTapSDK/CTConstants.h @@ -2,6 +2,7 @@ extern NSString *const kCTApiDomain; extern NSString *const kCTNotifViewedApiDomain; +extern NSString *const kHANDSHAKE_URL; extern NSString *CT_KIND_INT; extern NSString *CT_KIND_FLOAT; extern NSString *CT_KIND_STRING; diff --git a/CleverTapSDK/CTConstants.m b/CleverTapSDK/CTConstants.m index 2084252c..bbe0f281 100644 --- a/CleverTapSDK/CTConstants.m +++ b/CleverTapSDK/CTConstants.m @@ -2,6 +2,7 @@ NSString *const kCTApiDomain = @"clevertap-prod.com"; NSString *const kCTNotifViewedApiDomain = @"spiky.clevertap-prod.com"; +NSString *const kHANDSHAKE_URL = @"https://eu1.clevertap-prod.com/hello"; NSString *CT_KIND_INT = @"integer"; NSString *CT_KIND_FLOAT = @"float"; diff --git a/CleverTapSDK/CTRequest.h b/CleverTapSDK/CTRequest.h index bb9af88f..b03f0b27 100644 --- a/CleverTapSDK/CTRequest.h +++ b/CleverTapSDK/CTRequest.h @@ -18,7 +18,6 @@ typedef void (^CTNetworkResponseErrorBlock)(NSError * _Nullable error); - (void)onResponse:(CTNetworkResponseBlock _Nonnull)responseBlock; - (void)onError:(CTNetworkResponseErrorBlock _Nonnull)errorBlock; -//- (void)send:(NSURLSession *_Nonnull)urlSession; @property (nonatomic, strong, nonnull) NSMutableURLRequest *urlRequest; @property (nonatomic, strong, nonnull) CTNetworkResponseBlock responseBlock; diff --git a/CleverTapSDK/CTRequestFactory.m b/CleverTapSDK/CTRequestFactory.m index 2b0d436a..5c2923c5 100644 --- a/CleverTapSDK/CTRequestFactory.m +++ b/CleverTapSDK/CTRequestFactory.m @@ -12,10 +12,7 @@ @implementation CTRequestFactory + (CTRequest *_Nonnull)helloRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config { - - return -// [CTRequestFactory createGetForParams:params]; - [[CTRequest alloc]initWithHttpMethod:@"GET" config:config params:nil url:@"https://eu1.clevertap-prod.com/hello"]; + return [[CTRequest alloc]initWithHttpMethod:@"GET" config:config params:nil url:kHANDSHAKE_URL]; } + (CTRequest *_Nonnull)eventRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config params:(id _Nullable)params url:(NSString *_Nonnull)url { @@ -26,14 +23,4 @@ + (CTRequest *_Nonnull)syncVarsRequestWithConfig:(CleverTapInstanceConfig *_Nonn return [[CTRequest alloc]initWithHttpMethod:@"POST" config:config params: params url:url]; } -#pragma mark Private methods - -//+ (CTRequest *)createGetForParams:(NSDictionary *)params { -// return [CTRequest getForParams:params]; -//} -// -//+ (CTRequest *)createPostForApiMethod:(NSString *)apiMethod params:(NSDictionary *)params { -// return [CTRequest postForParams:params]; -//} - @end diff --git a/CleverTapSDK/CTRequestSender.h b/CleverTapSDK/CTRequestSender.h index aaf5dd50..e939df61 100644 --- a/CleverTapSDK/CTRequestSender.h +++ b/CleverTapSDK/CTRequestSender.h @@ -14,8 +14,6 @@ #endif @interface CTRequestSender : NSObject -//+ (instancetype _Nullable)sharedInstance; -//- (void)setUpUrlSession; - (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig *_Nonnull)config redirectDomain:(NSString* _Nonnull)redirectDomain; - (void)send:(CTRequest *_Nonnull)ctRequest; diff --git a/CleverTapSDK/CTRequestSender.m b/CleverTapSDK/CTRequestSender.m index 538b23bf..2795d8cd 100644 --- a/CleverTapSDK/CTRequestSender.m +++ b/CleverTapSDK/CTRequestSender.m @@ -31,23 +31,10 @@ @interface CTRequestSender () @implementation CTRequestSender -//+ (instancetype)sharedInstance { -// static CTRequestSender *instance = nil; -// static dispatch_once_t onceToken; -// dispatch_once(&onceToken, ^{ -// instance = [[self alloc] init]; -// }); -// return instance; -//} - - (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig *_Nonnull)config redirectDomain:(NSString* _Nonnull)redirectDomain { if ((self = [super init])) { self.config = config; -//#if CLEVERTAP_SSL_PINNING -// // Only pin anchor/CA certificates -// _sslCertNames = @[@"AmazonRootCA1"]; -//#endif self.redirectDomain = redirectDomain; [self setUpUrlSession]; diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index bd7c0c64..1ccad216 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -92,8 +92,6 @@ NSString *const kQUEUE_NAME_EVENTS = @"events"; NSString *const kQUEUE_NAME_NOTIFICATIONS = @"notifications"; -NSString *const kHANDSHAKE_URL = @"https://eu1.clevertap-prod.com/hello"; - NSString *const kREDIRECT_DOMAIN_KEY = @"CLTAP_REDIRECT_DOMAIN_KEY"; NSString *const kREDIRECT_NOTIF_VIEWED_DOMAIN_KEY = @"CLTAP_REDIRECT_NOTIF_VIEWED_DOMAIN_KEY"; NSString *const kMUTED_TS_KEY = @"CLTAP_MUTED_TS_KEY"; From 0a6c387f345034b1d384b77aecee4591bf4ece3b Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Tue, 28 Mar 2023 13:29:06 +0300 Subject: [PATCH 39/86] Refactor unflatten, add unit tests --- CleverTapSDK.xcodeproj/project.pbxproj | 4 + CleverTapSDK/ProductExperiences/CTVariables.m | 22 +- CleverTapSDKTests/CTVarTests.m | 42 --- CleverTapSDKTests/CTVariablesTest.m | 294 ++++++++++++++++++ 4 files changed, 308 insertions(+), 54 deletions(-) create mode 100644 CleverTapSDKTests/CTVariablesTest.m diff --git a/CleverTapSDK.xcodeproj/project.pbxproj b/CleverTapSDK.xcodeproj/project.pbxproj index 41f8349d..5917f5ce 100644 --- a/CleverTapSDK.xcodeproj/project.pbxproj +++ b/CleverTapSDK.xcodeproj/project.pbxproj @@ -250,6 +250,7 @@ 583D9573DCDA5D219016B990 /* libPods-shared-CleverTapSDKTestsApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2108AFE9090417CC69A0E3EB /* libPods-shared-CleverTapSDKTestsApp.a */; }; 63359A5791C468263B934E22 /* libPods-shared-CleverTapSDKTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 917B24847F7C2A8B0825AEC1 /* libPods-shared-CleverTapSDKTests.a */; }; 6A2E0B9129CCCC8600FCEA5F /* ContentMergerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */; }; + 6A2E0B9329D0A5CF00FCEA5F /* CTVariablesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A2E0B9229D0A5CE00FCEA5F /* CTVariablesTest.m */; }; 6A2E4C18291E8A4A00385536 /* CleverTapInstanceConfigTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */; }; 6A775C3329BE78C7007790E0 /* CTVariables.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A775C3129BE78C7007790E0 /* CTVariables.h */; settings = {ATTRIBUTES = (Private, ); }; }; 6A775C3429BE78C7007790E0 /* CTVariables.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A775C3129BE78C7007790E0 /* CTVariables.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -647,6 +648,7 @@ 5709005227FD8E1E0011B89F /* CleverTap+SCDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CleverTap+SCDomain.h"; sourceTree = ""; }; 57EDC7A02683845B001DD157 /* CleverTap+InAppNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CleverTap+InAppNotifications.h"; sourceTree = ""; }; 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContentMergerTest.m; sourceTree = ""; }; + 6A2E0B9229D0A5CE00FCEA5F /* CTVariablesTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVariablesTest.m; sourceTree = ""; }; 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CleverTapInstanceConfigTests.m; sourceTree = ""; }; 6A775C3129BE78C7007790E0 /* CTVariables.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTVariables.h; sourceTree = ""; }; 6A775C3229BE78C7007790E0 /* CTVariables.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVariables.m; sourceTree = ""; }; @@ -1076,6 +1078,7 @@ 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */, 4EED219A29AF6368006CEA19 /* CTVarTests.m */, 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */, + 6A2E0B9229D0A5CE00FCEA5F /* CTVariablesTest.m */, ); path = CleverTapSDKTests; sourceTree = ""; @@ -1862,6 +1865,7 @@ 4E1F155B276B662C009387AE /* EventDetail.m in Sources */, 4EED219B29AF6368006CEA19 /* CTVarTests.m in Sources */, 6A2E0B9129CCCC8600FCEA5F /* ContentMergerTest.m in Sources */, + 6A2E0B9329D0A5CF00FCEA5F /* CTVariablesTest.m in Sources */, D02AC2DB276044F70031C1BE /* CleverTapSDKTests.m in Sources */, D02AC2EB2767F4590031C1BE /* BaseTestCase.m in Sources */, 4E1F15532769B83D009387AE /* EventTests.m in Sources */, diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 1851dade..5fc937b8 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -216,25 +216,23 @@ - (NSDictionary*)unflatten:(NSDictionary*)flatDictionary { [flatDictionary enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { if ([key containsString:@"."]) { NSArray *components = [self.varCache getNameComponents:key]; - long namePosition = components.count - 1; NSMutableDictionary *currentMap = unflattenVars; + NSString *lastComponent = [components lastObject]; - for (int i = 0; i < components.count; i++) { + for (int i = 0; i < components.count - 1; i++) { NSString *component = components[i]; - if (i == namePosition) { - currentMap[component] = value; + if (!currentMap[component]) { + NSMutableDictionary *nestedMap = [NSMutableDictionary dictionary]; + currentMap[component] = nestedMap; + currentMap = nestedMap; } else { - if (!currentMap[component]) { - NSMutableDictionary *nestedMap = [NSMutableDictionary dictionary]; - currentMap[component] = nestedMap; - currentMap = nestedMap; - } - else { - currentMap = ((NSMutableDictionary*)currentMap[component]); - } + currentMap = ((NSMutableDictionary*)currentMap[component]); } } + if ([currentMap isKindOfClass:[NSMutableDictionary class]]) { + currentMap[lastComponent] = value; + } } else { unflattenVars[key] = value; diff --git a/CleverTapSDKTests/CTVarTests.m b/CleverTapSDKTests/CTVarTests.m index 5d1e2358..bc5818d2 100644 --- a/CleverTapSDKTests/CTVarTests.m +++ b/CleverTapSDKTests/CTVarTests.m @@ -176,46 +176,4 @@ - (void)testSyncVarsPayload { XCTAssertEqualObjects(titleMap[@"type"],definedVar.kind); } -- (void)testVariablesFlatten { - - NSString *varName = @"EmployeeMap"; - NSDictionary *employeeMap = @{ - @"Team": @{ - @"TeamName": @"Testing", - @"Designation": @"Tester" - }, - @"Name": @"Niko", - @"EmployeeID": @123 - }; - NSDictionary *flattenedEmployeeMap = @{ - @"EmployeeMap.Team.TeamName": @{ @"defaultValue": @"Testing" }, - @"EmployeeMap.Team.Designation": @{ @"defaultValue": @"Tester" }, - @"EmployeeMap.Name": @{ @"defaultValue": @"Niko" }, - @"EmployeeMap.EmployeeID": @{ @"defaultValue": @123 } - }; - NSDictionary *result = [variables flatten:employeeMap varName:varName]; - XCTAssertEqualObjects(result,flattenedEmployeeMap); -} - -- (void)testVariablesUnflatten { - NSDictionary *flattenedEmployeeMap = @{ - @"EmployeeMap.Team.TeamName": @{ @"defaultValue": @"Testing" }, - @"EmployeeMap.Team.Designation": @{ @"defaultValue": @"Tester" }, - @"EmployeeMap.Name": @{ @"defaultValue": @"Niko" }, - @"EmployeeMap.EmployeeID": @{ @"defaultValue": @123 } - }; - NSDictionary *unflattenedEmployeeMap = @{ - @"EmployeeMap": @{ - @"Team": @{ - @"TeamName": @{ @"defaultValue": @"Testing" }, - @"Designation": @{ @"defaultValue": @"Tester" } - }, - @"Name": @{ @"defaultValue": @"Niko" }, - @"EmployeeID": @{ @"defaultValue": @123 } - } - }; - NSDictionary *result = [variables unflatten:flattenedEmployeeMap]; - XCTAssertEqualObjects(result,unflattenedEmployeeMap); -} - @end diff --git a/CleverTapSDKTests/CTVariablesTest.m b/CleverTapSDKTests/CTVariablesTest.m new file mode 100644 index 00000000..d8d207a0 --- /dev/null +++ b/CleverTapSDKTests/CTVariablesTest.m @@ -0,0 +1,294 @@ +// +// CTVariablesTest.m +// CleverTapSDKTests +// +// Created by Nikola Zagorchev on 26.03.23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import +#import "CTVariables.h" + +@interface CTVariablesTest : XCTestCase + +@property(strong, nonatomic) CTVariables *variables; + +@end + +@implementation CTVariablesTest + +- (void)setUp { + CleverTapInstanceConfig *config = [[CleverTapInstanceConfig alloc] initWithAccountId:@"id" accountToken:@"token" accountRegion:@"eu"]; + CTDeviceInfo *deviceInfo = [[CTDeviceInfo alloc] initWithConfig:config andCleverTapID:@"test"]; + _variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo]; +} + +- (void)tearDown { + _variables = nil; +} + +#pragma mark Unflatten Variables +- (void)testUnflattenVariables { + NSDictionary *flat = @{ + @"a.b.c.d": @"d value", + @"a.b.c.dd": @"dd value", + @"a.e": @"e value", + @"a.b.bb": @"bb value", + }; + NSDictionary *expected = @{ + @"a": @{ + @"b": @{ + @"c": @{ + @"d": @"d value", + @"dd": @"dd value" + }, + @"bb": @"bb value" + }, + @"e": @"e value" + } + }; + NSDictionary *result = [self.variables unflatten:flat]; + XCTAssertEqualObjects(result, expected); +} + +- (void)testUnflattenWithFlatInput { + NSDictionary *inputDict = @{ + @"a": @"value1", + @"b": @123 + }; + + NSDictionary *expectedOutput = @{ + @"a": @"value1", + @"b": @123 + }; + + NSDictionary *actualOutput = [self.variables unflatten:inputDict]; + XCTAssertEqualObjects(actualOutput, expectedOutput); +} + +- (void)testUnflattenWithDictionaryInput { + NSDictionary *inputDict = @{ + @"testVarName.a.b": @{ + @"defaultValue": @"value1" + }, + @"testVarName.a.c.d": @{ + @"defaultValue": @"value2" + }, + @"testVarName.e": @{ + @"defaultValue": @"value3" + }, + @"testVarName.f": @{ + @"defaultValue": @"value4" + } + }; + + NSDictionary *expectedOutput = @{ + @"testVarName": @{ + @"a": @{ + @"b": @{ + @"defaultValue": @"value1" + }, + @"c": @{ + @"d": @{ + @"defaultValue": @"value2" + } + } + }, + @"e": @{ + @"defaultValue": @"value3" + }, + @"f": @{ + @"defaultValue": @"value4" + } + } + }; + + NSDictionary *actualOutput = [self.variables unflatten:inputDict]; + XCTAssertEqualObjects(actualOutput, expectedOutput); +} + +- (void)testUnflattenWithEmptyInput { + NSDictionary *inputDict = @{}; + + NSDictionary *expectedOutput = @{}; + + NSDictionary *actualOutput = [self.variables unflatten:inputDict]; + XCTAssertEqualObjects(actualOutput, expectedOutput); +} + +- (void)testUnflattenWithInvalidDictionary { + NSDictionary *inputDict = @{ + @"a.b.c.d": @"d value", + @"a.b.c": @"c value", + @"a.e": @"e value", + @"a.b": @"b value", + }; + + NSDictionary *expectedOutput = @{ + @"a": @{ + @"b": @"b value", + @"e": @"e value" + } + }; + + NSDictionary *actualOutput = [self.variables unflatten:inputDict]; + XCTAssertEqualObjects(actualOutput, expectedOutput); +} + +- (void)testUnflattenWithInvalidDictionaryDifferentOrder { + NSDictionary *inputDict = @{ + @"a.b.c": @"c value", + @"a.b.c.d": @"d value", + @"a.e": @"e value", + @"a.b": @"b value", + }; + + NSDictionary *expectedOutput = @{ + @"a": @{ + @"b": @"b value", + @"e": @"e value" + } + }; + + NSDictionary *actualOutput = [self.variables unflatten:inputDict]; + XCTAssertEqualObjects(actualOutput, expectedOutput); +} + +- (void)testUnflattenWithInvalidInputArray { + NSDictionary *inputDict = @{ + @"a": @[ @"value2" ] + }; + + NSDictionary *expectedOutput = @{ + @"a": @[ @"value2" ] + }; + + NSDictionary *actualOutput = [self.variables unflatten:inputDict]; + XCTAssertEqualObjects(actualOutput, expectedOutput); +} + +#pragma mark Flatten Variables + +- (void)testFlatten { + NSDictionary *inputDict = @{ + @"Team": @{ + @"TeamName": @"Testing", + @"Designation": @"Tester" + }, + @"Name": @"CleverTap", + @"EmployeeID": @123 + }; + + NSString *varName = @"Employee"; + NSDictionary *expected = @{ + @"Employee.Team.TeamName": @{ + @"defaultValue": @"Testing" + }, + @"Employee.Team.Designation": @{ + @"defaultValue": @"Tester" + }, + @"Employee.Name": @{ + @"defaultValue": @"CleverTap" + }, + @"Employee.EmployeeID": @{ + @"defaultValue": @123 + } + }; + + NSDictionary *result = [self.variables flatten:inputDict varName:varName]; + XCTAssertEqualObjects(result, expected); +} + +- (void)testFlattenWithMultipleDictionaries { + NSDictionary *inputDict = @{ + @"a": @{ + @"b": @"value1", + @"c": @{ + @"d": @"value2", + @"e": @{ + @"f": @"value3", + @"g": @"value4" + }, + @"h": @"value5" + }, + @"i": @"value6" + }, + @"j": @"value7", + @"k": @{ + @"l": @"value8", + @"m": @{ + @"n": @"value9" + } + } + }; + + NSString *varName = @"testVarName"; + NSDictionary *expected = @{ + @"testVarName.a.b": @{ + @"defaultValue": @"value1" + }, + @"testVarName.a.c.d": @{ + @"defaultValue": @"value2" + }, + @"testVarName.a.c.e.f": @{ + @"defaultValue": @"value3" + }, + @"testVarName.a.c.e.g": @{ + @"defaultValue": @"value4" + }, + @"testVarName.a.c.h": @{ + @"defaultValue": @"value5" + }, + @"testVarName.a.i": @{ + @"defaultValue": @"value6" + }, + @"testVarName.j": @{ + @"defaultValue": @"value7" + }, + @"testVarName.k.l": @{ + @"defaultValue": @"value8" + }, + @"testVarName.k.m.n": @{ + @"defaultValue": @"value9" + }, + }; + + NSDictionary *actual = [self.variables flatten:inputDict varName:varName]; + XCTAssertEqualObjects(actual, expected); +} + +- (void)testFlattenWithEmptyInput { + NSDictionary *inputDict = @{}; + + NSString *varName = @"testVarName"; + NSDictionary *expectedOutput = @{}; + + NSDictionary *actualOutput = [self.variables flatten:inputDict varName:varName]; + XCTAssertEqualObjects(actualOutput, expectedOutput); +} + +- (void)testFlattenWithInvalidInputArray { + NSDictionary *inputDict = @{ + @"a": @"value1", + @"b": @123, + @"c": @[ @"value2" ] + }; + + NSString *varName = @"testVarName"; + // The array will be dropped + NSDictionary *expectedOutput = @{ + @"testVarName.a": @{ + @"defaultValue": @"value1" + }, + @"testVarName.b": @{ + @"defaultValue": @123 + } + }; + + NSDictionary *actualOutput = [self.variables flatten:inputDict varName:varName]; + XCTAssertEqualObjects(actualOutput, expectedOutput); +} + +@end + From c18f99b4c4132ee899baf4796519d608f3354f1f Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Tue, 28 Mar 2023 21:18:48 +0300 Subject: [PATCH 40/86] Add syncVars tests --- CleverTapSDKTests/CTVariablesTest.m | 216 +++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 2 deletions(-) diff --git a/CleverTapSDKTests/CTVariablesTest.m b/CleverTapSDKTests/CTVariablesTest.m index d8d207a0..62176de1 100644 --- a/CleverTapSDKTests/CTVariablesTest.m +++ b/CleverTapSDKTests/CTVariablesTest.m @@ -9,6 +9,7 @@ #import #import #import "CTVariables.h" +#import "CTConstants.h" @interface CTVariablesTest : XCTestCase @@ -16,16 +17,227 @@ @interface CTVariablesTest : XCTestCase @end +@interface CTVarCacheMock : CTVarCache +@end + +@implementation CTVarCacheMock + +- (void)loadDiffs { + // Do NOT read from file +} + +- (void)saveDiffs { + // Do NOT save to file +} + +@end + +@implementation CTVariables (Mock) + +- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo *)deviceInfo varCache: (CTVarCacheMock *)varCache { + self = [super init]; + if (self) { + self.varCache = varCache; + } + return self; +} + +@end + @implementation CTVariablesTest - (void)setUp { CleverTapInstanceConfig *config = [[CleverTapInstanceConfig alloc] initWithAccountId:@"id" accountToken:@"token" accountRegion:@"eu"]; CTDeviceInfo *deviceInfo = [[CTDeviceInfo alloc] initWithConfig:config andCleverTapID:@"test"]; - _variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo]; + CTVarCacheMock *varCache = [[CTVarCacheMock alloc] initWithConfig:config deviceInfo:deviceInfo]; + self.variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo varCache:varCache]; } - (void)tearDown { - _variables = nil; + self.variables = nil; +} + +- (void)testVarCacheNotNil { + XCTAssertNotNil(self.variables.varCache); +} + +#pragma mark Sync Variables +- (void)testSyncVars { + NSString *varName = @"var1"; + NSString *varValue = @"value1"; + + CTVar *definedVar = [self.variables define:varName with:varValue kind:CT_KIND_STRING]; + + NSDictionary *payload = [self.variables varsPayload]; + + XCTAssertEqualObjects(payload[@"type"], CT_PE_VARS_PAYLOAD_TYPE); + NSDictionary *vars = [payload objectForKey:CT_PE_VARS_PAYLOAD_KEY]; + NSDictionary *titleMap = [vars objectForKey:varName]; + XCTAssertEqualObjects(titleMap[CT_PE_DEFAULT_VALUE], varValue); + XCTAssertEqualObjects(titleMap[CT_PE_VAR_TYPE], definedVar.kind); +} + +- (void)testSyncVarsComplex { + [self.variables define:@"var1" with:@"value1" kind:CT_KIND_STRING]; + [self.variables define:@"var2.var22" with:@"value2" kind:CT_KIND_STRING]; + [self.variables define:@"var3" with:@YES kind:CT_KIND_BOOLEAN]; + [self.variables define:@"var4" with:@1234 kind:CT_KIND_INT]; + [self.variables define:@"var5" with:@12.34 kind:CT_KIND_FLOAT]; + [self.variables define:@"var6" with:@{ + @"var7": @"value7", + @"var8": @"value8" + } kind:CT_KIND_DICTIONARY]; + + NSDictionary *expected = @{ + @"type": @"varsPayload", + @"vars": @{ + @"var1": @{ + @"defaultValue": @"value1", + @"type": @"string" + }, + @"var2.var22": @{ + @"defaultValue": @"value2", + @"type": @"string" + }, + @"var3": @{ + @"defaultValue": @1, + @"type": @"boolean" + }, + @"var4": @{ + @"defaultValue": @1234, + @"type": @"number" + }, + @"var5": @{ + @"defaultValue": @12.34, + @"type": @"number" + }, + @"var6.var7": @{ + @"defaultValue": @"value7", + }, + @"var6.var8": @{ + @"defaultValue": @"value8", + }, + } + }; + + NSDictionary *actual = [self.variables varsPayload]; + XCTAssertEqualObjects(actual, expected); +} + +- (void)testSyncVarsWithDots { + [self.variables define:@"var1.var2" with:@"value2" kind:CT_KIND_STRING]; + [self.variables define:@"var1.var3" with:@"value3" kind:CT_KIND_STRING]; + [self.variables define:@"var1.var4.var5" with:@YES kind:CT_KIND_BOOLEAN]; + [self.variables define:@"var1.var4.var6" with:@1234 kind:CT_KIND_INT]; + [self.variables define:@"var7.var8" with:@12.34 kind:CT_KIND_FLOAT]; + + NSDictionary *expected = @{ + @"type": @"varsPayload", + @"vars": @{ + @"var1.var2": @{ + @"defaultValue": @"value2", + @"type": @"string" + }, + @"var1.var3": @{ + @"defaultValue": @"value3", + @"type": @"string" + }, + @"var1.var4.var5": @{ + @"defaultValue": @1, + @"type": @"boolean" + }, + @"var1.var4.var6": @{ + @"defaultValue": @1234, + @"type": @"number" + }, + @"var7.var8": @{ + @"defaultValue": @12.34, + @"type": @"number" + } + } + }; + + NSDictionary *actual = [self.variables varsPayload]; + XCTAssertEqualObjects(actual, expected); +} + +- (void)testSyncVarsWithDotsAndDictionaries { + [self.variables define:@"var1.var2" with:@"value2" kind:CT_KIND_STRING]; + [self.variables define:@"var1.var4" with:@{ + @"var5": @NO, + @"var6": @1234 + } kind:CT_KIND_DICTIONARY]; + [self.variables define:@"var7.var8" with:@"value8" kind:CT_KIND_STRING]; + [self.variables define:@"var7" with:@{ + @"var9": @12.34 + } kind:CT_KIND_DICTIONARY]; + [self.variables define:@"var7.var10" with:@"value10" kind:CT_KIND_STRING]; + + + NSDictionary *expected = @{ + @"type": @"varsPayload", + @"vars": @{ + @"var1.var2": @{ + @"defaultValue": @"value2", + @"type": @"string" + }, + @"var1.var4.var5": @{ + @"defaultValue": @0 + }, + @"var1.var4.var6": @{ + @"defaultValue": @1234 + }, + @"var7.var8": @{ + @"defaultValue": @"value8", + @"type": @"string" + }, + @"var7.var9": @{ + @"defaultValue": @12.34 + }, + @"var7.var10": @{ + @"defaultValue": @"value10", + @"type": @"string" + } + } + }; + + NSDictionary *actual = [self.variables varsPayload]; + XCTAssertEqualObjects(actual, expected); +} + +- (void)testSyncVarsWithInvalidArray { + [self.variables define:@"var1" with:@"value1" kind:CT_KIND_STRING]; + [self.variables define:@"var2" with:@{ + @"var3": @[ @"arr" ], + @"var4": @"value4" + } kind:CT_KIND_DICTIONARY]; + + // The array will be dropped + NSDictionary *expected = @{ + @"type": @"varsPayload", + @"vars": @{ + @"var1": @{ + @"defaultValue": @"value1", + @"type": @"string" + }, + @"var2.var4": @{ + @"defaultValue": @"value4" + } + } + }; + + NSDictionary *actual = [self.variables varsPayload]; + XCTAssertEqualObjects(actual, expected); +} + +- (void)testSyncVarsWithEmpty { + NSDictionary *expected = @{ + @"type": @"varsPayload", + @"vars": @{} + }; + + NSDictionary *actual = [self.variables varsPayload]; + XCTAssertEqualObjects(actual, expected); } #pragma mark Unflatten Variables From d78ed5781c01f2ae71447a767c536a9519957358 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 29 Mar 2023 16:02:23 +0300 Subject: [PATCH 41/86] Rename FCU to fetchVariables --- CleverTapSDK/CTConstants.h | 1 - CleverTapSDK/CleverTap.h | 2 +- CleverTapSDK/CleverTap.m | 6 +++--- CleverTapSDK/ProductExperiences/CTVar.h | 2 +- CleverTapSDK/ProductExperiences/CTVariables.h | 6 +++--- CleverTapSDK/ProductExperiences/CTVariables.m | 11 ++++++----- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/CleverTapSDK/CTConstants.h b/CleverTapSDK/CTConstants.h index 26ea3272..926ff9ba 100644 --- a/CleverTapSDK/CTConstants.h +++ b/CleverTapSDK/CTConstants.h @@ -8,7 +8,6 @@ extern NSString *CT_KIND_FLOAT; extern NSString *CT_KIND_STRING; extern NSString *CT_KIND_BOOLEAN; extern NSString *CT_KIND_DICTIONARY; -extern NSString *CT_KIND_ARRAY; extern NSString *CLEVERTAP_DEFAULTS_VARIABLES_KEY; extern NSString *CLEVERTAP_DEFAULTS_VARS_JSON_KEY; diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index dcdb1514..a56e43d9 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -1341,7 +1341,7 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; @param block a callback with a boolean flag whether the update was successful. */ -- (void)forceContentUpdate:(CleverTapForceContentUpdateBlock _Nullable)block; +- (void)fetchVariables:(CleverTapFetchVariablesBlock _Nullable)block; @end diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 1ccad216..d0bb0fc7 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -2827,7 +2827,7 @@ - (void)sendQueue:(NSMutableArray *)queue { if (error) { CleverTapLogDebug(self.config.logLevel, @"%@: Network error while sending queue, will retry: %@", self, error.localizedDescription); } - [[self variables] triggerForceContentUpdate:NO]; + [[self variables] triggerFetchVariables:NO]; dispatch_semaphore_signal(semaphore); }]; [self.requestSender send:ctRequest]; @@ -5120,8 +5120,8 @@ - (void)_syncVars { dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); } -- (void)forceContentUpdate:(CleverTapForceContentUpdateBlock)block { - [[self variables] setForceContentUpdateBlock:block]; +- (void)fetchVariables:(CleverTapFetchVariablesBlock)block { + [[self variables] setFetchVariablesBlock:block]; [self queueEvent:@{@"evtName": CLTAP_WZRK_FETCH_EVENT, @"evtData" : @{@"t": @4}} withType:CleverTapEventTypeFetch]; } diff --git a/CleverTapSDK/ProductExperiences/CTVar.h b/CleverTapSDK/ProductExperiences/CTVar.h index 2e3ca220..656d3756 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.h +++ b/CleverTapSDK/ProductExperiences/CTVar.h @@ -4,7 +4,7 @@ NS_ASSUME_NONNULL_BEGIN typedef void (^CleverTapVariablesChangedBlock)(void); -typedef void (^CleverTapForceContentUpdateBlock)(BOOL success); +typedef void (^CleverTapFetchVariablesBlock)(BOOL success); @class CTVar; diff --git a/CleverTapSDK/ProductExperiences/CTVariables.h b/CleverTapSDK/ProductExperiences/CTVariables.h index 29db46bb..76874c3a 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.h +++ b/CleverTapSDK/ProductExperiences/CTVariables.h @@ -16,9 +16,9 @@ NS_ASSUME_NONNULL_BEGIN @interface CTVariables : NSObject @property(strong, nonatomic) CTVarCache *varCache; -@property(strong, nonatomic, nullable) CleverTapForceContentUpdateBlock forceContentUpdateBlock; +@property(strong, nonatomic, nullable) CleverTapFetchVariablesBlock fetchVariablesBlock; -- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo; +- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo *)deviceInfo; - (CTVar *)define:(NSString *)name with:(nullable NSObject *)defaultValue @@ -27,7 +27,7 @@ NS_SWIFT_NAME(define(name:value:kind:)); - (CTVar *)getVariable:(NSString *)name; - (void)handleVariablesResponse:(NSDictionary *)varsResponse; -- (void)triggerForceContentUpdate:(BOOL)success; +- (void)triggerFetchVariables:(BOOL)success; - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; - (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName; diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 5fc937b8..3e78700a 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -66,13 +66,14 @@ - (void)handleVariablesResponse:(NSDictionary *)varsResponse [[self varCache] setAppLaunchedRecorded:YES]; NSDictionary *values = [self unflatten:varsResponse]; [[self varCache] applyVariableDiffs:values]; - [self triggerForceContentUpdate:YES]; + [self triggerFetchVariables:YES]; } } -- (void)triggerForceContentUpdate:(BOOL)success { - if (self.forceContentUpdateBlock) { - CleverTapForceContentUpdateBlock block = [self.forceContentUpdateBlock copy]; +// TODO: callback is overridden after second call if first is not ready yet +- (void)triggerFetchVariables:(BOOL)success { + if (self.fetchVariablesBlock) { + CleverTapFetchVariablesBlock block = [self.fetchVariablesBlock copy]; if (![NSThread isMainThread]) { dispatch_async(dispatch_get_main_queue(), ^{ block(success); @@ -81,7 +82,7 @@ - (void)triggerForceContentUpdate:(BOOL)success { block(success); } - self.forceContentUpdateBlock = nil; + self.fetchVariablesBlock = nil; } } From e120c9b5ee56a1d71c2cab4a78bf416162311797 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 29 Mar 2023 16:04:14 +0300 Subject: [PATCH 42/86] Refactor CTVarCache interface --- CleverTapSDK/ProductExperiences/CTVarCache.h | 26 +++++++++----------- CleverTapSDK/ProductExperiences/CTVarCache.m | 9 +++++-- CleverTapSDKTests/CTVarCache+Tests.h | 2 ++ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.h b/CleverTapSDK/ProductExperiences/CTVarCache.h index 35f19427..cc131db3 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.h +++ b/CleverTapSDK/ProductExperiences/CTVarCache.h @@ -10,28 +10,26 @@ typedef void (^CacheUpdateBlock)(void); NS_SWIFT_NAME(VarCache) @interface CTVarCache : NSObject -- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo; +- (instancetype)init NS_UNAVAILABLE; -// Handling variables. -- (NSArray *)getNameComponents:(NSString *)name; -- (void)loadDiffs; -- (void)saveDiffs; +- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo; -- (void)registerVariable:(CTVar *)var; -- (nullable CTVar *)getVariable:(NSString *)name; +@property (assign, nonatomic) BOOL silent; +@property (strong, nonatomic) NSMutableDictionary *vars; +@property (assign, nonatomic) BOOL appLaunchedRecorded; -// Handling values. -- (nullable id)getValueFromComponentArray:(NSArray *) components fromDict:(NSDictionary *)values; -- (nullable id)getMergedValueFromComponentArray:(NSArray *) components; - (nullable NSDictionary *)diffs; +- (void)loadDiffs; - (BOOL)hasReceivedDiffs; - (void)applyVariableDiffs:(nullable NSDictionary *)diffs_; - (void)onUpdate:(CacheUpdateBlock)block; -- (void)setSilent:(BOOL)silent; -- (BOOL)silent; -@property (strong, nonatomic) NSMutableDictionary *vars; -@property (assign, nonatomic) BOOL appLaunchedRecorded; +- (void)registerVariable:(CTVar *)var; +- (nullable CTVar *)getVariable:(NSString *)name; +- (id)getMergedValue:(NSString *)name; + +- (NSArray *)getNameComponents:(NSString *)name; +- (nullable id)getMergedValueFromComponentArray:(NSArray *) components; @end diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 438da4ef..d2e19b34 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -13,8 +13,6 @@ @interface CTVarCache() @property (strong, nonatomic) CacheUpdateBlock updateBlock; @property (assign, nonatomic) BOOL hasReceivedDiffs; -@property (assign, nonatomic) BOOL silent; - @property (nonatomic, strong) CleverTapInstanceConfig *config; @property (nonatomic, strong) CTDeviceInfo *deviceInfo; @end @@ -182,6 +180,13 @@ - (CTVar *)getVariable:(NSString *)name return [self.vars objectForKey:name]; } +// TODO: expose through CleverTap +- (id)getMergedValue:(NSString *)name +{ + NSArray *components = [self getNameComponents:name]; + return [self getMergedValueFromComponentArray:components]; +} + - (id)getValueFromComponentArray:(NSArray *) components fromDict:(NSDictionary *)values { id mergedPtr = values; diff --git a/CleverTapSDKTests/CTVarCache+Tests.h b/CleverTapSDKTests/CTVarCache+Tests.h index aad78ae3..4d6440de 100644 --- a/CleverTapSDKTests/CTVarCache+Tests.h +++ b/CleverTapSDKTests/CTVarCache+Tests.h @@ -12,6 +12,8 @@ NS_ASSUME_NONNULL_BEGIN @interface CTVarCache (Tests) - (NSString *)getArchiveFileName; +- (id)traverse:(id)collection withKey:(id)key autoInsert:(BOOL)autoInsert; +- (void)saveDiffs; @end NS_ASSUME_NONNULL_END From d5cceddc4f250cd13ce3e9ba10aa04255bf1b26f Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 29 Mar 2023 23:24:55 +0300 Subject: [PATCH 43/86] Expose getVariable and getVariableValue --- CleverTapSDK/CleverTap.h | 4 ++++ CleverTapSDK/CleverTap.m | 13 +++++++++++++ CleverTapSDK/ProductExperiences/CTVarCache.m | 1 - CleverTapSDK/ProductExperiences/CTVariables.h | 1 - CleverTapSDK/ProductExperiences/CTVariables.m | 9 --------- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index a56e43d9..9fcc3b1a 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -1343,6 +1343,10 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; */ - (void)fetchVariables:(CleverTapFetchVariablesBlock _Nullable)block; +- (CTVar * _Nullable)getVariable:(NSString * _Nonnull)name; + +- (id _Nullable)getVariableValue:(NSString * _Nonnull)name; + @end #pragma clang diagnostic pop diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index d0bb0fc7..2544944f 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -5125,6 +5125,19 @@ - (void)fetchVariables:(CleverTapFetchVariablesBlock)block { [self queueEvent:@{@"evtName": CLTAP_WZRK_FETCH_EVENT, @"evtData" : @{@"t": @4}} withType:CleverTapEventTypeFetch]; } +- (CTVar * _Nullable)getVariable:(NSString * _Nonnull)name { + CTVar *var = [[self.variables varCache] getVariable:name]; + if (!var) { + CleverTapLogDebug(self.config.logLevel, @"%@: Variable with name: %@ not found.", self, name); + } + return var; +} + +- (id _Nullable)getVariableValue:(NSString * _Nonnull)name { + // TODO: return a copy + return [[self.variables varCache] getVariable:name]; +} + #pragma mark - PE Vars - (CTVar *)defineVar:(NSString *)name { diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index d2e19b34..a38445bd 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -180,7 +180,6 @@ - (CTVar *)getVariable:(NSString *)name return [self.vars objectForKey:name]; } -// TODO: expose through CleverTap - (id)getMergedValue:(NSString *)name { NSArray *components = [self getNameComponents:name]; diff --git a/CleverTapSDK/ProductExperiences/CTVariables.h b/CleverTapSDK/ProductExperiences/CTVariables.h index 76874c3a..25c55655 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.h +++ b/CleverTapSDK/ProductExperiences/CTVariables.h @@ -25,7 +25,6 @@ NS_ASSUME_NONNULL_BEGIN kind:(nullable NSString *)kind NS_SWIFT_NAME(define(name:value:kind:)); -- (CTVar *)getVariable:(NSString *)name; - (void)handleVariablesResponse:(NSDictionary *)varsResponse; - (void)triggerFetchVariables:(BOOL)success; - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 3e78700a..e3e9916a 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -51,15 +51,6 @@ - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString } } -- (CTVar *)getVariable:(NSString *)name -{ - CTVar *var = [self.varCache getVariable:name]; - if (!var) { - CleverTapLogDebug(self.config.logLevel, @"%@: Variable with name: %@ not found.", self, name); - } - return var; -} - - (void)handleVariablesResponse:(NSDictionary *)varsResponse { if (varsResponse) { From bb0be4be98d39c499eca09d465db655e1e0e60dc Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 29 Mar 2023 23:55:29 +0300 Subject: [PATCH 44/86] Add CTVarCache mock and CTVariables category --- CleverTapSDK.xcodeproj/project.pbxproj | 12 +++++++++++ CleverTapSDKTests/CTVarCacheMock.h | 18 ++++++++++++++++ CleverTapSDKTests/CTVarCacheMock.m | 21 +++++++++++++++++++ CleverTapSDKTests/CTVariables+Tests.h | 16 ++++++++++++++ CleverTapSDKTests/CTVariables+Tests.m | 22 +++++++++++++++++++ CleverTapSDKTests/CTVariablesTest.m | 29 ++------------------------ 6 files changed, 91 insertions(+), 27 deletions(-) create mode 100644 CleverTapSDKTests/CTVarCacheMock.h create mode 100644 CleverTapSDKTests/CTVarCacheMock.m create mode 100644 CleverTapSDKTests/CTVariables+Tests.h create mode 100644 CleverTapSDKTests/CTVariables+Tests.m diff --git a/CleverTapSDK.xcodeproj/project.pbxproj b/CleverTapSDK.xcodeproj/project.pbxproj index 5917f5ce..66afa159 100644 --- a/CleverTapSDK.xcodeproj/project.pbxproj +++ b/CleverTapSDK.xcodeproj/project.pbxproj @@ -251,6 +251,8 @@ 63359A5791C468263B934E22 /* libPods-shared-CleverTapSDKTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 917B24847F7C2A8B0825AEC1 /* libPods-shared-CleverTapSDKTests.a */; }; 6A2E0B9129CCCC8600FCEA5F /* ContentMergerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */; }; 6A2E0B9329D0A5CF00FCEA5F /* CTVariablesTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A2E0B9229D0A5CE00FCEA5F /* CTVariablesTest.m */; }; + 6A2E0B9529D49D0200FCEA5F /* CTVariables+Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A2E0B9429D49D0200FCEA5F /* CTVariables+Tests.m */; }; + 6A2E0B9829D49D5100FCEA5F /* CTVarCacheMock.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A2E0B9729D49D5100FCEA5F /* CTVarCacheMock.m */; }; 6A2E4C18291E8A4A00385536 /* CleverTapInstanceConfigTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */; }; 6A775C3329BE78C7007790E0 /* CTVariables.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A775C3129BE78C7007790E0 /* CTVariables.h */; settings = {ATTRIBUTES = (Private, ); }; }; 6A775C3429BE78C7007790E0 /* CTVariables.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A775C3129BE78C7007790E0 /* CTVariables.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -649,6 +651,10 @@ 57EDC7A02683845B001DD157 /* CleverTap+InAppNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CleverTap+InAppNotifications.h"; sourceTree = ""; }; 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContentMergerTest.m; sourceTree = ""; }; 6A2E0B9229D0A5CE00FCEA5F /* CTVariablesTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVariablesTest.m; sourceTree = ""; }; + 6A2E0B9429D49D0200FCEA5F /* CTVariables+Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CTVariables+Tests.m"; sourceTree = ""; }; + 6A2E0B9629D49D5100FCEA5F /* CTVarCacheMock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTVarCacheMock.h; sourceTree = ""; }; + 6A2E0B9729D49D5100FCEA5F /* CTVarCacheMock.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVarCacheMock.m; sourceTree = ""; }; + 6A2E0B9929D49E4700FCEA5F /* CTVariables+Tests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CTVariables+Tests.h"; sourceTree = ""; }; 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CleverTapInstanceConfigTests.m; sourceTree = ""; }; 6A775C3129BE78C7007790E0 /* CTVariables.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTVariables.h; sourceTree = ""; }; 6A775C3229BE78C7007790E0 /* CTVariables.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVariables.m; sourceTree = ""; }; @@ -1079,6 +1085,10 @@ 4EED219A29AF6368006CEA19 /* CTVarTests.m */, 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */, 6A2E0B9229D0A5CE00FCEA5F /* CTVariablesTest.m */, + 6A2E0B9429D49D0200FCEA5F /* CTVariables+Tests.m */, + 6A2E0B9629D49D5100FCEA5F /* CTVarCacheMock.h */, + 6A2E0B9729D49D5100FCEA5F /* CTVarCacheMock.m */, + 6A2E0B9929D49E4700FCEA5F /* CTVariables+Tests.h */, ); path = CleverTapSDKTests; sourceTree = ""; @@ -1865,6 +1875,8 @@ 4E1F155B276B662C009387AE /* EventDetail.m in Sources */, 4EED219B29AF6368006CEA19 /* CTVarTests.m in Sources */, 6A2E0B9129CCCC8600FCEA5F /* ContentMergerTest.m in Sources */, + 6A2E0B9529D49D0200FCEA5F /* CTVariables+Tests.m in Sources */, + 6A2E0B9829D49D5100FCEA5F /* CTVarCacheMock.m in Sources */, 6A2E0B9329D0A5CF00FCEA5F /* CTVariablesTest.m in Sources */, D02AC2DB276044F70031C1BE /* CleverTapSDKTests.m in Sources */, D02AC2EB2767F4590031C1BE /* BaseTestCase.m in Sources */, diff --git a/CleverTapSDKTests/CTVarCacheMock.h b/CleverTapSDKTests/CTVarCacheMock.h new file mode 100644 index 00000000..443e789b --- /dev/null +++ b/CleverTapSDKTests/CTVarCacheMock.h @@ -0,0 +1,18 @@ +// +// CTVarCacheMock.h +// CleverTapSDKTests +// +// Created by Nikola Zagorchev on 29.03.23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import "CTVarCache.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface CTVarCacheMock : CTVarCache + +@end + +NS_ASSUME_NONNULL_END diff --git a/CleverTapSDKTests/CTVarCacheMock.m b/CleverTapSDKTests/CTVarCacheMock.m new file mode 100644 index 00000000..89100b1d --- /dev/null +++ b/CleverTapSDKTests/CTVarCacheMock.m @@ -0,0 +1,21 @@ +// +// CTVarCacheMock.m +// CleverTapSDKTests +// +// Created by Nikola Zagorchev on 29.03.23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import "CTVarCacheMock.h" + +@implementation CTVarCacheMock + +- (void)loadDiffs { + // Do NOT read from file +} + +- (void)saveDiffs { + // Do NOT save to file +} + +@end diff --git a/CleverTapSDKTests/CTVariables+Tests.h b/CleverTapSDKTests/CTVariables+Tests.h new file mode 100644 index 00000000..662e3505 --- /dev/null +++ b/CleverTapSDKTests/CTVariables+Tests.h @@ -0,0 +1,16 @@ +// +// CTVariables+Tests.h +// CleverTapSDK +// +// Created by Nikola Zagorchev on 29.03.23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import "CTVariables.h" +#import "CTVarCacheMock.h" + +@interface CTVariables (Tests) + +- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo *)deviceInfo varCache: (CTVarCacheMock *)varCache; + +@end diff --git a/CleverTapSDKTests/CTVariables+Tests.m b/CleverTapSDKTests/CTVariables+Tests.m new file mode 100644 index 00000000..0542d2c8 --- /dev/null +++ b/CleverTapSDKTests/CTVariables+Tests.m @@ -0,0 +1,22 @@ +// +// CTVariables+Tests.m +// CleverTapSDKTests +// +// Created by Nikola Zagorchev on 29.03.23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import "CTVariables+Tests.h" + +@implementation CTVariables (Tests) + +- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo *)deviceInfo varCache: (CTVarCacheMock *)varCache { + self = [super init]; + if (self) { + self.varCache = varCache; + } + return self; +} + +@end diff --git a/CleverTapSDKTests/CTVariablesTest.m b/CleverTapSDKTests/CTVariablesTest.m index 62176de1..29add7e2 100644 --- a/CleverTapSDKTests/CTVariablesTest.m +++ b/CleverTapSDKTests/CTVariablesTest.m @@ -8,6 +8,8 @@ #import #import +#import "CTVariables+Tests.h" +#import "CTVarCacheMock.h" #import "CTVariables.h" #import "CTConstants.h" @@ -17,33 +19,6 @@ @interface CTVariablesTest : XCTestCase @end -@interface CTVarCacheMock : CTVarCache -@end - -@implementation CTVarCacheMock - -- (void)loadDiffs { - // Do NOT read from file -} - -- (void)saveDiffs { - // Do NOT save to file -} - -@end - -@implementation CTVariables (Mock) - -- (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo *)deviceInfo varCache: (CTVarCacheMock *)varCache { - self = [super init]; - if (self) { - self.varCache = varCache; - } - return self; -} - -@end - @implementation CTVariablesTest - (void)setUp { From cb684338b649e47da843bd5dd8cffdb25e81d56f Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 29 Mar 2023 23:58:58 +0300 Subject: [PATCH 45/86] CTVarCacheTest draft --- CleverTapSDK.xcodeproj/project.pbxproj | 8 +- CleverTapSDKTests/CTVarCacheTest.m | 417 +++++++++++++++++++++++++ CleverTapSDKTests/CTVarTests.m | 179 ----------- 3 files changed, 421 insertions(+), 183 deletions(-) create mode 100644 CleverTapSDKTests/CTVarCacheTest.m delete mode 100644 CleverTapSDKTests/CTVarTests.m diff --git a/CleverTapSDK.xcodeproj/project.pbxproj b/CleverTapSDK.xcodeproj/project.pbxproj index 66afa159..bd3169f5 100644 --- a/CleverTapSDK.xcodeproj/project.pbxproj +++ b/CleverTapSDK.xcodeproj/project.pbxproj @@ -242,7 +242,7 @@ 4EA64A2E296C1190001D9B22 /* CTRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EA64A2B296C1190001D9B22 /* CTRequest.m */; }; 4EA64A2F296C1190001D9B22 /* CTRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EA64A2B296C1190001D9B22 /* CTRequest.m */; }; 4EC2D085278AAD8000F4DE54 /* IdentityManagementTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EC2D084278AAD8000F4DE54 /* IdentityManagementTests.m */; }; - 4EED219B29AF6368006CEA19 /* CTVarTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EED219A29AF6368006CEA19 /* CTVarTests.m */; }; + 4EED219B29AF6368006CEA19 /* CTVarCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EED219A29AF6368006CEA19 /* CTVarCacheTest.m */; }; 4EF1CF9E29B076A300E3CB6A /* CTVarCache+Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EF1CF9D29B076A300E3CB6A /* CTVarCache+Tests.m */; }; 5709005327FD8E1F0011B89F /* CleverTap+SCDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = 5709005227FD8E1E0011B89F /* CleverTap+SCDomain.h */; settings = {ATTRIBUTES = (Public, ); }; }; 57D2E1C82684B1630068E45A /* CleverTap.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C7BBC8207D8837001345EF /* CleverTap.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -644,7 +644,7 @@ 4EA64A2B296C1190001D9B22 /* CTRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTRequest.m; sourceTree = ""; }; 4EC2D084278AAD8000F4DE54 /* IdentityManagementTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IdentityManagementTests.m; sourceTree = ""; }; 4EDCDE4D278AC4DF0065E699 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - 4EED219A29AF6368006CEA19 /* CTVarTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVarTests.m; sourceTree = ""; }; + 4EED219A29AF6368006CEA19 /* CTVarCacheTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVarCacheTest.m; sourceTree = ""; }; 4EF1CF9C29B076A300E3CB6A /* CTVarCache+Tests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CTVarCache+Tests.h"; sourceTree = ""; }; 4EF1CF9D29B076A300E3CB6A /* CTVarCache+Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CTVarCache+Tests.m"; sourceTree = ""; }; 5709005227FD8E1E0011B89F /* CleverTap+SCDomain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CleverTap+SCDomain.h"; sourceTree = ""; }; @@ -1082,7 +1082,7 @@ 4E1F155027692042009387AE /* EventTests.m */, 4EC2D084278AAD8000F4DE54 /* IdentityManagementTests.m */, 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */, - 4EED219A29AF6368006CEA19 /* CTVarTests.m */, + 4EED219A29AF6368006CEA19 /* CTVarCacheTest.m */, 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */, 6A2E0B9229D0A5CE00FCEA5F /* CTVariablesTest.m */, 6A2E0B9429D49D0200FCEA5F /* CTVariables+Tests.m */, @@ -1873,7 +1873,7 @@ files = ( 4E1F154F27691CA0009387AE /* CleverTapInstanceTests.m in Sources */, 4E1F155B276B662C009387AE /* EventDetail.m in Sources */, - 4EED219B29AF6368006CEA19 /* CTVarTests.m in Sources */, + 4EED219B29AF6368006CEA19 /* CTVarCacheTest.m in Sources */, 6A2E0B9129CCCC8600FCEA5F /* ContentMergerTest.m in Sources */, 6A2E0B9529D49D0200FCEA5F /* CTVariables+Tests.m in Sources */, 6A2E0B9829D49D5100FCEA5F /* CTVarCacheMock.m in Sources */, diff --git a/CleverTapSDKTests/CTVarCacheTest.m b/CleverTapSDKTests/CTVarCacheTest.m new file mode 100644 index 00000000..87fa1b35 --- /dev/null +++ b/CleverTapSDKTests/CTVarCacheTest.m @@ -0,0 +1,417 @@ +// +// CTVarCacheTest.m +// CleverTapSDKTests +// +// Created by Nikola Zagorchev on 29.03.23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import "CTVariables.h" +#import "CTVarCache.h" +#import +#import "CTUtils.h" +#import "CTPreferences.h" +#import "CTVarCache+Tests.h" +#import "CTVarCacheMock.h" +#import "CTVariables+Tests.h" +#import "CTConstants.h" +#import +#import +#import +#import "CTRequestFactory.h" + + +@interface CTVarCacheTest : XCTestCase + +@end + +CleverTapInstanceConfig *config; +CTDeviceInfo *deviceInfo; +CTVariables *variables; + +@implementation CTVarCacheTest + +- (void)setUp { + config = [[CleverTapInstanceConfig alloc] initWithAccountId:@"id" accountToken:@"token" accountRegion:@"eu"]; + config.useCustomCleverTapId = YES; + deviceInfo = [[CTDeviceInfo alloc] initWithConfig:config andCleverTapID:@"test"]; + CTVarCacheMock *varCache = [[CTVarCacheMock alloc] initWithConfig:config deviceInfo:deviceInfo]; + variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo varCache:varCache]; +// variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo]; +} + +- (void)tearDown { + variables = nil; +} + +- (void)testVarCacheFetchesNameComponents { + NSString *component1 = @"first"; + NSString *component2 = @"second"; + NSString *component3 = @"third"; + NSArray *nameComponents = [[variables varCache] getNameComponents:[NSString stringWithFormat:@"%@.%@.%@",component1,component2,component3]]; + XCTAssertNotNil(nameComponents); + + BOOL expression = [nameComponents containsObject:component1] && [nameComponents containsObject:component2] && [nameComponents containsObject:component3]; + XCTAssertTrue(expression); + + XCTAssertTrue(nameComponents.count == 3); +} + +// TODO: check test +- (void)testTraverse { + // Create a dictionary for testing purposes + NSDictionary *dictionary = @{ + @"key1": @"value1", + @"key2": @{ + @"nestedKey1": @"nestedValue1", + @"nestedKey2": @"nestedValue2" + } + }; + + // Test that the method returns the correct value when the key exists in the dictionary + id result = [variables.varCache traverse:dictionary withKey:@"key1" autoInsert:NO]; + XCTAssertEqualObjects(result, @"value1"); + + // Test that the method returns nil when the key does not exist in the dictionary + result = [variables.varCache traverse:dictionary withKey:@"key3" autoInsert:NO]; + XCTAssertNil(result); + + // Test that the method returns nil when the value for the key is NSNull + NSDictionary *dictionaryWithNull = @{ + @"key1": [NSNull null] + }; + result = [variables.varCache traverse:dictionaryWithNull withKey:@"key1" autoInsert:NO]; + XCTAssertNil(result); +} + +- (void)testTraverseWithAutoInsert { + NSDictionary *dictionary = @{ + @"key1": @"value1", + @"key2": @{ + @"nestedKey1": @"nestedValue1", + @"nestedKey2": @"nestedValue2" + } + }; + + // Test that the method creates a new dictionary and adds it to the collection when autoInsert is true and the key does not exist + NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary]; + [variables.varCache traverse:mutableDictionary withKey:@"newKey" autoInsert:YES]; + XCTAssertTrue([mutableDictionary objectForKey:@"newKey"] != nil); + XCTAssertTrue([[mutableDictionary objectForKey:@"newKey"] isKindOfClass:[NSMutableDictionary class]]); + + // Test that the method does not create a new dictionary when autoInsert is false and the key does not exist + [variables.varCache traverse:mutableDictionary withKey:@"newKey2" autoInsert:NO]; + XCTAssertNil([mutableDictionary objectForKey:@"newKey2"]); +} + + +// TODO: check test +- (void)testVarCacheResgitersVars { + CTVar *varMock = OCMPartialMock([variables define:@"test" with:@"test" kind:CT_KIND_STRING]); + [variables.varCache registerVariable:varMock]; + + XCTAssertEqual(variables.varCache.vars[varMock.name], varMock); +} + +// TODO: check test +- (void)testVarCacheGetVarsForName { + NSString *varName = @"test"; + CTVar *var = [variables define:varName with:@"test" kind:CT_KIND_STRING]; + CTVar *varResult = [variables.varCache getVariable:varName]; + + XCTAssertEqual(varResult, var); +} + +#pragma mark Register Variables +- (void)testRegisterVariableWithGroup { + [variables define:@"group.var1" with:@"value1" kind:CT_KIND_STRING]; + [variables define:@"group" with:@{ + @"var2": @"value2" + } kind:CT_KIND_DICTIONARY]; + + NSDictionary *expectedGroupDefaultValue = @{ @"var2": @"value2" }; + NSDictionary *expectedGroupValue = @{ @"var1": @"value1", @"var2": @"value2" }; + + CTVarCache *varCache = variables.varCache; + XCTAssertEqual(2, varCache.vars.count); + XCTAssertEqualObjects(@"value1", [varCache getVariable:@"group.var1"].defaultValue); + XCTAssertEqualObjects(@"value1", [varCache getVariable:@"group.var1"].value); + XCTAssertEqualObjects(expectedGroupDefaultValue, [varCache getVariable:@"group"].defaultValue); + XCTAssertEqualObjects(expectedGroupValue, [varCache getVariable:@"group"].value); +} + +- (void)testRegisterVariableWithNestedGroup { + [variables define:@"group1.var1" with:@1 kind:CT_KIND_INT]; + [variables define:@"group1.group2.var3" with:@NO kind:CT_KIND_BOOLEAN]; + NSDictionary *group1DefaultValue = @{ + @"var2": @2, + @"group2": @{ + @"var4": @4, + } + }; + [variables define:@"group1" with:group1DefaultValue kind:CT_KIND_DICTIONARY]; + + NSDictionary *expectedGroup1Value = @{ + @"var1": @1, + @"var2": @2, + @"group2": @{ + @"var4": @4, + @"var3": @NO, + } + }; + + CTVarCache *varCache = variables.varCache; + XCTAssertEqual(3, varCache.vars.count); + XCTAssertEqualObjects(@1, [varCache getVariable:@"group1.var1"].value); + XCTAssertEqualObjects(@NO, [varCache getVariable:@"group1.group2.var3"].value); + + XCTAssertEqualObjects(group1DefaultValue, [varCache getVariable:@"group1"].defaultValue); + XCTAssertEqualObjects(expectedGroup1Value, [varCache getVariable:@"group1"].value); +} + +#pragma mark GetMergedValue +- (void)testVarCacheGetMergedValue { + NSString *varName = @"var1"; + [variables define:varName with:@"value1" kind:CT_KIND_STRING]; + NSString *value = [variables.varCache getMergedValue:varName]; + + XCTAssertEqual(@"value1", value); +} + +- (void)testVarCacheGetMergedValueWithGroup { + [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_STRING]; + [variables define:@"group1.group2.var3" with:@NO kind:CT_KIND_BOOLEAN]; + [variables define:@"group1" with:@{ + @"var2": @2, + @"group2": @{ + @"var4": @4, + } + } kind:CT_KIND_DICTIONARY]; + + NSDictionary *expectedGroup1Value = @{ + @"var1": @"value1", + @"var2": @2, + @"group2": @{ + @"var4": @4, + @"var3": @NO, + } + }; + + XCTAssertEqualObjects(expectedGroup1Value, [variables.varCache getMergedValue:@"group1"]); +} + +- (void)testVarCacheGetMergedValueWithGroups { + [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_STRING]; + [variables define:@"group1.group2.var3" with:@NO kind:CT_KIND_BOOLEAN]; + [variables define:@"group1" with:@{ + @"var2": @2, + @"group2": @{ + @"var4": @4, + } + } kind:CT_KIND_DICTIONARY]; + + [variables define:@"var5" with:@"value5" kind:CT_KIND_STRING]; + + XCTAssertEqual(@"value1", [variables.varCache getMergedValue:@"group1.var1"]); + XCTAssertEqual(@2, [variables.varCache getMergedValue:@"group1.var2"]); + XCTAssertEqual(@NO, [variables.varCache getMergedValue:@"group1.group2.var3"]); + XCTAssertEqual(@4, [variables.varCache getMergedValue:@"group1.group2.var4"]); + + XCTAssertEqual(@"value5", [variables.varCache getMergedValue:@"var5"]); +} + +#pragma mark Apply Diffs +- (void)testVarCacheApplyDiffs { + CTVar *var1 = [variables define:@"var1" with:@1 kind:CT_KIND_INT]; + CTVar *group1_var1 = [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_INT]; + CTVar *var3 = [variables define:@"group1.group2.var3" with:@NO kind:CT_KIND_BOOLEAN]; + + NSDictionary *diffs = @{ + @"var1": @2, + @"group1": @{ + @"var1": @"value2", + @"var22": @"value22", + @"group2": @{ + @"var3": @YES, + } + } + }; + + [variables.varCache applyVariableDiffs:diffs]; + + XCTAssertEqual(@2, var1.value); + XCTAssertEqual(@"value2", group1_var1.value); + XCTAssertEqual(@YES, var3.value); + XCTAssertEqual(@"value22", [variables.varCache getMergedValue:@"group1.var22"]); +} + +- (void)testVarCacheApplyDiffsDefaultValue { + CTVar *var1 = [variables define:@"var1" with:@1 kind:CT_KIND_INT]; + CTVar *group1_var1 = [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_STRING]; + CTVar *var3 = [variables define:@"group1.group2.var3" with:@NO kind:CT_KIND_BOOLEAN]; + + NSDictionary *diffs = @{ + @"group1": @{ + @"var22": @"value22", + } + }; + + [variables.varCache applyVariableDiffs:diffs]; + + XCTAssertEqual(@1, var1.value); + XCTAssertEqual(@"value1", group1_var1.value); + XCTAssertEqual(@NO, var3.value); + XCTAssertEqual(@"value22", [variables.varCache getMergedValue:@"group1.var22"]); +} + +- (void)testVarCacheApplyDiffsGroup { + CTVar *var1 = [variables define:@"group1.group2.var1" with:@1 kind:CT_KIND_INT]; + CTVar *group1_group2 = [variables define:@"group1.group2" with:@{ + @"var2": @"value2" + } kind:CT_KIND_DICTIONARY]; + + NSDictionary *diffs = @{ + @"group1": @{ + @"group2": @{ + @"var3": @"value3" + } + } + }; + + [variables.varCache applyVariableDiffs:diffs]; + + NSDictionary *group1_group2_value = @{ + @"var1": @1, + @"var2": @"value2", + @"var3": @"value3" + }; + + XCTAssertEqual(@1, var1.value); + XCTAssertEqualObjects(group1_group2_value, group1_group2.value); + XCTAssertEqual(@"value3", [variables.varCache getMergedValue:@"group1.group2.var3"]); +} + +// TODO: test defining var later +- (void)testVarCacheMergeVariable { + NSDictionary *diffs = @{ + @"var1": @2, + @"group1": @{ + @"var1": @"value2", + @"group2": @{ + @"var2": @YES, + } + } + }; + // Apply diffs first, before defining the variable + [variables.varCache applyVariableDiffs:diffs]; + + CTVar *var1 = [variables define:@"var1" with:@1 kind:CT_KIND_INT]; + CTVar *group1_var1 = [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_STRING]; + CTVar *group1_group2 = [variables define:@"group1.group2" with:@{ + @"var2": @NO, + @"var3": @3, + } kind:CT_KIND_DICTIONARY]; + + XCTAssertEqual(@2, var1.value); + XCTAssertEqual(@"value2", group1_var1.value); + XCTAssertEqualObjects((@{ @"var2": @YES, @"var3": @3 }), group1_group2.value); +} + +- (void)testVarCacheMergeVariableNestedGroups { + +} + + +- (void)testVarCacheSavesDiffs { + + variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo]; + + id variablesMock = OCMPartialMock(variables); + [variablesMock define:@"Title" with:@"Hello" kind:CT_KIND_STRING]; + + NSDictionary *updatedVarsFromServer = @{ + @"Title": @"TitleUpdated", + }; + id varCacheMock = OCMPartialMock(variables.varCache); + [varCacheMock applyVariableDiffs:updatedVarsFromServer]; + OCMVerify([varCacheMock saveDiffs]); + XCTAssertTrue([varCacheMock hasReceivedDiffs]); + + NSString *fileName = [varCacheMock getArchiveFileName]; + NSString *filePath = [CTPreferences filePathfromFileName:fileName]; + NSData *diffsData = [NSData dataWithContentsOfFile:filePath]; + NSKeyedUnarchiver *unarchiver; + NSError *error = nil; + if (@available(iOS 12.0, *)) { + unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:diffsData error:&error]; + XCTAssertNil(error); + unarchiver.requiresSecureCoding = NO; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:diffsData]; +#pragma clang diagnostic pop + } + NSDictionary *loadedVars = (NSDictionary *) [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; + XCTAssertTrue([updatedVarsFromServer isEqualToDictionary:loadedVars]); + + [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; +} + +- (void)testVarCacheLoadsDiffs { + NSString *varName = @"Title"; + NSString *initialVarValue = @"Hello"; + NSString *updatedVarValue = @"TitleUpdated"; + CTVariables *variablesMock = OCMPartialMock(variables); + CTVarCache *varCacheMock = OCMPartialMock(variables.varCache); + [variablesMock define:varName with:initialVarValue kind:CT_KIND_STRING]; + + NSDictionary *updatedVarsFromServer = @{ + varName: updatedVarValue, + }; + NSString *varsJson = [CTUtils dictionaryToJsonString:updatedVarsFromServer]; + [varCacheMock applyVariableDiffs:updatedVarsFromServer]; + OCMVerify([varCacheMock saveDiffs]); + XCTAssertTrue([varCacheMock hasReceivedDiffs]); + + [varCacheMock setSilent:YES]; + [varCacheMock loadDiffs]; + CTVar *loadedVar = varCacheMock.vars[varName]; + XCTAssertEqualObjects(loadedVar.value, updatedVarValue); + + NSString *fileName = [varCacheMock getArchiveFileName]; + NSString *filePath = [CTPreferences filePathfromFileName:fileName]; + NSError *error = nil; + [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; +} + +- (void)testVarValues { + NSNumber *varValue = [NSNumber numberWithDouble:6.67345983745897]; + CTVar *var = [variables define:@"MyNumber" with:varValue kind:CT_KIND_FLOAT]; + + XCTAssertEqualObjects(var.stringValue,varValue.stringValue); + XCTAssertEqual(var.floatValue,varValue.floatValue); + XCTAssertEqual(var.intValue,varValue.intValue); + XCTAssertEqual(var.integerValue,varValue.integerValue); + XCTAssertEqual(var.doubleValue,varValue.doubleValue); + XCTAssertEqual(var.boolValue,varValue.boolValue); + XCTAssertEqual(var.longValue,varValue.longValue); + XCTAssertEqual(var.longLongValue,varValue.longLongValue); + XCTAssertEqual(var.unsignedIntValue,varValue.unsignedIntValue); + XCTAssertEqual(var.unsignedLongValue,varValue.unsignedLongValue); + XCTAssertEqual(var.unsignedIntegerValue,varValue.unsignedIntegerValue); + XCTAssertEqual(var.shortValue,varValue.shortValue); + XCTAssertEqual(var.unsignedShortValue,varValue.unsignedShortValue); + XCTAssertEqual(var.unsignedLongLongValue,varValue.unsignedLongLongValue); + XCTAssertEqual(var.cgFloatValue,varValue.doubleValue); + XCTAssertEqual(var.charValue,varValue.charValue); + XCTAssertEqual(var.unsignedCharValue,varValue.unsignedCharValue); + + CTVar *mapVar = [variables define:@"MyMap" with:@{@"MyMapNumber":varValue} kind:CT_KIND_DICTIONARY]; + XCTAssertTrue([[mapVar objectForKey:@"MyMapNumber"]isKindOfClass:[varValue class]]); + XCTAssertTrue([mapVar.value isKindOfClass:[NSDictionary class]]); + XCTAssertTrue([mapVar.defaultValue isKindOfClass:[NSDictionary class]]); +} + +@end diff --git a/CleverTapSDKTests/CTVarTests.m b/CleverTapSDKTests/CTVarTests.m deleted file mode 100644 index bc5818d2..00000000 --- a/CleverTapSDKTests/CTVarTests.m +++ /dev/null @@ -1,179 +0,0 @@ -// -// CTVarCacheTests.m -// CleverTapSDKTests -// -// Created by Akash Malhotra on 01/03/23. -// Copyright © 2023 CleverTap. All rights reserved. -// - -#import -#import "CTVariables.h" -#import "CTVarCache.h" -#import -#import "CTUtils.h" -#import "CTPreferences.h" -#import "CTVarCache+Tests.h" -#import "CTConstants.h" -#import -#import -#import -#import "CTRequestFactory.h" - -@interface CTVarTests : XCTestCase - -@end - -CleverTapInstanceConfig *config; -CTDeviceInfo *deviceInfo; -CTVariables *variables; - -@implementation CTVarTests - -- (void)setUp { - config = [[CleverTapInstanceConfig alloc] initWithAccountId:@"id" accountToken:@"token" accountRegion:@"eu"]; - config.useCustomCleverTapId = YES; - deviceInfo = [[CTDeviceInfo alloc] initWithConfig:config andCleverTapID:@"test"]; - variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo]; -} - -- (void)tearDown { - variables = nil; -} - -- (void)testVarCacheNotNil { - XCTAssertNotNil(variables); -} - -- (void)testVarCacheFetchesNameComponents { - NSString *component1 = @"Primary"; - NSString *component2 = @"Secondary"; - NSString *component3 = @"Tertiary"; - NSArray *nameComponents = [[variables varCache] getNameComponents:[NSString stringWithFormat:@"%@.%@.%@",component1,component2,component3]]; - XCTAssertNotNil(nameComponents); - - BOOL expression = [nameComponents containsObject:component1] && [nameComponents containsObject:component2] && [nameComponents containsObject:component3]; - XCTAssertTrue(expression); - - XCTAssertTrue(nameComponents.count == 3); -} - -- (void)testVarCacheResgitersVars { - CTVar *varMock = OCMPartialMock([variables define:@"test" with:@"test" kind:CT_KIND_STRING]); - [variables.varCache registerVariable:varMock]; - - XCTAssertEqual(variables.varCache.vars[varMock.name], varMock); -} - -- (void)testVarCacheGetVarsForName { - NSString *varName = @"test"; - CTVar *var = [variables define:varName with:@"test" kind:CT_KIND_STRING]; - CTVar *varResult = [variables getVariable:varName]; - - XCTAssertEqual(varResult, var); -} - -- (void)testVarCacheSavesDiffs { - id variablesMock = OCMPartialMock(variables); - [variablesMock define:@"Title" with:@"Hello" kind:CT_KIND_STRING]; - - NSDictionary *updatedVarsFromServer = @{ - @"Title": @"TitleUpdated", - }; - id varCacheMock = OCMPartialMock(variables.varCache); - [varCacheMock applyVariableDiffs:updatedVarsFromServer]; - OCMVerify([varCacheMock saveDiffs]); - XCTAssertTrue([varCacheMock hasReceivedDiffs]); - - NSString *fileName = [varCacheMock getArchiveFileName]; - NSString *filePath = [CTPreferences filePathfromFileName:fileName]; - NSData *diffsData = [NSData dataWithContentsOfFile:filePath]; - NSKeyedUnarchiver *unarchiver; - NSError *error = nil; - if (@available(iOS 12.0, *)) { - unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:diffsData error:&error]; - XCTAssertNil(error); - unarchiver.requiresSecureCoding = NO; - } else { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:diffsData]; -#pragma clang diagnostic pop - } - NSDictionary *loadedVars = (NSDictionary *) [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; - XCTAssertTrue([updatedVarsFromServer isEqualToDictionary:loadedVars]); - - [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; -} - -- (void)testVarCacheLoadsDiffs { - NSString *varName = @"Title"; - NSString *initialVarValue = @"Hello"; - NSString *updatedVarValue = @"TitleUpdated"; - CTVariables *variablesMock = OCMPartialMock(variables); - CTVarCache *varCacheMock = OCMPartialMock(variables.varCache); - [variablesMock define:varName with:initialVarValue kind:CT_KIND_STRING]; - - NSDictionary *updatedVarsFromServer = @{ - varName: updatedVarValue, - }; - NSString *varsJson = [CTUtils dictionaryToJsonString:updatedVarsFromServer]; - [varCacheMock applyVariableDiffs:updatedVarsFromServer]; - OCMVerify([varCacheMock saveDiffs]); - XCTAssertTrue([varCacheMock hasReceivedDiffs]); - - [varCacheMock setSilent:YES]; - [varCacheMock loadDiffs]; - CTVar *loadedVar = varCacheMock.vars[varName]; - XCTAssertEqualObjects(loadedVar.value, updatedVarValue); - - NSString *fileName = [varCacheMock getArchiveFileName]; - NSString *filePath = [CTPreferences filePathfromFileName:fileName]; - NSError *error = nil; - [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; -} - -- (void)testVarValues { - NSNumber *varValue = [NSNumber numberWithDouble:6.67345983745897]; - CTVar *var = [variables define:@"MyNumber" with:varValue kind:CT_KIND_FLOAT]; - - XCTAssertEqualObjects(var.stringValue,varValue.stringValue); - XCTAssertEqual(var.floatValue,varValue.floatValue); - XCTAssertEqual(var.intValue,varValue.intValue); - XCTAssertEqual(var.integerValue,varValue.integerValue); - XCTAssertEqual(var.doubleValue,varValue.doubleValue); - XCTAssertEqual(var.boolValue,varValue.boolValue); - XCTAssertEqual(var.longValue,varValue.longValue); - XCTAssertEqual(var.longLongValue,varValue.longLongValue); - XCTAssertEqual(var.unsignedIntValue,varValue.unsignedIntValue); - XCTAssertEqual(var.unsignedLongValue,varValue.unsignedLongValue); - XCTAssertEqual(var.unsignedIntegerValue,varValue.unsignedIntegerValue); - XCTAssertEqual(var.shortValue,varValue.shortValue); - XCTAssertEqual(var.unsignedShortValue,varValue.unsignedShortValue); - XCTAssertEqual(var.unsignedLongLongValue,varValue.unsignedLongLongValue); - XCTAssertEqual(var.cgFloatValue,varValue.doubleValue); - XCTAssertEqual(var.charValue,varValue.charValue); - XCTAssertEqual(var.unsignedCharValue,varValue.unsignedCharValue); - - CTVar *mapVar = [variables define:@"MyMap" with:@{@"MyMapNumber":varValue} kind:CT_KIND_DICTIONARY]; - XCTAssertTrue([[mapVar objectForKey:@"MyMapNumber"]isKindOfClass:[varValue class]]); - XCTAssertTrue([mapVar.value isKindOfClass:[NSDictionary class]]); - XCTAssertTrue([mapVar.defaultValue isKindOfClass:[NSDictionary class]]); -} - -- (void)testSyncVarsPayload { - NSString *varName = @"Title"; - NSString *varValue = @"Hello"; - - CTVariables *variablesMock = OCMPartialMock(variables); - CTVar *definedVar = [variablesMock define:varName with:varValue kind:CT_KIND_STRING]; - - NSDictionary *payload = [variables varsPayload]; - - XCTAssertEqualObjects(payload[@"type"],@"varsPayload"); - NSDictionary *vars = [payload objectForKey:@"vars"]; - NSDictionary *titleMap = [vars objectForKey:varName]; - XCTAssertEqualObjects(titleMap[@"defaultValue"],varValue); - XCTAssertEqualObjects(titleMap[@"type"],definedVar.kind); -} - -@end From 368935fcdc9234b506b77ec7cbcc5b5711ec9c05 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Thu, 30 Mar 2023 16:00:35 +0530 Subject: [PATCH 46/86] - Refactored appEnteredBackground method to perform background tasks with expiration handler. - Added a try catch to prevent app crashes --- CleverTapSDK/CleverTap.m | 45 +++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 68d1651d..cee2e682 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -1438,12 +1438,28 @@ - (void)_appEnteredForeground { - (void)_appEnteredBackground { self.isAppForeground = NO; - if (![self isMuted]) { - [self persistQueues]; - } - [self runSerialAsync:^{ + + UIApplication *application = [[self class]getSharedApplication]; + UIBackgroundTaskIdentifier __block backgroundTask; + + void (^finishTaskHandler)(void) = ^(){ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [application endBackgroundTask:backgroundTask]; + backgroundTask = UIBackgroundTaskInvalid; + }); + }; + // Start background task to make sure it runs when the app is in background. + backgroundTask = [application beginBackgroundTaskWithExpirationHandler:finishTaskHandler]; + + @try { + [self persistOrClearQueues]; [self updateSessionTime:(long) [[NSDate date] timeIntervalSince1970]]; - }]; + finishTaskHandler(); + } + @catch (NSException *exception) { + CleverTapLogDebug(self.config.logLevel, @"%@: Exception caught: %@", self, [exception reason]); + finishTaskHandler(); + } } - (void)recordAppLaunched:(NSString *)caller { @@ -2803,17 +2819,22 @@ - (void)clearNotificationsQueue { self.notificationsQueue = [NSMutableArray array]; } +- (void)persistOrClearQueues { + if ([self isMuted]) { + [self clearQueues]; + } else { + [self persistProfileQueue]; + [self persistEventsQueue]; + [self persistNotificationsQueue]; + } +} + - (void)persistQueues { [self runSerialAsync:^{ - if ([self isMuted]) { - [self clearQueues]; - } else { - [self persistProfileQueue]; - [self persistEventsQueue]; - [self persistNotificationsQueue]; - } + [self persistOrClearQueues]; }]; } + - (void)persistEventsQueue { NSString *fileName = [self eventsFileName]; NSMutableArray *eventsCopy; From 7fedb105c90d602a71bc035b906de5a059db5ef0 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Thu, 30 Mar 2023 16:38:41 +0530 Subject: [PATCH 47/86] minor changes --- CleverTapSDK/CTPreferences.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CleverTapSDK/CTPreferences.m b/CleverTapSDK/CTPreferences.m index daac0856..c699841d 100644 --- a/CleverTapSDK/CTPreferences.m +++ b/CleverTapSDK/CTPreferences.m @@ -161,7 +161,7 @@ + (BOOL)archiveObject:(id)object forFileName:(NSString *)filename { if (@available(iOS 11.0, tvOS 11.0, *)) { NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:NO error:&archiveError]; - [data writeToFile:filePath options:NSDataWritingAtomic error:&writeError]; + success = [data writeToFile:filePath options:NSDataWritingAtomic error:&writeError]; if (archiveError) { CleverTapLogStaticInternal(@"%@ failed to archive data at %@: %@", self, filePath, archiveError); } From fe8ac12237dedc850474b758ac9a6f2be09ae1f6 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Thu, 30 Mar 2023 17:57:09 +0530 Subject: [PATCH 48/86] fixed pod lib lint errors --- CleverTap-iOS-SDK.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CleverTap-iOS-SDK.podspec b/CleverTap-iOS-SDK.podspec index d1a38df2..ecc52732 100644 --- a/CleverTap-iOS-SDK.podspec +++ b/CleverTap-iOS-SDK.podspec @@ -15,7 +15,7 @@ s.ios.deployment_target = '9.0' s.ios.source_files = 'CleverTapSDK/**/*.{h,m}' s.ios.public_header_files = 'CleverTapSDK/CleverTap.h', 'CleverTapSDK/CleverTap+SSLPinning.h','CleverTapSDK/CleverTap+Inbox.h', 'CleverTapSDK/CleverTapInstanceConfig.h', 'CleverTapSDK/CleverTapBuildInfo.h', 'CleverTapSDK/CleverTapEventDetail.h', 'CleverTapSDK/CleverTapInAppNotificationDelegate.h', 'CleverTapSDK/CleverTapSyncDelegate.h', 'CleverTapSDK/CleverTapTrackedViewController.h', 'CleverTapSDK/CleverTapUTMDetail.h', 'CleverTapSDK/CleverTapJSInterface.h', 'CleverTapSDK/CleverTap+DisplayUnit.h', 'CleverTapSDK/CleverTap+FeatureFlags.h', 'CleverTapSDK/CleverTap+ProductConfig.h', 'CleverTapSDK/CleverTapPushNotificationDelegate.h', 'CleverTapSDK/CleverTapURLDelegate.h', 'CleverTapSDK/CleverTap+InAppNotifications.h', 'CleverTapSDK/CleverTap+SCDomain.h', 'CleverTapSDK/CleverTap+PushPermission.h', 'CleverTapSDK/InApps/CTLocalInApp.h', 'CleverTapSDK/CleverTap+CTVar.h', 'CleverTapSDK/ProductExperiences/CTVar.h' s.tvos.deployment_target = '9.0' -s.tvos.source_files = 'CleverTapSDK/*.{h,m}', 'CleverTapSDK/ProductConfig/**/*.{h,m}', 'CleverTapSDK/FeatureFlags/**/*.{h,m}' +s.tvos.source_files = 'CleverTapSDK/*.{h,m}', 'CleverTapSDK/ProductConfig/**/*.{h,m}', 'CleverTapSDK/FeatureFlags/**/*.{h,m}', 'CleverTapSDK/ProductExperiences/*.{h,m}' s.tvos.exclude_files = 'CleverTapSDK/CleverTapJSInterface.{h,m}' s.tvos.public_header_files = 'CleverTapSDK/CleverTap.h', 'CleverTapSDK/CleverTap+SSLPinning.h', 'CleverTapSDK/CleverTapInstanceConfig.h', 'CleverTapSDK/CleverTapBuildInfo.h', 'CleverTapSDK/CleverTapEventDetail.h', 'CleverTapSDK/CleverTapSyncDelegate.h', 'CleverTapSDK/CleverTapTrackedViewController.h', 'CleverTapSDK/CleverTapUTMDetail.h', 'CleverTapSDK/CleverTap+FeatureFlags.h', 'CleverTapSDK/CleverTap+ProductConfig.h', 'CleverTapSDK/CleverTap+CTVar.h', 'CleverTapSDK/ProductExperiences/CTVar.h' end From 8e5c4beb5ca090ba053268d8801a1fca8017f860 Mon Sep 17 00:00:00 2001 From: Aishwarya Nanna Date: Thu, 30 Mar 2023 23:56:32 +0530 Subject: [PATCH 49/86] - Added 'setCustomSdkVersion' to record hybrid sdk name and version --- CleverTapSDK/CleverTap.h | 11 +++++++++++ CleverTapSDK/CleverTap.m | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index 1f884a8d..328e7430 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -1227,6 +1227,17 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; */ - (void)setLibrary:(NSString * _Nonnull)name; +/*! + @method + + @abstract + Set the Library name for Auxiliary SDKs + + @discussion + Call this to method to set library name in the Auxiliary SDK + */ +- (void)setCustomSdkVersion:(NSString * _Nonnull)name version:(int)version; + /*! @method diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 68d1651d..01703e9f 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -78,6 +78,7 @@ static const void *const kQueueKey = &kQueueKey; static const void *const kNotificationQueueKey = &kNotificationQueueKey; static BOOL isLocationEnabled; +static NSMutableDictionary *libVersionDict; static NSRecursiveLock *instanceLock; static const int kMaxBatchSize = 49; @@ -1234,6 +1235,11 @@ - (NSDictionary *)generateAppFields { evtData[@"lib"] = self.deviceInfo.library; } + if (libVersionDict){ + evtData[@"lib"] = libVersionDict[@"name"]; + evtData[@"libVersion"] = libVersionDict[@"version"]; + } + #if CLEVERTAP_SSL_PINNING evtData[@"sslpin"] = @YES; #endif @@ -3992,6 +3998,12 @@ - (void)setLibrary:(NSString *)name { self.deviceInfo.library = name; } +- (void)setCustomSdkVersion:(NSString *)name version:(int)version{ + libVersionDict = [NSMutableDictionary new]; + libVersionDict[@"name"] = name; + libVersionDict[@"version"] = @(version); +} + + (void)setDebugLevel:(int)level { [CTLogger setDebugLevel:level]; if (_defaultInstanceConfig) { From 89f3b131ce3428685b902ab0c6cc8b359d9ba4fb Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Fri, 31 Mar 2023 11:26:48 +0530 Subject: [PATCH 50/86] changed header structure --- CleverTapSDK/CleverTap.h | 4 ++-- CleverTapSDK/CleverTap.m | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index 328e7430..0ca4611a 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -1231,10 +1231,10 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; @method @abstract - Set the Library name for Auxiliary SDKs + Set the Library name and version for Auxiliary SDKs @discussion - Call this to method to set library name in the Auxiliary SDK + Call this to method to set library name and version in the Auxiliary SDK */ - (void)setCustomSdkVersion:(NSString * _Nonnull)name version:(int)version; diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 01703e9f..e34706ac 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -78,7 +78,7 @@ static const void *const kQueueKey = &kQueueKey; static const void *const kNotificationQueueKey = &kNotificationQueueKey; static BOOL isLocationEnabled; -static NSMutableDictionary *libVersionDict; +static NSMutableDictionary *auxiliarySdkVersions; static NSRecursiveLock *instanceLock; static const int kMaxBatchSize = 49; @@ -1235,9 +1235,10 @@ - (NSDictionary *)generateAppFields { evtData[@"lib"] = self.deviceInfo.library; } - if (libVersionDict){ - evtData[@"lib"] = libVersionDict[@"name"]; - evtData[@"libVersion"] = libVersionDict[@"version"]; + if (auxiliarySdkVersions && auxiliarySdkVersions.count > 0) { + [auxiliarySdkVersions enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) { + [evtData setObject:value forKey:key]; + }]; } #if CLEVERTAP_SSL_PINNING @@ -3998,10 +3999,11 @@ - (void)setLibrary:(NSString *)name { self.deviceInfo.library = name; } -- (void)setCustomSdkVersion:(NSString *)name version:(int)version{ - libVersionDict = [NSMutableDictionary new]; - libVersionDict[@"name"] = name; - libVersionDict[@"version"] = @(version); +- (void)setCustomSdkVersion:(NSString *)name version:(int)version { + if (!auxiliarySdkVersions) { + auxiliarySdkVersions = [NSMutableDictionary new]; + } + auxiliarySdkVersions[name] = @(version); } + (void)setDebugLevel:(int)level { From 2c50fab446272205c5e14de50ea9029195051b9b Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Fri, 31 Mar 2023 14:06:35 +0300 Subject: [PATCH 51/86] Split by dot for name components --- CleverTapSDK/ProductExperiences/CTVarCache.m | 50 +++---------------- CleverTapSDK/ProductExperiences/CTVariables.m | 8 ++- 2 files changed, 14 insertions(+), 44 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index a38445bd..dba37fff 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -5,7 +5,6 @@ #import "ContentMerger.h" @interface CTVarCache() -@property (strong, nonatomic) NSRegularExpression *varNameRegex; @property (strong, nonatomic) NSMutableDictionary *valuesFromClient; @property (strong, nonatomic) id merged; @property (strong, nonatomic) NSDictionary *diffs; @@ -35,47 +34,12 @@ - (void)initialize self.valuesFromClient = [NSMutableDictionary dictionary]; self.hasReceivedDiffs = NO; self.silent = NO; - NSError *error = NULL; - self.varNameRegex = [NSRegularExpression regularExpressionWithPattern:@"(?:[^\\.\\[.(\\\\]+|\\\\.)+" - options:NSRegularExpressionCaseInsensitive error:&error]; -} - -- (NSArray *)arrayOfCaptureComponentsOfString:(NSString *)data matchedBy:(NSRegularExpression *)regExpression -{ - NSMutableArray *test = [NSMutableArray array]; - - NSArray *matches = [regExpression matchesInString:data options:0 range:NSMakeRange(0, data.length)]; - - for(NSTextCheckingResult *match in matches) { - NSMutableArray *result = [NSMutableArray arrayWithCapacity:match.numberOfRanges]; - for(NSInteger i=0; i *nameComponents = [self.varCache getNameComponents:name]; + if ([nameComponents[0] isEqualToString:@""] || [nameComponents.lastObject isEqualToString:@""]) { + CleverTapLogDebug(_config.logLevel, @"%@: Variable name starts or ends with a `.` which is not allowed.", self); + return nil; + } @synchronized (self.varCache.vars) { CT_TRY @@ -43,7 +49,7 @@ - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString } CT_END_TRY CTVar *var = [[CTVar alloc] initWithName:name - withComponents:[self.varCache getNameComponents:name] + withComponents:nameComponents withDefaultValue:defaultValue withKind:kind varCache:self.varCache]; From 78e4e6acb4d7e2e555720f11234a35afdc52c06c Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Fri, 31 Mar 2023 14:06:51 +0300 Subject: [PATCH 52/86] Prevent deadlock --- CleverTapSDK/ProductExperiences/CTVariables.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index c18c3213..3ce5c5f3 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -92,7 +92,7 @@ - (void)addVarListeners { - (void)triggerVariablesChanged { if (![NSThread isMainThread]) { - dispatch_sync(dispatch_get_main_queue(), ^{ + dispatch_async(dispatch_get_main_queue(), ^{ [self triggerVariablesChanged]; }); return; From 9b990764f95a92d5febe5b36df229a9fed36c46e Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 5 Apr 2023 16:14:32 +0300 Subject: [PATCH 53/86] Do not allow var names starting or ending with dot --- CleverTapSDK/ProductExperiences/CTVariables.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 3ce5c5f3..db8b7f40 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -34,9 +34,8 @@ - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString CleverTapLogDebug(_config.logLevel, @"%@: Empty name provided as parameter while defining a variable.", self); return nil; } - - NSArray *nameComponents = [self.varCache getNameComponents:name]; - if ([nameComponents[0] isEqualToString:@""] || [nameComponents.lastObject isEqualToString:@""]) { + + if ([name hasPrefix:@"."] || [name hasSuffix:@"."]) { CleverTapLogDebug(_config.logLevel, @"%@: Variable name starts or ends with a `.` which is not allowed.", self); return nil; } @@ -48,6 +47,7 @@ - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString return existing; } CT_END_TRY + NSArray *nameComponents = [self.varCache getNameComponents:name]; CTVar *var = [[CTVar alloc] initWithName:name withComponents:nameComponents withDefaultValue:defaultValue From 054f098278568b3a5069b26e67e6515f85bef519 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 5 Apr 2023 18:38:24 +0300 Subject: [PATCH 54/86] Refactor boolean flags for CTVarCache and CTVariables --- CleverTapSDK/CleverTap.m | 2 -- CleverTapSDK/ProductExperiences/CTVar.m | 8 ++------ CleverTapSDK/ProductExperiences/CTVarCache.h | 4 +--- CleverTapSDK/ProductExperiences/CTVarCache.m | 11 ++--------- CleverTapSDK/ProductExperiences/CTVariables.m | 14 ++++++++++---- 5 files changed, 15 insertions(+), 24 deletions(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 2544944f..acb32660 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -1339,9 +1339,7 @@ - (void)recordAppLaunched:(NSString *)caller { } // LOAD VARS FROM CACHE BEFORE APP LAUNCHED - [self.variables.varCache setSilent:YES]; [self.variables.varCache loadDiffs]; - [self.variables.varCache setSilent:NO]; self.appLaunchProcessed = YES; diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index 2b56ba57..748d496a 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -90,11 +90,7 @@ - (void)update _hasChanged = YES; } - if (varCache.silent) { - return; - } - - if (varCache.appLaunchedRecorded) { + if (varCache.hasVarsRequestCompleted) { [self triggerValueChanged]; _hadStarted = YES; } @@ -126,7 +122,7 @@ - (void)onValueChanged:(CleverTapVariablesChangedBlock)block _valueChangedBlocks = [NSMutableArray array]; } [_valueChangedBlocks addObject:[block copy]]; - if (varCache.appLaunchedRecorded) { + if (varCache.hasVarsRequestCompleted) { [self triggerValueChanged]; } CT_END_TRY diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.h b/CleverTapSDK/ProductExperiences/CTVarCache.h index cc131db3..d0a097b3 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.h +++ b/CleverTapSDK/ProductExperiences/CTVarCache.h @@ -14,13 +14,11 @@ NS_SWIFT_NAME(VarCache) - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo; -@property (assign, nonatomic) BOOL silent; @property (strong, nonatomic) NSMutableDictionary *vars; -@property (assign, nonatomic) BOOL appLaunchedRecorded; +@property (assign, nonatomic) BOOL hasVarsRequestCompleted; - (nullable NSDictionary *)diffs; - (void)loadDiffs; -- (BOOL)hasReceivedDiffs; - (void)applyVariableDiffs:(nullable NSDictionary *)diffs_; - (void)onUpdate:(CacheUpdateBlock)block; diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index dba37fff..adb458ab 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -10,8 +10,6 @@ @interface CTVarCache() @property (strong, nonatomic) NSDictionary *diffs; @property (strong, nonatomic) CacheUpdateBlock updateBlock; -@property (assign, nonatomic) BOOL hasReceivedDiffs; - @property (nonatomic, strong) CleverTapInstanceConfig *config; @property (nonatomic, strong) CTDeviceInfo *deviceInfo; @end @@ -32,8 +30,7 @@ - (void)initialize self.vars = [NSMutableDictionary dictionary]; self.diffs = [NSMutableDictionary dictionary]; self.valuesFromClient = [NSMutableDictionary dictionary]; - self.hasReceivedDiffs = NO; - self.silent = NO; + self.hasVarsRequestCompleted = NO; } - (NSArray *)getNameComponents:(NSString *)name @@ -228,7 +225,6 @@ - (NSString*)dataArchiveFileName { - (void)applyVariableDiffs:(NSDictionary *)diffs_ { @synchronized (self.vars) { - if (diffs_ || (!self.silent && !self.hasReceivedDiffs)) { // Prevent overriding variables if API returns null // If no variables are defined, API returns {} if (diffs_ != nil && ![diffs_ isEqual:[NSNull null]]) { @@ -249,13 +245,10 @@ - (void)applyVariableDiffs:(NSDictionary *)diffs_ } else { CleverTapLogDebug(self.config.logLevel, @"%@: No variables received from the server", self); } - } // Do NOT save vars to cache if silent - if (!self.silent) { + if (self.hasVarsRequestCompleted) { [self saveDiffs]; - - self.hasReceivedDiffs = YES; if (self.updateBlock) { self.updateBlock(); } diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index db8b7f40..bd8cb864 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -20,7 +20,7 @@ @interface CTVariables() @end @implementation CTVariables - +// TODO: Linting: Brackets and spaces consistency - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo { if ((self = [super init])) { self.varCache = [[CTVarCache alloc] initWithConfig:config deviceInfo:deviceInfo]; @@ -60,13 +60,19 @@ - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString - (void)handleVariablesResponse:(NSDictionary *)varsResponse { if (varsResponse) { - [[self varCache] setAppLaunchedRecorded:YES]; + [[self varCache] setHasVarsRequestCompleted:YES]; NSDictionary *values = [self unflatten:varsResponse]; [[self varCache] applyVariableDiffs:values]; [self triggerFetchVariables:YES]; } } +- (void)handleVariablesError +{ + [[self varCache] setHasVarsRequestCompleted:YES]; + [self triggerFetchVariables:NO]; +} + // TODO: callback is overridden after second call if first is not ready yet - (void)triggerFetchVariables:(BOOL)success { if (self.fetchVariablesBlock) { @@ -126,7 +132,7 @@ - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { [self.variablesChangedBlocks addObject:[block copy]]; CT_END_TRY - if ([self.varCache hasReceivedDiffs]) { + if ([self.varCache hasVarsRequestCompleted]) { block(); } } @@ -138,7 +144,7 @@ - (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { return; } - if ([self.varCache hasReceivedDiffs]) { + if ([self.varCache hasVarsRequestCompleted]) { block(); } else { CT_TRY From 62e831dcfc235be09d80a63ec3d3bd0100a744c0 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 5 Apr 2023 20:40:53 +0300 Subject: [PATCH 55/86] Fix trigger callbacks --- CleverTapSDK/CleverTap.m | 6 +-- CleverTapSDK/ProductExperiences/CTVarCache.h | 1 - CleverTapSDK/ProductExperiences/CTVarCache.m | 49 ++++++++----------- CleverTapSDK/ProductExperiences/CTVariables.h | 2 +- CleverTapSDK/ProductExperiences/CTVariables.m | 15 +++--- 5 files changed, 30 insertions(+), 43 deletions(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index acb32660..542fafe3 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -694,9 +694,6 @@ - (instancetype)initWithConfig:(CleverTapInstanceConfig*)config andCleverTapID:( self.variables = [[CTVariables alloc] initWithConfig:self.config deviceInfo:self.deviceInfo]; - // TODO: check listeners here are needed - // ADD PE VAR CHANGED LISTENERS - [[self variables] addVarListeners]; [self notifyUserProfileInitialized]; } @@ -2821,11 +2818,10 @@ - (void)sendQueue:(NSMutableArray *)queue { dispatch_semaphore_signal(semaphore); }]; [ctRequest onError:^(NSError * _Nullable error) { - if (error) { CleverTapLogDebug(self.config.logLevel, @"%@: Network error while sending queue, will retry: %@", self, error.localizedDescription); } - [[self variables] triggerFetchVariables:NO]; + [[self variables] handleVariablesError]; dispatch_semaphore_signal(semaphore); }]; [self.requestSender send:ctRequest]; diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.h b/CleverTapSDK/ProductExperiences/CTVarCache.h index d0a097b3..979b10c6 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.h +++ b/CleverTapSDK/ProductExperiences/CTVarCache.h @@ -20,7 +20,6 @@ NS_SWIFT_NAME(VarCache) - (nullable NSDictionary *)diffs; - (void)loadDiffs; - (void)applyVariableDiffs:(nullable NSDictionary *)diffs_; -- (void)onUpdate:(CacheUpdateBlock)block; - (void)registerVariable:(CTVar *)var; - (nullable CTVar *)getVariable:(NSString *)name; diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index adb458ab..6275d3f6 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -225,40 +225,33 @@ - (NSString*)dataArchiveFileName { - (void)applyVariableDiffs:(NSDictionary *)diffs_ { @synchronized (self.vars) { - // Prevent overriding variables if API returns null - // If no variables are defined, API returns {} - if (diffs_ != nil && ![diffs_ isEqual:[NSNull null]]) { - self.diffs = diffs_; - - // Merger helper will mutate diffs. - // We need to lock it in case multiple threads will be accessing this. - @synchronized (self.diffs) { - self.merged = [ContentMerger mergeWithVars:self.valuesFromClient diff:self.diffs]; - } - - // Update variables with new values. - // Have to extract the keys because a dictionary variable may add a new sub-variable, - // modifying the variable dictionary. - for (NSString *name in [self.vars allKeys]) { - [self.vars[name] update]; - } - } else { - CleverTapLogDebug(self.config.logLevel, @"%@: No variables received from the server", self); + // Prevent overriding variables if API returns null + // If no variables are defined, API returns {} + if (diffs_ != nil && ![diffs_ isEqual:[NSNull null]]) { + self.diffs = diffs_; + + // Merger helper will mutate diffs. + // We need to lock it in case multiple threads will be accessing this. + @synchronized (self.diffs) { + self.merged = [ContentMerger mergeWithVars:self.valuesFromClient diff:self.diffs]; } + + // Update variables with new values. + // Have to extract the keys because a dictionary variable may add a new sub-variable, + // modifying the variable dictionary. + for (NSString *name in [self.vars allKeys]) { + [self.vars[name] update]; + } + } else { + CleverTapLogDebug(self.config.logLevel, @"%@: No variables received from the server", self); + } - // Do NOT save vars to cache if silent + // Do NOT save diffs when loading from cache + // Load diffs is called before vars request has been sent if (self.hasVarsRequestCompleted) { [self saveDiffs]; - if (self.updateBlock) { - self.updateBlock(); - } } } } -- (void)onUpdate:(CacheUpdateBlock) block -{ - self.updateBlock = block; -} - @end diff --git a/CleverTapSDK/ProductExperiences/CTVariables.h b/CleverTapSDK/ProductExperiences/CTVariables.h index 25c55655..f7569f3d 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.h +++ b/CleverTapSDK/ProductExperiences/CTVariables.h @@ -26,12 +26,12 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(define(name:value:kind:)); - (void)handleVariablesResponse:(NSDictionary *)varsResponse; +- (void)handleVariablesError; - (void)triggerFetchVariables:(BOOL)success; - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; - (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName; - (NSDictionary*)varsPayload; -- (void)addVarListeners; - (NSDictionary*)unflatten:(NSDictionary*)result; @end diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index bd8cb864..aa40fee3 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -63,14 +63,19 @@ - (void)handleVariablesResponse:(NSDictionary *)varsResponse [[self varCache] setHasVarsRequestCompleted:YES]; NSDictionary *values = [self unflatten:varsResponse]; [[self varCache] applyVariableDiffs:values]; + [self triggerVariablesChanged]; [self triggerFetchVariables:YES]; } } - (void)handleVariablesError { - [[self varCache] setHasVarsRequestCompleted:YES]; - [self triggerFetchVariables:NO]; + if (![[self varCache] hasVarsRequestCompleted]) { + [[self varCache] setHasVarsRequestCompleted:YES]; + [self triggerVariablesChanged]; + } else { + [self triggerFetchVariables:NO]; + } } // TODO: callback is overridden after second call if first is not ready yet @@ -89,12 +94,6 @@ - (void)triggerFetchVariables:(BOOL)success { } } -- (void)addVarListeners { - [self.varCache onUpdate:^{ - [self triggerVariablesChanged]; - }]; -} - - (void)triggerVariablesChanged { if (![NSThread isMainThread]) { From 8132494b845b76565f1c95e78ff316b971a49fc2 Mon Sep 17 00:00:00 2001 From: SonalKachare Date: Thu, 6 Apr 2023 14:51:39 +0530 Subject: [PATCH 56/86] Merge branch 'master' into SDK-2840_sdkVersionPublicAPI * master: Update CHANGELOG.md added a try catch guardrail to prevent background state crashes Update CHANGELOG.md Update CleverTapBuildInfo.h Update CleverTap-iOS-SDK.podspec fixed xcode analyse warnings fixed warnings Fixed compilation errors in xcode 14.3+ --- CHANGELOG.md | 6 +++ CleverTap-iOS-SDK.podspec | 2 +- CleverTapSDK/CTPlistInfo.h | 2 +- CleverTapSDK/CTPlistInfo.m | 2 +- CleverTapSDK/CTPreferences.h | 8 ++-- CleverTapSDK/CTPreferences.m | 8 ++-- CleverTapSDK/CleverTap.h | 2 +- CleverTapSDK/CleverTap.m | 37 ++++++++++++------- CleverTapSDK/CleverTapBuildInfo.h | 2 +- .../models/CTUserMO+CoreDataProperties.h | 2 +- .../models/CTUserMO+CoreDataProperties.m | 4 +- 11 files changed, 46 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aa2470d..4b59361b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. +### [Version 4.2.2](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/4.2.2) (April 03, 2023) + +#### Fixed +- Fixed compilation errors in xcode 14.3+. +- Added guard rails to prevent crashes for background state tasks. + ### [Version 4.2.1](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/4.2.1) (March 22, 2023) #### Added diff --git a/CleverTap-iOS-SDK.podspec b/CleverTap-iOS-SDK.podspec index 5dadcf72..ea1c507f 100644 --- a/CleverTap-iOS-SDK.podspec +++ b/CleverTap-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "CleverTap-iOS-SDK" -s.version = "4.2.1" +s.version = "4.2.2" s.summary = "The CleverTap iOS SDK for App Analytics and Engagement." s.homepage = "https://github.com/CleverTap/clevertap-ios-sdk" s.license = { :type => "MIT" } diff --git a/CleverTapSDK/CTPlistInfo.h b/CleverTapSDK/CTPlistInfo.h index ac649af8..2b0691be 100644 --- a/CleverTapSDK/CTPlistInfo.h +++ b/CleverTapSDK/CTPlistInfo.h @@ -16,6 +16,6 @@ + (instancetype _Nullable)sharedInstance; - (void)setCredentialsWithAccountID:(NSString * _Nonnull)accountID token:(NSString * _Nonnull)token region:(NSString * _Nullable)region; - (void)setCredentialsWithAccountID:(NSString * _Nonnull)accountID token:(NSString * _Nonnull)token proxyDomain:(NSString * _Nonnull)proxyDomain; -- (void)setCredentialsWithAccountID:(NSString * _Nonnull)accountID token:(NSString * _Nonnull)token proxyDomain:(NSString * _Nonnull)proxyDomain spikyProxyDomain:(NSString * _Nonnull)spikyProxyDomain; +- (void)setCredentialsWithAccountID:(NSString * _Nonnull)accountID token:(NSString * _Nonnull)token proxyDomain:(NSString * _Nonnull)proxyDomain spikyProxyDomain:(NSString * _Nullable)spikyProxyDomain; @end diff --git a/CleverTapSDK/CTPlistInfo.m b/CleverTapSDK/CTPlistInfo.m index f03620fe..3564d03a 100644 --- a/CleverTapSDK/CTPlistInfo.m +++ b/CleverTapSDK/CTPlistInfo.m @@ -106,7 +106,7 @@ - (void)setCredentialsWithAccountID:(NSString *)accountID token:(NSString *)toke _proxyDomain = proxyDomain; } -- (void)setCredentialsWithAccountID:(NSString *)accountID token:(NSString *)token proxyDomain:(NSString *)proxyDomain spikyProxyDomain:(NSString *)spikyProxyDomain { +- (void)setCredentialsWithAccountID:(NSString * _Nonnull)accountID token:(NSString * _Nonnull)token proxyDomain:(NSString * _Nonnull)proxyDomain spikyProxyDomain:(NSString * _Nullable)spikyProxyDomain { _accountId = accountID; _accountToken = token; _proxyDomain = proxyDomain; diff --git a/CleverTapSDK/CTPreferences.h b/CleverTapSDK/CTPreferences.h index 308f26b1..f0a94cdf 100644 --- a/CleverTapSDK/CTPreferences.h +++ b/CleverTapSDK/CTPreferences.h @@ -7,7 +7,7 @@ + (void)putInt:(long)resetValue forKey:(NSString *_Nonnull)key; -+ (NSString *_Nonnull)getStringForKey:(NSString *_Nonnull)key withResetValue:(NSString *_Nullable)resetValue; ++ (NSString *_Nullable)getStringForKey:(NSString *_Nonnull)key withResetValue:(NSString *_Nullable)resetValue; + (void)putString:(NSString *_Nonnull)resetValue forKey:(NSString *_Nonnull)key; @@ -17,12 +17,12 @@ + (void)removeObjectForKey:(NSString *_Nonnull)key; -+ (id _Nonnull)unarchiveFromFile:(NSString *_Nonnull)filename ofType:(Class _Nonnull)cls removeFile:(BOOL)remove; ++ (id _Nullable)unarchiveFromFile:(NSString *_Nonnull)filename ofType:(Class _Nonnull)cls removeFile:(BOOL)remove; -+ (id _Nonnull)unarchiveFromFile:(NSString *_Nonnull)filename ofTypes:(nonnull NSSet *)classes removeFile:(BOOL)remove; ++ (id _Nullable)unarchiveFromFile:(NSString *_Nonnull)filename ofTypes:(nonnull NSSet *)classes removeFile:(BOOL)remove; + (BOOL)archiveObject:(id _Nonnull)object forFileName:(NSString *_Nonnull)fileName; -+ (NSString *)storageKeyWithSuffix: (NSString *)suffix config: (CleverTapInstanceConfig*)config; ++ (NSString * _Nonnull)storageKeyWithSuffix: (NSString * _Nonnull)suffix config: (CleverTapInstanceConfig* _Nonnull)config; @end diff --git a/CleverTapSDK/CTPreferences.m b/CleverTapSDK/CTPreferences.m index daac0856..42daa2b6 100644 --- a/CleverTapSDK/CTPreferences.m +++ b/CleverTapSDK/CTPreferences.m @@ -25,7 +25,7 @@ + (void)putInt:(long)resetValue forKey:(NSString *)key { [defaults synchronize]; } -+ (NSString *)getStringForKey:(NSString *)key withResetValue:(NSString *)resetValue { ++ (NSString *_Nullable)getStringForKey:(NSString *_Nonnull)key withResetValue:(NSString *_Nullable)resetValue { key = [NSString stringWithFormat:@"%@%@", PREF_PREFIX, key]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; id value = [defaults objectForKey:key]; @@ -92,7 +92,7 @@ + (void)logUnarchiveError:(NSError *)error filePath:(NSString *)filePath removeF } } -+ (id)unarchiveFromFile:(NSString *)filename ofTypes:(nonnull NSSet *)classes removeFile:(BOOL)remove { ++ (id _Nullable)unarchiveFromFile:(NSString *_Nonnull)filename ofTypes:(nonnull NSSet *)classes removeFile:(BOOL)remove { id data = nil; NSError *error = nil; NSString *filePath = [self filePathfromFileName:filename]; @@ -119,7 +119,7 @@ + (id)unarchiveFromFile:(NSString *)filename ofTypes:(nonnull NSSet *)cla return data; } -+ (id)unarchiveFromFile:(NSString *)filename ofType:(Class)cls removeFile:(BOOL)remove { ++ (id _Nullable)unarchiveFromFile:(NSString *_Nonnull)filename ofType:(Class _Nonnull)cls removeFile:(BOOL)remove{ id data = nil; NSError *error = nil; @@ -177,7 +177,7 @@ + (BOOL)archiveObject:(id)object forFileName:(NSString *)filename { return success; } -+ (NSString *)storageKeyWithSuffix: (NSString *)suffix config: (CleverTapInstanceConfig*)config { ++ (NSString * _Nonnull)storageKeyWithSuffix: (NSString * _Nonnull)suffix config: (CleverTapInstanceConfig* _Nonnull)config { return [NSString stringWithFormat:@"%@:%@", config.accountId, suffix]; } diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index 0ca4611a..65326b2e 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -1285,7 +1285,7 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; #if defined(CLEVERTAP_HOST_WATCHOS) /** HostWatchOS */ -- (BOOL)handleMessage:(NSDictionary *)message forWatchSession:(WCSession *)session API_AVAILABLE(ios(9.0)); +- (BOOL)handleMessage:(NSDictionary *_Nonnull)message forWatchSession:(WCSession *_Nonnull)session API_AVAILABLE(ios(9.0)); #endif /*! diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index e34706ac..b0ffec42 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -1449,7 +1449,12 @@ - (void)_appEnteredBackground { [self persistQueues]; } [self runSerialAsync:^{ - [self updateSessionTime:(long) [[NSDate date] timeIntervalSince1970]]; + @try { + [self updateSessionTime:(long) [[NSDate date] timeIntervalSince1970]]; + } + @catch (NSException *exception) { + CleverTapLogDebug(self.config.logLevel, @"%@: Exception caught: %@", self, [exception reason]); + } }]; } @@ -2812,12 +2817,17 @@ - (void)clearNotificationsQueue { - (void)persistQueues { [self runSerialAsync:^{ - if ([self isMuted]) { - [self clearQueues]; - } else { - [self persistProfileQueue]; - [self persistEventsQueue]; - [self persistNotificationsQueue]; + @try { + if ([self isMuted]) { + [self clearQueues]; + } else { + [self persistProfileQueue]; + [self persistEventsQueue]; + [self persistNotificationsQueue]; + } + } + @catch (NSException *exception) { + CleverTapLogDebug(self.config.logLevel, @"%@: Exception caught: %@", self, [exception reason]); } }]; } @@ -4044,13 +4054,14 @@ + (void)setCredentialsWithAccountID:(NSString *)accountID token:(NSString *)toke + (void)setCredentialsWithAccountID:(NSString *)accountID token:(NSString *)token proxyDomain:(NSString *)proxyDomain spikyProxyDomain:(NSString *)spikyProxyDomain { [self _setCredentialsWithAccountID:accountID token:token proxyDomain:proxyDomain]; + NSString *spikyProxyDomainResult; if (spikyProxyDomain != nil && ![spikyProxyDomain isEqualToString:@""]) { - spikyProxyDomain = [spikyProxyDomain stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - if (spikyProxyDomain.length <= 0) { - spikyProxyDomain = nil; + spikyProxyDomainResult = [spikyProxyDomain stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if (spikyProxyDomainResult.length <= 0) { + spikyProxyDomainResult = nil; } } - [_plistInfo setCredentialsWithAccountID:accountID token:token proxyDomain:proxyDomain spikyProxyDomain:spikyProxyDomain]; + [_plistInfo setCredentialsWithAccountID:accountID token:token proxyDomain:proxyDomain spikyProxyDomain:spikyProxyDomainResult]; } + (void)enablePersonalization { @@ -4230,7 +4241,7 @@ - (CleverTapUTMDetail *)getUTMDetails { } #if defined(CLEVERTAP_HOST_WATCHOS) -- (BOOL)handleMessage:(NSDictionary *)message forWatchSession:(WCSession *)session { +- (BOOL)handleMessage:(NSDictionary *_Nonnull)message forWatchSession:(WCSession *_Nonnull)session { NSString *type = [message objectForKey:@"clevertap_type"]; BOOL handled = (type != nil); @@ -4576,7 +4587,7 @@ - (BOOL)didHandleInboxMessageTestFromPushNotificaton:(NSDictionary*)notification NSMutableDictionary *message = [NSMutableDictionary dictionary]; [message setObject:nowEpoch forKey:@"_id"]; [message setObject:[NSNumber numberWithLong:expireTime] forKey:@"wzrk_ttl"]; - [message addEntriesFromDictionary:msg]; + [message addEntriesFromDictionary:msg ?: @{}]; NSMutableArray *inboxMsg = [NSMutableArray new]; [inboxMsg addObject:message]; diff --git a/CleverTapSDK/CleverTapBuildInfo.h b/CleverTapSDK/CleverTapBuildInfo.h index a4098590..a3409210 100644 --- a/CleverTapSDK/CleverTapBuildInfo.h +++ b/CleverTapSDK/CleverTapBuildInfo.h @@ -1,3 +1,3 @@ -#define WR_SDK_REVISION @"40201" +#define WR_SDK_REVISION @"40202" diff --git a/CleverTapSDK/Inbox/models/CTUserMO+CoreDataProperties.h b/CleverTapSDK/Inbox/models/CTUserMO+CoreDataProperties.h index 6a04c364..b97b1a6a 100755 --- a/CleverTapSDK/Inbox/models/CTUserMO+CoreDataProperties.h +++ b/CleverTapSDK/Inbox/models/CTUserMO+CoreDataProperties.h @@ -6,7 +6,7 @@ NS_ASSUME_NONNULL_BEGIN @interface CTUserMO (CoreDataProperties) -+ (instancetype)fetchOrCreateFromJSON:(NSDictionary *)json forContext:(NSManagedObjectContext *)context; ++ (instancetype _Nullable)fetchOrCreateFromJSON:(NSDictionary *)json forContext:(NSManagedObjectContext *)context; - (BOOL)updateMessages:(NSArray *)messages forContext:(NSManagedObjectContext *)context; @property (nullable, nonatomic, copy) NSString *accountId; diff --git a/CleverTapSDK/Inbox/models/CTUserMO+CoreDataProperties.m b/CleverTapSDK/Inbox/models/CTUserMO+CoreDataProperties.m index d17d0896..ee0accc5 100755 --- a/CleverTapSDK/Inbox/models/CTUserMO+CoreDataProperties.m +++ b/CleverTapSDK/Inbox/models/CTUserMO+CoreDataProperties.m @@ -17,13 +17,13 @@ - (void)removeMessages:(NSOrderedSet *)values; @implementation CTUserMO (CoreDataProperties) -+ (instancetype)fetchOrCreateFromJSON:(NSDictionary *)json forContext:(NSManagedObjectContext *)context { ++ (instancetype _Nullable)fetchOrCreateFromJSON:(NSDictionary *)json forContext:(NSManagedObjectContext *)context { CTUserMO *_user; @try { NSString *identifier = json[@"identifier"]; if (!identifier) { - CleverTapLogStaticInternal(@"CTUserMO fetchOrCreate for: %@ requires an identifier returning nil", json); + CleverTapLogStaticInternal(@"CTUserMO fetchOrCreate for: %@ requires an identifier. Returning nil", json); return nil; } From e0753e32b3f0c49604ca1411001665ad4bfa18bc Mon Sep 17 00:00:00 2001 From: SonalKachare Date: Thu, 6 Apr 2023 14:54:27 +0530 Subject: [PATCH 57/86] Removed signedcallSDKVersion Updates setCustomSdkVersion func --- CleverTapSDK/CTDeviceInfo.h | 3 --- CleverTapSDK/CTDeviceInfo.m | 9 --------- CleverTapSDK/CleverTap.h | 12 +----------- CleverTapSDK/CleverTap.m | 10 ++-------- 4 files changed, 3 insertions(+), 31 deletions(-) diff --git a/CleverTapSDK/CTDeviceInfo.h b/CleverTapSDK/CTDeviceInfo.h index cf4d9f6f..ebcfc766 100644 --- a/CleverTapSDK/CTDeviceInfo.h +++ b/CleverTapSDK/CTDeviceInfo.h @@ -25,15 +25,12 @@ @property (atomic, readwrite) NSString *library; @property (assign, readonly) BOOL wifi; @property (strong, readonly) NSMutableArray* validationErrors; -@property (strong, readonly) NSString *signedCallSDKVersion; - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config andCleverTapID:(NSString *)cleverTapID; - (void)forceUpdateDeviceID:(NSString *)newDeviceID; - (void)forceNewDeviceID; - (void)forceUpdateCustomDeviceID:(NSString *)cleverTapID; - (BOOL)isErrorDeviceID; -- (void)setDirectCallSDKVersion: (NSString *)version; - (void)incrementLocalInAppCount; - (int)getLocalInAppCount; -- (void)setSignedCallSDKVersion: (NSString *)version; @end diff --git a/CleverTapSDK/CTDeviceInfo.m b/CleverTapSDK/CTDeviceInfo.m index 67205939..988db20a 100644 --- a/CleverTapSDK/CTDeviceInfo.m +++ b/CleverTapSDK/CTDeviceInfo.m @@ -40,7 +40,6 @@ static NSString *_radio; static NSString *_deviceWidth; static NSString *_deviceHeight; -static NSString *_signedCallSDKVersion; #if !CLEVERTAP_NO_REACHABILITY_SUPPORT SCNetworkReachabilityRef _reachability; @@ -472,14 +471,6 @@ - (NSString *)getCurrentRadioAccessTechnology { } #endif -- (void)setSignedCallSDKVersion: (NSString *)version { - _signedCallSDKVersion = version; -} - -- (NSString *)signedCallSDKVersion { - return _signedCallSDKVersion; -} - - (void)incrementLocalInAppCount { self.localInAppCount = self.localInAppCount + 1; [CTPreferences putInt:self.localInAppCount forKey:kCLTAP_LOCAL_INAPP_COUNT]; diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index 65326b2e..ef4d7767 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -1236,7 +1236,7 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; @discussion Call this to method to set library name and version in the Auxiliary SDK */ -- (void)setCustomSdkVersion:(NSString * _Nonnull)name version:(int)version; +- (void)setCustomSdkVersion:(NSString * _Nonnull)name version:(NSString *_Nonnull)version; /*! @method @@ -1298,16 +1298,6 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; */ - (void)recordSignedCallEvent:(int)eventRawValue forCallDetails:(NSDictionary *_Nonnull)calldetails; -/*! - @method - - @abstract - Record Signed Call SDK version. - - @param version Signed call SDK version - */ -- (void)setSignedCallVersion:(NSString* _Nullable)version; - /*! @method diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index b0ffec42..434603dc 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -1178,8 +1178,6 @@ - (NSArray *)insertHeader:(NSDictionary *)header inBatch:(NSArray *)batch { - (NSDictionary *)generateAppFields { NSMutableDictionary *evtData = [NSMutableDictionary new]; - evtData[@"scv"] = self.deviceInfo.signedCallSDKVersion; - evtData[@"Version"] = self.deviceInfo.appVersion; evtData[@"Build"] = self.deviceInfo.appBuild; @@ -4009,11 +4007,11 @@ - (void)setLibrary:(NSString *)name { self.deviceInfo.library = name; } -- (void)setCustomSdkVersion:(NSString *)name version:(int)version { +- (void)setCustomSdkVersion:(NSString *)name version:(NSString *)version { if (!auxiliarySdkVersions) { auxiliarySdkVersions = [NSMutableDictionary new]; } - auxiliarySdkVersions[name] = @(version); + auxiliarySdkVersions[name] = version; } + (void)setDebugLevel:(int)level { @@ -4999,10 +4997,6 @@ - (void)recordSignedCallEvent:(int)eventRawValue forCallDetails:(NSDictionary *) #endif } -- (void)setSignedCallVersion:(NSString *)version { - [self.deviceInfo setSignedCallSDKVersion: version]; -} - - (void)setDomainDelegate:(id)delegate { if ([[self class] runningInsideAppExtension]){ CleverTapLogDebug(self.config.logLevel, @"%@: setDomainDelegate is a no-op in an app extension.", self); From 50af1e73a2efc74c08e96faa54482270b8374bb0 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Thu, 6 Apr 2023 15:40:26 +0300 Subject: [PATCH 58/86] Implement warnIfNotStarted --- CleverTapSDK/ProductExperiences/CTVar.m | 18 ++++++++---------- CleverTapSDK/ProductExperiences/CTVarCache.h | 1 + 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index 748d496a..85b88707 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -131,22 +131,20 @@ - (void)onValueChanged:(CleverTapVariablesChangedBlock)block - (void)setDelegate:(id)delegate { CT_TRY + // TODO: call immediately if started? _delegate = delegate; CT_END_TRY } -// TODO: decide if this method is relevant - (void)warnIfNotStarted { - // TODO: Add hasStarted equivalent logic - -// if (!_isInternal && ![LPInternalState sharedState].hasStarted && ![LPVar printedCallbackWarning]) { -// LPLog(LPInfo, @"Leanplum hasn't finished retrieving values from the server. You " -// @"should use a callback to make sure the value for '%@' is ready. Otherwise, your " -// @"app may not use the most up-to-date value.", self.name); - -// [CTVar setPrintedCallbackWarning:YES]; -// } + if (!varCache.hasVarsRequestCompleted && ![CTVar printedCallbackWarning]) { + CleverTapLogDebug(varCache.config.logLevel, @"%@: CleverTap hasn't finished retrieving values from the server. You " + @"should use a callback to make sure the value for '%@' is ready. Otherwise, your " + @"app may not use the most up-to-date value.", self, self.name); + + [CTVar setPrintedCallbackWarning:YES]; + } } #pragma mark Dictionary handling diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.h b/CleverTapSDK/ProductExperiences/CTVarCache.h index 979b10c6..5285cf30 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.h +++ b/CleverTapSDK/ProductExperiences/CTVarCache.h @@ -14,6 +14,7 @@ NS_SWIFT_NAME(VarCache) - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo; +@property (nonatomic, strong, readonly) CleverTapInstanceConfig *config; @property (strong, nonatomic) NSMutableDictionary *vars; @property (assign, nonatomic) BOOL hasVarsRequestCompleted; From cc5508d8c62d43d4852499dc03e5bb6973019eff Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Thu, 6 Apr 2023 18:03:27 +0300 Subject: [PATCH 59/86] Refactor CTVar --- .../ProductExperiences/CTVar-Internal.h | 3 +- CleverTapSDK/ProductExperiences/CTVar.h | 3 +- CleverTapSDK/ProductExperiences/CTVar.m | 90 ++++++++++--------- 3 files changed, 52 insertions(+), 44 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVar-Internal.h b/CleverTapSDK/ProductExperiences/CTVar-Internal.h index 8bf5e32e..8c058885 100644 --- a/CleverTapSDK/ProductExperiences/CTVar-Internal.h +++ b/CleverTapSDK/ProductExperiences/CTVar-Internal.h @@ -11,13 +11,12 @@ NS_ASSUME_NONNULL_BEGIN withKind:(NSString *)kind varCache:(CTVarCache *)cache; -@property (readonly) BOOL isInternal; +@property (readonly, strong) CTVarCache *varCache; @property (readonly, strong) NSString *name; @property (readonly, strong) NSArray *nameComponents; @property (readonly) BOOL hadStarted; @property (readonly, strong) NSString *kind; @property (readonly, strong) NSMutableArray *valueChangedBlocks; -@property (readonly) BOOL fileIsPending; @property (nonatomic, unsafe_unretained, nullable) id delegate; @property (readonly) BOOL hasChanged; diff --git a/CleverTapSDK/ProductExperiences/CTVar.h b/CleverTapSDK/ProductExperiences/CTVar.h index 656d3756..39435202 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.h +++ b/CleverTapSDK/ProductExperiences/CTVar.h @@ -7,7 +7,6 @@ typedef void (^CleverTapVariablesChangedBlock)(void); typedef void (^CleverTapFetchVariablesBlock)(BOOL success); @class CTVar; - /** * Receives callbacks for {@link CTVar} */ @@ -17,7 +16,7 @@ NS_SWIFT_NAME(VarDelegate) /** * Called when the value of the variable changes. */ -- (void)valueDidChange:(CTVar *)var; +- (void)valueDidChange:(CTVar *)variable; @end /** diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index 85b88707..dba269d8 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -3,9 +3,9 @@ #import "CTConstants.h" static BOOL LPVAR_PRINTED_CALLBACK_WARNING = NO; -CTVarCache *varCache; + @interface CTVar (PrivateProperties) -@property (nonatomic) BOOL isInternal; +@property (nonatomic, strong) CTVarCache *varCache; @property (nonatomic, strong) NSString *name; @property (nonatomic, strong) NSArray *nameComponents; @property (nonatomic, strong) NSString *stringValue; @@ -14,29 +14,12 @@ @interface CTVar (PrivateProperties) @property (nonatomic, strong) id value; @property (nonatomic, strong) id defaultValue; @property (nonatomic, strong) NSString *kind; -@property (nonatomic, strong) NSMutableArray *fileReadyBlocks; @property (nonatomic, strong) NSMutableArray *valueChangedBlocks; -@property (nonatomic) BOOL fileIsPending; @property (nonatomic) BOOL hasChanged; @end @implementation CTVar -@synthesize stringValue=_stringValue; -@synthesize numberValue=_numberValue; -@synthesize hadStarted=_hadStarted; -@synthesize hasChanged=_hasChanged; - -+ (BOOL)printedCallbackWarning -{ - return LPVAR_PRINTED_CALLBACK_WARNING; -} - -+ (void)setPrintedCallbackWarning:(BOOL)newPrintedCallbackWarning -{ - LPVAR_PRINTED_CALLBACK_WARNING = newPrintedCallbackWarning; -} - - (instancetype)initWithName:(NSString *)name withComponents:(NSArray *)components withDefaultValue:(NSNumber *)defaultValue withKind:(NSString *)kind varCache:(CTVarCache *)cache { @@ -44,14 +27,14 @@ - (instancetype)initWithName:(NSString *)name withComponents:(NSArray *)componen if (self) { CT_TRY _name = name; - varCache = cache; - _nameComponents = [varCache getNameComponents:name]; + self.varCache = cache; + _nameComponents = [self.varCache getNameComponents:name]; _defaultValue = defaultValue; _value = defaultValue; _kind = kind; [self cacheComputedValues]; - [varCache registerVariable:self]; + [self.varCache registerVariable:self]; [self update]; CT_END_TRY @@ -59,7 +42,22 @@ - (instancetype)initWithName:(NSString *)name withComponents:(NSArray *)componen return self; } -#pragma mark Updating +// Manually @synthesize since CTVar provides custom getters/setters +// Properties are defined as readonly in CTVar-Internal +// and readwrite in PrivateProperties category +@synthesize stringValue = _stringValue; +@synthesize numberValue = _numberValue; +@synthesize varCache = _varCache; + +- (CTVarCache *)varCache { + return _varCache; +} + +- (void)setVarCache:(CTVarCache *)varCache { + _varCache = varCache; +} + +#pragma mark Updates - (void) cacheComputedValues { @@ -79,7 +77,7 @@ - (void) cacheComputedValues - (void)update { NSObject *oldValue = _value; - _value = [varCache getMergedValueFromComponentArray:_nameComponents]; + _value = [self.varCache getMergedValueFromComponentArray:_nameComponents]; if ([_value isEqual:oldValue] && _hadStarted) { return; @@ -90,13 +88,13 @@ - (void)update _hasChanged = YES; } - if (varCache.hasVarsRequestCompleted) { + if (self.varCache.hasVarsRequestCompleted) { [self triggerValueChanged]; _hadStarted = YES; } } -#pragma mark Basic accessors +#pragma mark Callbacks - (void)triggerValueChanged { @@ -122,7 +120,7 @@ - (void)onValueChanged:(CleverTapVariablesChangedBlock)block _valueChangedBlocks = [NSMutableArray array]; } [_valueChangedBlocks addObject:[block copy]]; - if (varCache.hasVarsRequestCompleted) { + if (self.varCache.hasVarsRequestCompleted) { [self triggerValueChanged]; } CT_END_TRY @@ -136,17 +134,6 @@ - (void)setDelegate:(id)delegate CT_END_TRY } -- (void)warnIfNotStarted -{ - if (!varCache.hasVarsRequestCompleted && ![CTVar printedCallbackWarning]) { - CleverTapLogDebug(varCache.config.logLevel, @"%@: CleverTap hasn't finished retrieving values from the server. You " - @"should use a callback to make sure the value for '%@' is ready. Otherwise, your " - @"app may not use the most up-to-date value.", self, self.name); - - [CTVar setPrintedCallbackWarning:YES]; - } -} - #pragma mark Dictionary handling - (id) objectForKey:(NSString *)key @@ -171,7 +158,7 @@ - (id) objectForKeyPath:(id)firstComponent, ... [components addObject:component]; } va_end(args); - return [varCache getMergedValueFromComponentArray:components]; + return [self.varCache getMergedValueFromComponentArray:components]; CT_END_TRY return nil; } @@ -182,7 +169,7 @@ - (id)objectForKeyPathComponents:(NSArray *)pathComponents [self warnIfNotStarted]; NSMutableArray *components = [_nameComponents mutableCopy]; [components addObjectsFromArray:pathComponents]; - return [varCache getMergedValueFromComponentArray:components]; + return [self.varCache getMergedValueFromComponentArray:components]; CT_END_TRY return nil; } @@ -218,4 +205,27 @@ - (NSUInteger)unsignedIntegerValue { return [[self numberValue] unsignedIntegerV - (unsigned long)unsignedLongValue { return [[self numberValue] unsignedLongValue]; } - (unsigned long long)unsignedLongLongValue { return [[self numberValue] unsignedLongLongValue]; } +#pragma mark Utils + ++ (BOOL)printedCallbackWarning +{ + return LPVAR_PRINTED_CALLBACK_WARNING; +} + ++ (void)setPrintedCallbackWarning:(BOOL)newPrintedCallbackWarning +{ + LPVAR_PRINTED_CALLBACK_WARNING = newPrintedCallbackWarning; +} + +- (void)warnIfNotStarted +{ + if (!self.varCache.hasVarsRequestCompleted && ![CTVar printedCallbackWarning]) { + CleverTapLogDebug(self.varCache.config.logLevel, @"%@: CleverTap hasn't finished retrieving values from the server. You " + @"should use a callback to make sure the value for '%@' is ready. Otherwise, your " + @"app may not use the most up-to-date value.", self, self.name); + + [CTVar setPrintedCallbackWarning:YES]; + } +} + @end From be258271c04712b58b69d42ec3ac5e79ece2f9b6 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Thu, 6 Apr 2023 18:05:54 +0300 Subject: [PATCH 60/86] Fix var updates on error --- CleverTapSDK/ProductExperiences/CTVariables.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index aa40fee3..e49c82fc 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -72,6 +72,8 @@ - (void)handleVariablesError { if (![[self varCache] hasVarsRequestCompleted]) { [[self varCache] setHasVarsRequestCompleted:YES]; + // Ensure variables are loaded from cache. Triggers individual Vars update. + [[self varCache] loadDiffs]; [self triggerVariablesChanged]; } else { [self triggerFetchVariables:NO]; From dcd547c87d6f9ee4d8e9c774415e125342b0d8f0 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Fri, 7 Apr 2023 19:14:32 +0300 Subject: [PATCH 61/86] Fix getVariableValue --- CleverTapSDK/CleverTap.m | 3 +-- CleverTapSDK/ProductExperiences/CTVarCache.m | 10 +++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 542fafe3..2d9bc50a 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -5128,8 +5128,7 @@ - (CTVar * _Nullable)getVariable:(NSString * _Nonnull)name { } - (id _Nullable)getVariableValue:(NSString * _Nonnull)name { - // TODO: return a copy - return [[self.variables varCache] getVariable:name]; + return [[self.variables varCache] getMergedValue:name]; } #pragma mark - PE Vars diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 6275d3f6..bc242983 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -144,7 +144,15 @@ - (CTVar *)getVariable:(NSString *)name - (id)getMergedValue:(NSString *)name { NSArray *components = [self getNameComponents:name]; - return [self getMergedValueFromComponentArray:components]; + id value = [self getMergedValueFromComponentArray:components]; + if ([value conformsToProtocol:@protocol(NSCopying)] && [value respondsToSelector:@selector(copyWithZone:)]) { + if ([value respondsToSelector:@selector(mutableCopy)]) { + return [value mutableCopy]; + } + return [value copy]; + } + + return value; } - (id)getValueFromComponentArray:(NSArray *) components fromDict:(NSDictionary *)values From 30f4740a7e4c0864dabedef16be95e29fd5ed316 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 10 Apr 2023 16:42:18 +0300 Subject: [PATCH 62/86] Execute delegate if hasVarsRequestCompleted --- CleverTapSDK/ProductExperiences/CTVar.m | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index dba269d8..cdd84d30 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -88,7 +88,7 @@ - (void)update _hasChanged = YES; } - if (self.varCache.hasVarsRequestCompleted) { + if ([[self varCache] hasVarsRequestCompleted]) { [self triggerValueChanged]; _hadStarted = YES; } @@ -120,7 +120,7 @@ - (void)onValueChanged:(CleverTapVariablesChangedBlock)block _valueChangedBlocks = [NSMutableArray array]; } [_valueChangedBlocks addObject:[block copy]]; - if (self.varCache.hasVarsRequestCompleted) { + if ([[self varCache] hasVarsRequestCompleted]) { [self triggerValueChanged]; } CT_END_TRY @@ -129,8 +129,10 @@ - (void)onValueChanged:(CleverTapVariablesChangedBlock)block - (void)setDelegate:(id)delegate { CT_TRY - // TODO: call immediately if started? _delegate = delegate; + if ([[self varCache] hasVarsRequestCompleted]) { + [self triggerValueChanged]; + } CT_END_TRY } From 110261a186e4e9dff9fab332f28bff9203c5c049 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 10 Apr 2023 16:42:44 +0300 Subject: [PATCH 63/86] Note callback can be overridden --- CleverTapSDK/ProductExperiences/CTVariables.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index e49c82fc..6b60f41d 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -80,7 +80,6 @@ - (void)handleVariablesError } } -// TODO: callback is overridden after second call if first is not ready yet - (void)triggerFetchVariables:(BOOL)success { if (self.fetchVariablesBlock) { CleverTapFetchVariablesBlock block = [self.fetchVariablesBlock copy]; @@ -91,7 +90,9 @@ - (void)triggerFetchVariables:(BOOL)success { } else { block(success); } - + // The callback will be overridden by subsequent fetch call, + // if the first one has not completed yet. + // Callback cannot be attached to an individual fetch request, only to the queue batch. self.fetchVariablesBlock = nil; } } From d8c2b449402d5b5a011dc14666c0bd05591051e7 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Mon, 10 Apr 2023 19:56:35 +0300 Subject: [PATCH 64/86] Fix mergedValue copy --- CleverTapSDK/ProductExperiences/CTVarCache.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index bc242983..ee92d8da 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -146,7 +146,7 @@ - (id)getMergedValue:(NSString *)name NSArray *components = [self getNameComponents:name]; id value = [self getMergedValueFromComponentArray:components]; if ([value conformsToProtocol:@protocol(NSCopying)] && [value respondsToSelector:@selector(copyWithZone:)]) { - if ([value respondsToSelector:@selector(mutableCopy)]) { + if ([value respondsToSelector:@selector(mutableCopyWithZone:)]) { return [value mutableCopy]; } return [value copy]; From 6ea06238773a8f481928d1d62c98f1dae30671ea Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Tue, 11 Apr 2023 12:17:44 +0300 Subject: [PATCH 65/86] Fix loading vars for new user --- CleverTapSDK/ProductExperiences/CTVar.h | 2 ++ CleverTapSDK/ProductExperiences/CTVar.m | 6 ++++++ CleverTapSDK/ProductExperiences/CTVarCache.h | 1 + CleverTapSDK/ProductExperiences/CTVarCache.m | 15 ++++++++++++--- CleverTapSDK/ProductExperiences/CTVariables.h | 1 + CleverTapSDK/ProductExperiences/CTVariables.m | 8 +++++++- 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVar.h b/CleverTapSDK/ProductExperiences/CTVar.h index 39435202..8c347875 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.h +++ b/CleverTapSDK/ProductExperiences/CTVar.h @@ -73,6 +73,8 @@ NS_SWIFT_NAME(Var) */ - (void)setDelegate:(nullable id )delegate; +- (void)clearState; + /** * @{ * Accessess the value(s) of the variable diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index cdd84d30..a979194c 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -230,4 +230,10 @@ - (void)warnIfNotStarted } } +- (void)clearState +{ + _hadStarted = NO; + _hasChanged = NO; +} + @end diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.h b/CleverTapSDK/ProductExperiences/CTVarCache.h index 5285cf30..9a8a7996 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.h +++ b/CleverTapSDK/ProductExperiences/CTVarCache.h @@ -28,6 +28,7 @@ NS_SWIFT_NAME(VarCache) - (NSArray *)getNameComponents:(NSString *)name; - (nullable id)getMergedValueFromComponentArray:(NSArray *) components; +- (void)clearUserContent; @end diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index ee92d8da..44225a23 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -176,6 +176,7 @@ - (void)loadDiffs NSString *filePath = [CTPreferences filePathfromFileName:fileName]; NSData *diffsData = [NSData dataWithContentsOfFile:filePath]; if (!diffsData) { + [self applyVariableDiffs:@{}]; return; } NSKeyedUnarchiver *unarchiver; @@ -205,8 +206,7 @@ - (void)saveDiffs { // Stores the variables on the device in case we don't have a connection. // Restores next time when the app is opened. - // Diffs need to be locked incase other thread changes the diffs using - // mergeHelper:. + // Diffs need to be locked incase other thread changes the diffs @synchronized (self.diffs) { NSMutableData *diffsData = [[NSMutableData alloc] init]; #pragma clang diagnostic push @@ -238,7 +238,6 @@ - (void)applyVariableDiffs:(NSDictionary *)diffs_ if (diffs_ != nil && ![diffs_ isEqual:[NSNull null]]) { self.diffs = diffs_; - // Merger helper will mutate diffs. // We need to lock it in case multiple threads will be accessing this. @synchronized (self.diffs) { self.merged = [ContentMerger mergeWithVars:self.valuesFromClient diff:self.diffs]; @@ -262,4 +261,14 @@ - (void)applyVariableDiffs:(NSDictionary *)diffs_ } } +- (void)clearUserContent { + // Disable callbacks and wait until fetch is finished + [self setHasVarsRequestCompleted:NO]; + // Clear Var state to allow callback invocation when server values are downloaded + [self.vars enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + CTVar *var = (CTVar *)obj; + [var clearState]; + }]; +} + @end diff --git a/CleverTapSDK/ProductExperiences/CTVariables.h b/CleverTapSDK/ProductExperiences/CTVariables.h index f7569f3d..97229cb0 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.h +++ b/CleverTapSDK/ProductExperiences/CTVariables.h @@ -33,6 +33,7 @@ NS_SWIFT_NAME(define(name:value:kind:)); - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName; - (NSDictionary*)varsPayload; - (NSDictionary*)unflatten:(NSDictionary*)result; +- (void)clearUserContent; @end diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 6b60f41d..3c2770f5 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -75,11 +75,17 @@ - (void)handleVariablesError // Ensure variables are loaded from cache. Triggers individual Vars update. [[self varCache] loadDiffs]; [self triggerVariablesChanged]; - } else { + } + + if (self.fetchVariablesBlock) { [self triggerFetchVariables:NO]; } } +- (void)clearUserContent { + [self.varCache clearUserContent]; +} + - (void)triggerFetchVariables:(BOOL)success { if (self.fetchVariablesBlock) { CleverTapFetchVariablesBlock block = [self.fetchVariablesBlock copy]; From 6d7537f609513d641f4712867a6916cc01c8d2a7 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Tue, 11 Apr 2023 12:18:21 +0300 Subject: [PATCH 66/86] Fix reset vars --- CleverTapSDK/CleverTap.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 2d9bc50a..6d968df8 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -4700,9 +4700,9 @@ - (void)_resetProductConfig { // run off main - (void)_resetVars { - if (self.config && self.deviceInfo.deviceId) { - self.variables = [[CTVariables alloc]initWithConfig:self.config deviceInfo:self.deviceInfo]; - } + /// Clear content for current user + /// Content for new user will be loaded in `recordAppLaunched:` using `CTVarCache.loadDiffs` + [[self variables] clearUserContent]; } - (NSDictionary *)_setProductConfig:(NSDictionary *)arp { From 939a2bf06ca46bc4d24882e5e7ac8e1c5245e017 Mon Sep 17 00:00:00 2001 From: SonalKachare Date: Tue, 11 Apr 2023 18:00:12 +0530 Subject: [PATCH 67/86] Updates version handling to int --- CleverTapSDK/CleverTap.h | 2 +- CleverTapSDK/CleverTap.m | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index ef4d7767..8e3fb5e9 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -1236,7 +1236,7 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; @discussion Call this to method to set library name and version in the Auxiliary SDK */ -- (void)setCustomSdkVersion:(NSString * _Nonnull)name version:(NSString *_Nonnull)version; +- (void)setCustomSdkVersion:(NSString * _Nonnull)name version:(int)version; /*! @method diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 434603dc..6b9113e1 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -4007,11 +4007,11 @@ - (void)setLibrary:(NSString *)name { self.deviceInfo.library = name; } -- (void)setCustomSdkVersion:(NSString *)name version:(NSString *)version { +- (void)setCustomSdkVersion:(NSString *)name version:(int)version { if (!auxiliarySdkVersions) { auxiliarySdkVersions = [NSMutableDictionary new]; } - auxiliarySdkVersions[name] = version; + auxiliarySdkVersions[name] = @(version); } + (void)setDebugLevel:(int)level { From 4d5679331f62322b7c5afadbc542548d70e0d9fb Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Tue, 11 Apr 2023 16:47:02 +0300 Subject: [PATCH 68/86] Fix tests. Add CTVar tests. --- CleverTapSDK.xcodeproj/project.pbxproj | 4 + CleverTapSDKTests/CTVarCache+Tests.h | 1 + CleverTapSDKTests/CTVarCacheMock.h | 6 + CleverTapSDKTests/CTVarCacheMock.m | 15 +- CleverTapSDKTests/CTVarCacheTest.m | 349 ++++++++++++++----------- CleverTapSDKTests/CTVarTest.m | 228 ++++++++++++++++ CleverTapSDKTests/CTVariables+Tests.h | 2 +- CleverTapSDKTests/CTVariablesTest.m | 98 +++++++ 8 files changed, 544 insertions(+), 159 deletions(-) create mode 100644 CleverTapSDKTests/CTVarTest.m diff --git a/CleverTapSDK.xcodeproj/project.pbxproj b/CleverTapSDK.xcodeproj/project.pbxproj index bd3169f5..4b59ec28 100644 --- a/CleverTapSDK.xcodeproj/project.pbxproj +++ b/CleverTapSDK.xcodeproj/project.pbxproj @@ -258,6 +258,7 @@ 6A775C3429BE78C7007790E0 /* CTVariables.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A775C3129BE78C7007790E0 /* CTVariables.h */; settings = {ATTRIBUTES = (Private, ); }; }; 6A775C3529BE78C7007790E0 /* CTVariables.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A775C3229BE78C7007790E0 /* CTVariables.m */; }; 6A775C3629BE78C7007790E0 /* CTVariables.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A775C3229BE78C7007790E0 /* CTVariables.m */; }; + 6A7BB8DC29E47CFF00651584 /* CTVarTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A7BB8DB29E47CFF00651584 /* CTVarTest.m */; }; D0047B0A2098D45B0019C6FD /* CTLocalDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = D0047B082098D45B0019C6FD /* CTLocalDataStore.h */; settings = {ATTRIBUTES = (Private, ); }; }; D0047B0B2098D45B0019C6FD /* CTLocalDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = D0047B092098D45B0019C6FD /* CTLocalDataStore.m */; }; D0047B0E2098E2F00019C6FD /* CTProfileBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = D0047B0C2098E2F00019C6FD /* CTProfileBuilder.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -658,6 +659,7 @@ 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CleverTapInstanceConfigTests.m; sourceTree = ""; }; 6A775C3129BE78C7007790E0 /* CTVariables.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTVariables.h; sourceTree = ""; }; 6A775C3229BE78C7007790E0 /* CTVariables.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVariables.m; sourceTree = ""; }; + 6A7BB8DB29E47CFF00651584 /* CTVarTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTVarTest.m; sourceTree = ""; }; 840B38DDFBBEAE05FC6C5458 /* Pods-shared-CleverTapSDKTestsApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-shared-CleverTapSDKTestsApp.release.xcconfig"; path = "Target Support Files/Pods-shared-CleverTapSDKTestsApp/Pods-shared-CleverTapSDKTestsApp.release.xcconfig"; sourceTree = ""; }; 917B24847F7C2A8B0825AEC1 /* libPods-shared-CleverTapSDKTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-shared-CleverTapSDKTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; A1765572FACEA4B081A89F82 /* Pods-shared-CleverTapSDKTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-shared-CleverTapSDKTests.debug.xcconfig"; path = "Target Support Files/Pods-shared-CleverTapSDKTests/Pods-shared-CleverTapSDKTests.debug.xcconfig"; sourceTree = ""; }; @@ -1089,6 +1091,7 @@ 6A2E0B9629D49D5100FCEA5F /* CTVarCacheMock.h */, 6A2E0B9729D49D5100FCEA5F /* CTVarCacheMock.m */, 6A2E0B9929D49E4700FCEA5F /* CTVariables+Tests.h */, + 6A7BB8DB29E47CFF00651584 /* CTVarTest.m */, ); path = CleverTapSDKTests; sourceTree = ""; @@ -1874,6 +1877,7 @@ 4E1F154F27691CA0009387AE /* CleverTapInstanceTests.m in Sources */, 4E1F155B276B662C009387AE /* EventDetail.m in Sources */, 4EED219B29AF6368006CEA19 /* CTVarCacheTest.m in Sources */, + 6A7BB8DC29E47CFF00651584 /* CTVarTest.m in Sources */, 6A2E0B9129CCCC8600FCEA5F /* ContentMergerTest.m in Sources */, 6A2E0B9529D49D0200FCEA5F /* CTVariables+Tests.m in Sources */, 6A2E0B9829D49D5100FCEA5F /* CTVarCacheMock.m in Sources */, diff --git a/CleverTapSDKTests/CTVarCache+Tests.h b/CleverTapSDKTests/CTVarCache+Tests.h index 4d6440de..9f39df79 100644 --- a/CleverTapSDKTests/CTVarCache+Tests.h +++ b/CleverTapSDKTests/CTVarCache+Tests.h @@ -11,6 +11,7 @@ NS_ASSUME_NONNULL_BEGIN @interface CTVarCache (Tests) +@property (strong, nonatomic) NSMutableDictionary *valuesFromClient; - (NSString *)getArchiveFileName; - (id)traverse:(id)collection withKey:(id)key autoInsert:(BOOL)autoInsert; - (void)saveDiffs; diff --git a/CleverTapSDKTests/CTVarCacheMock.h b/CleverTapSDKTests/CTVarCacheMock.h index 443e789b..1021e423 100644 --- a/CleverTapSDKTests/CTVarCacheMock.h +++ b/CleverTapSDKTests/CTVarCacheMock.h @@ -13,6 +13,12 @@ NS_ASSUME_NONNULL_BEGIN @interface CTVarCacheMock : CTVarCache +@property int loadCount; +@property int applyCount; +@property int saveCount; + +- (void)originalSaveDiffs; + @end NS_ASSUME_NONNULL_END diff --git a/CleverTapSDKTests/CTVarCacheMock.m b/CleverTapSDKTests/CTVarCacheMock.m index 89100b1d..1a14f98f 100644 --- a/CleverTapSDKTests/CTVarCacheMock.m +++ b/CleverTapSDKTests/CTVarCacheMock.m @@ -7,15 +7,28 @@ // #import "CTVarCacheMock.h" +#import "CTVarCache+Tests.h" @implementation CTVarCacheMock - (void)loadDiffs { - // Do NOT read from file + self.loadCount++; + [super loadDiffs]; +} + +- (void)applyVariableDiffs:(NSDictionary *)diffs_ { + self.applyCount++; + [super applyVariableDiffs:diffs_]; } - (void)saveDiffs { // Do NOT save to file + self.saveCount++; +} + +- (void)originalSaveDiffs { + // Save to file + [super saveDiffs]; } @end diff --git a/CleverTapSDKTests/CTVarCacheTest.m b/CleverTapSDKTests/CTVarCacheTest.m index 87fa1b35..5e2b5281 100644 --- a/CleverTapSDKTests/CTVarCacheTest.m +++ b/CleverTapSDKTests/CTVarCacheTest.m @@ -9,58 +9,76 @@ #import #import "CTVariables.h" #import "CTVarCache.h" -#import #import "CTUtils.h" #import "CTPreferences.h" #import "CTVarCache+Tests.h" #import "CTVarCacheMock.h" #import "CTVariables+Tests.h" #import "CTConstants.h" -#import -#import -#import -#import "CTRequestFactory.h" - @interface CTVarCacheTest : XCTestCase @end -CleverTapInstanceConfig *config; -CTDeviceInfo *deviceInfo; CTVariables *variables; @implementation CTVarCacheTest - (void)setUp { - config = [[CleverTapInstanceConfig alloc] initWithAccountId:@"id" accountToken:@"token" accountRegion:@"eu"]; + CleverTapInstanceConfig *config = [[CleverTapInstanceConfig alloc] initWithAccountId:@"id" accountToken:@"token" accountRegion:@"eu"]; config.useCustomCleverTapId = YES; - deviceInfo = [[CTDeviceInfo alloc] initWithConfig:config andCleverTapID:@"test"]; + CTDeviceInfo *deviceInfo = [[CTDeviceInfo alloc] initWithConfig:config andCleverTapID:@"test"]; CTVarCacheMock *varCache = [[CTVarCacheMock alloc] initWithConfig:config deviceInfo:deviceInfo]; variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo varCache:varCache]; -// variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo]; } - (void)tearDown { variables = nil; } -- (void)testVarCacheFetchesNameComponents { +#pragma mark Name Components +- (void)testNameComponents { + NSString *name = @""; + NSArray *components = [[variables varCache] getNameComponents:name]; + XCTAssertEqual(1, [components count]); + XCTAssertEqual(name, components[0]); + + NSString *name1 = @"my var 1"; + NSArray *components1 = [[variables varCache] getNameComponents:name1]; + XCTAssertEqual(1, [components1 count]); + XCTAssertEqual(name1, components1[0]); + + NSString *name2 = @" "; + NSArray *components2 = [[variables varCache] getNameComponents:name2]; + XCTAssertEqual(1, [components2 count]); + XCTAssertEqual(name2, components2[0]); + + NSString *name3 = @"var 2.var4. var 5 "; + NSArray *components3 = [[variables varCache] getNameComponents:name3]; + XCTAssertEqual(3, [components3 count]); + XCTAssertEqualObjects((@[@"var 2", @"var4", @" var 5 "]), components3); + + NSString *name4 = @"&"; + NSArray *components4 = [[variables varCache] getNameComponents:name4]; + XCTAssertEqual(1, [components4 count]); + XCTAssertEqual(name4, components4[0]); + + NSString *name5 = @"var[0]"; + NSArray *components5 = [[variables varCache] getNameComponents:name5]; + XCTAssertEqual(1, [components5 count]); + XCTAssertEqual(name5, components5[0]); + NSString *component1 = @"first"; NSString *component2 = @"second"; NSString *component3 = @"third"; NSArray *nameComponents = [[variables varCache] getNameComponents:[NSString stringWithFormat:@"%@.%@.%@",component1,component2,component3]]; XCTAssertNotNil(nameComponents); - - BOOL expression = [nameComponents containsObject:component1] && [nameComponents containsObject:component2] && [nameComponents containsObject:component3]; - XCTAssertTrue(expression); - + XCTAssertEqualObjects((@[component1, component2, component3]), nameComponents); XCTAssertTrue(nameComponents.count == 3); } -// TODO: check test +#pragma mark Traverse - (void)testTraverse { - // Create a dictionary for testing purposes NSDictionary *dictionary = @{ @"key1": @"value1", @"key2": @{ @@ -69,15 +87,15 @@ - (void)testTraverse { } }; - // Test that the method returns the correct value when the key exists in the dictionary + // Returns the correct value when the key exists in the dictionary id result = [variables.varCache traverse:dictionary withKey:@"key1" autoInsert:NO]; XCTAssertEqualObjects(result, @"value1"); - // Test that the method returns nil when the key does not exist in the dictionary + // Returns nil when the key does not exist in the dictionary result = [variables.varCache traverse:dictionary withKey:@"key3" autoInsert:NO]; XCTAssertNil(result); - // Test that the method returns nil when the value for the key is NSNull + // Returns nil when the value for the key is NSNull NSDictionary *dictionaryWithNull = @{ @"key1": [NSNull null] }; @@ -94,36 +112,23 @@ - (void)testTraverseWithAutoInsert { } }; - // Test that the method creates a new dictionary and adds it to the collection when autoInsert is true and the key does not exist + // Creates a new dictionary and adds it to the collection when autoInsert is true and the key does not exist NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary]; [variables.varCache traverse:mutableDictionary withKey:@"newKey" autoInsert:YES]; XCTAssertTrue([mutableDictionary objectForKey:@"newKey"] != nil); XCTAssertTrue([[mutableDictionary objectForKey:@"newKey"] isKindOfClass:[NSMutableDictionary class]]); - // Test that the method does not create a new dictionary when autoInsert is false and the key does not exist + // Does not create a new dictionary when autoInsert is false and the key does not exist [variables.varCache traverse:mutableDictionary withKey:@"newKey2" autoInsert:NO]; XCTAssertNil([mutableDictionary objectForKey:@"newKey2"]); } - -// TODO: check test -- (void)testVarCacheResgitersVars { - CTVar *varMock = OCMPartialMock([variables define:@"test" with:@"test" kind:CT_KIND_STRING]); - [variables.varCache registerVariable:varMock]; - - XCTAssertEqual(variables.varCache.vars[varMock.name], varMock); -} - -// TODO: check test -- (void)testVarCacheGetVarsForName { - NSString *varName = @"test"; - CTVar *var = [variables define:varName with:@"test" kind:CT_KIND_STRING]; - CTVar *varResult = [variables.varCache getVariable:varName]; - - XCTAssertEqual(varResult, var); +#pragma mark Register Variables +- (void)testRegisterVars { + CTVar *var = [variables define:@"test" with:@"test" kind:CT_KIND_STRING]; + XCTAssertEqual(variables.varCache.vars[var.name], var); } -#pragma mark Register Variables - (void)testRegisterVariableWithGroup { [variables define:@"group.var1" with:@"value1" kind:CT_KIND_STRING]; [variables define:@"group" with:@{ @@ -170,16 +175,16 @@ - (void)testRegisterVariableWithNestedGroup { XCTAssertEqualObjects(expectedGroup1Value, [varCache getVariable:@"group1"].value); } -#pragma mark GetMergedValue -- (void)testVarCacheGetMergedValue { +#pragma mark Get Merged Value +- (void)testGetMergedValue { NSString *varName = @"var1"; [variables define:varName with:@"value1" kind:CT_KIND_STRING]; NSString *value = [variables.varCache getMergedValue:varName]; - - XCTAssertEqual(@"value1", value); + + XCTAssertEqualObjects(@"value1", value); } -- (void)testVarCacheGetMergedValueWithGroup { +- (void)testGetMergedValueWithGroup { [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_STRING]; [variables define:@"group1.group2.var3" with:@NO kind:CT_KIND_BOOLEAN]; [variables define:@"group1" with:@{ @@ -197,11 +202,11 @@ - (void)testVarCacheGetMergedValueWithGroup { @"var3": @NO, } }; - + XCTAssertEqualObjects(expectedGroup1Value, [variables.varCache getMergedValue:@"group1"]); } -- (void)testVarCacheGetMergedValueWithGroups { +- (void)testGetMergedValueWithGroups { [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_STRING]; [variables define:@"group1.group2.var3" with:@NO kind:CT_KIND_BOOLEAN]; [variables define:@"group1" with:@{ @@ -212,19 +217,68 @@ - (void)testVarCacheGetMergedValueWithGroups { } kind:CT_KIND_DICTIONARY]; [variables define:@"var5" with:@"value5" kind:CT_KIND_STRING]; + + XCTAssertEqualObjects(@"value1", [variables.varCache getMergedValue:@"group1.var1"]); + XCTAssertEqualObjects(@2, [variables.varCache getMergedValue:@"group1.var2"]); + XCTAssertEqualObjects(@NO, [variables.varCache getMergedValue:@"group1.group2.var3"]); + XCTAssertEqualObjects(@4, [variables.varCache getMergedValue:@"group1.group2.var4"]); + + XCTAssertEqualObjects(@"value5", [variables.varCache getMergedValue:@"var5"]); +} - XCTAssertEqual(@"value1", [variables.varCache getMergedValue:@"group1.var1"]); - XCTAssertEqual(@2, [variables.varCache getMergedValue:@"group1.var2"]); - XCTAssertEqual(@NO, [variables.varCache getMergedValue:@"group1.group2.var3"]); - XCTAssertEqual(@4, [variables.varCache getMergedValue:@"group1.group2.var4"]); +#pragma mark Get Variable +- (void)testGetVariable { + NSString *varName = @"var"; + CTVar *var = [variables define:varName with:@"value" kind:CT_KIND_STRING]; + CTVar *varResult = [variables.varCache getVariable:varName]; - XCTAssertEqual(@"value5", [variables.varCache getMergedValue:@"var5"]); + XCTAssertEqual(varResult, var); +} + +- (void)testGetVariableGroup { + NSString *varName = @"group.var"; + CTVar *var = [variables define:varName with:@"value" kind:CT_KIND_STRING]; + CTVar *varResult = [variables.varCache getVariable:varName]; + CTVar *varGroupResult = [variables.varCache getVariable:@"group"]; + + XCTAssertEqual(varResult, var); + XCTAssertNil(varGroupResult); + + CTVar *varDict = [variables define:@"dict" with:@{} kind:CT_KIND_DICTIONARY]; + CTVar *varDictResult = [variables.varCache getVariable:@"dict"]; + XCTAssertEqual(varDictResult, varDict); +} + +#pragma mark Merge Variable +- (void)testMergeVariable { + NSDictionary *diffs = @{ + @"var1": @2, + @"group1": @{ + @"var1": @"value2", + @"group2": @{ + @"var2": @YES, + } + } + }; + // Apply diffs first, before defining the variable + [variables.varCache applyVariableDiffs:diffs]; + + CTVar *var1 = [variables define:@"var1" with:@1 kind:CT_KIND_INT]; + CTVar *group1_var1 = [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_STRING]; + CTVar *group1_group2 = [variables define:@"group1.group2" with:@{ + @"var2": @NO, + @"var3": @3, + } kind:CT_KIND_DICTIONARY]; + + XCTAssertEqual(@2, var1.value); + XCTAssertEqual(@"value2", group1_var1.value); + XCTAssertEqualObjects((@{ @"var2": @YES, @"var3": @3 }), group1_group2.value); } #pragma mark Apply Diffs -- (void)testVarCacheApplyDiffs { +- (void)testApplyDiffs { CTVar *var1 = [variables define:@"var1" with:@1 kind:CT_KIND_INT]; - CTVar *group1_var1 = [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_INT]; + CTVar *group1_var1 = [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_STRING]; CTVar *var3 = [variables define:@"group1.group2.var3" with:@NO kind:CT_KIND_BOOLEAN]; NSDictionary *diffs = @{ @@ -239,14 +293,14 @@ - (void)testVarCacheApplyDiffs { }; [variables.varCache applyVariableDiffs:diffs]; - - XCTAssertEqual(@2, var1.value); - XCTAssertEqual(@"value2", group1_var1.value); - XCTAssertEqual(@YES, var3.value); - XCTAssertEqual(@"value22", [variables.varCache getMergedValue:@"group1.var22"]); + + XCTAssertEqualObjects(@2, var1.value); + XCTAssertEqualObjects(@"value2", group1_var1.value); + XCTAssertEqualObjects(@YES, var3.value); + XCTAssertEqualObjects(@"value22", [variables.varCache getMergedValue:@"group1.var22"]); } -- (void)testVarCacheApplyDiffsDefaultValue { +- (void)testApplyDiffsDefaultValue { CTVar *var1 = [variables define:@"var1" with:@1 kind:CT_KIND_INT]; CTVar *group1_var1 = [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_STRING]; CTVar *var3 = [variables define:@"group1.group2.var3" with:@NO kind:CT_KIND_BOOLEAN]; @@ -258,14 +312,14 @@ - (void)testVarCacheApplyDiffsDefaultValue { }; [variables.varCache applyVariableDiffs:diffs]; - - XCTAssertEqual(@1, var1.value); - XCTAssertEqual(@"value1", group1_var1.value); - XCTAssertEqual(@NO, var3.value); - XCTAssertEqual(@"value22", [variables.varCache getMergedValue:@"group1.var22"]); + + XCTAssertEqualObjects(@1, var1.value); + XCTAssertEqualObjects(@"value1", group1_var1.value); + XCTAssertEqualObjects(@NO, var3.value); + XCTAssertEqualObjects(@"value22", [variables.varCache getMergedValue:@"group1.var22"]); } -- (void)testVarCacheApplyDiffsGroup { +- (void)testApplyDiffsGroup { CTVar *var1 = [variables define:@"group1.group2.var1" with:@1 kind:CT_KIND_INT]; CTVar *group1_group2 = [variables define:@"group1.group2" with:@{ @"var2": @"value2" @@ -278,7 +332,7 @@ - (void)testVarCacheApplyDiffsGroup { } } }; - + [variables.varCache applyVariableDiffs:diffs]; NSDictionary *group1_group2_value = @{ @@ -286,59 +340,64 @@ - (void)testVarCacheApplyDiffsGroup { @"var2": @"value2", @"var3": @"value3" }; - - XCTAssertEqual(@1, var1.value); + + XCTAssertEqualObjects(@1, var1.value); XCTAssertEqualObjects(group1_group2_value, group1_group2.value); - XCTAssertEqual(@"value3", [variables.varCache getMergedValue:@"group1.group2.var3"]); + XCTAssertEqualObjects(@"value3", [variables.varCache getMergedValue:@"group1.group2.var3"]); } -// TODO: test defining var later -- (void)testVarCacheMergeVariable { +#pragma mark Save Diffs +- (void)testLoadSaveSpecialCharacters { + CTVar *var1 = [variables define:@"&" with:@"&" kind:CT_KIND_STRING]; + CTVar *var2 = [variables define:@"[var]" with:@"[var]" kind:CT_KIND_STRING]; + CTVar *var3 = [variables define:@" " with:@" " kind:CT_KIND_STRING]; + + CTVar *var4 = [variables define:@"<<" with:@"<<" kind:CT_KIND_STRING]; + CTVar *var5 = [variables define:@"and&" with:@"and&" kind:CT_KIND_STRING]; + CTVar *var6 = [variables define:@"'a" with:@"'a" kind:CT_KIND_STRING]; + + CTVarCacheMock *varCache = (CTVarCacheMock *)variables.varCache; + NSDictionary *diffs = @{ - @"var1": @2, - @"group1": @{ - @"var1": @"value2", - @"group2": @{ - @"var2": @YES, - } - } + @"&": @"&", + @"[var]": @"[var2]", + @" ": @" ", + @"<<": @"<<<", + @"and&": @"b&", + @"'a": @"'b" }; - // Apply diffs first, before defining the variable - [variables.varCache applyVariableDiffs:diffs]; - CTVar *var1 = [variables define:@"var1" with:@1 kind:CT_KIND_INT]; - CTVar *group1_var1 = [variables define:@"group1.var1" with:@"value1" kind:CT_KIND_STRING]; - CTVar *group1_group2 = [variables define:@"group1.group2" with:@{ - @"var2": @NO, - @"var3": @3, - } kind:CT_KIND_DICTIONARY]; - - XCTAssertEqual(@2, var1.value); - XCTAssertEqual(@"value2", group1_var1.value); - XCTAssertEqualObjects((@{ @"var2": @YES, @"var3": @3 }), group1_group2.value); -} - -- (void)testVarCacheMergeVariableNestedGroups { - + [varCache applyVariableDiffs:diffs]; + // Call original saveDiffs and write to file + [varCache originalSaveDiffs]; + [varCache loadDiffs]; + + XCTAssertEqualObjects(@"&", var1.stringValue); + XCTAssertEqualObjects(@"[var2]", var2.stringValue); + XCTAssertEqualObjects(@" ", var3.stringValue); + XCTAssertEqualObjects(@"<<<", var4.stringValue); + XCTAssertEqualObjects(@"b&", var5.stringValue); + XCTAssertEqualObjects(@"'b", var6.stringValue); + + [self deleteSavedFile:[variables.varCache getArchiveFileName]]; } - -- (void)testVarCacheSavesDiffs { +- (void)testSavesDiffs { + CTVarCacheMock *varCache = (CTVarCacheMock *)variables.varCache; + [variables define:@"var1" with:@"value" kind:CT_KIND_STRING]; - variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo]; + NSDictionary *diff = @{ + @"var1": @"new value", + }; + [variables handleVariablesResponse:diff]; - id variablesMock = OCMPartialMock(variables); - [variablesMock define:@"Title" with:@"Hello" kind:CT_KIND_STRING]; + XCTAssertEqual(1, varCache.saveCount); + XCTAssertTrue([varCache hasVarsRequestCompleted]); - NSDictionary *updatedVarsFromServer = @{ - @"Title": @"TitleUpdated", - }; - id varCacheMock = OCMPartialMock(variables.varCache); - [varCacheMock applyVariableDiffs:updatedVarsFromServer]; - OCMVerify([varCacheMock saveDiffs]); - XCTAssertTrue([varCacheMock hasReceivedDiffs]); + // Call original saveDiffs and write to file + [varCache originalSaveDiffs]; - NSString *fileName = [varCacheMock getArchiveFileName]; + NSString *fileName = [varCache getArchiveFileName]; NSString *filePath = [CTPreferences filePathfromFileName:fileName]; NSData *diffsData = [NSData dataWithContentsOfFile:filePath]; NSKeyedUnarchiver *unarchiver; @@ -354,64 +413,40 @@ - (void)testVarCacheSavesDiffs { #pragma clang diagnostic pop } NSDictionary *loadedVars = (NSDictionary *) [unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARIABLES_KEY]; - XCTAssertTrue([updatedVarsFromServer isEqualToDictionary:loadedVars]); + XCTAssertTrue([diff isEqualToDictionary:loadedVars]); - [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; + [self deleteSavedFile:fileName]; } -- (void)testVarCacheLoadsDiffs { - NSString *varName = @"Title"; - NSString *initialVarValue = @"Hello"; - NSString *updatedVarValue = @"TitleUpdated"; - CTVariables *variablesMock = OCMPartialMock(variables); - CTVarCache *varCacheMock = OCMPartialMock(variables.varCache); - [variablesMock define:varName with:initialVarValue kind:CT_KIND_STRING]; - - NSDictionary *updatedVarsFromServer = @{ - varName: updatedVarValue, +- (void)testLoadsDiffs { + NSString *varName = @"var1"; + NSString *initialValue = @"value"; + NSString *overriddenValue = @"overridden"; + CTVarCacheMock *varCache = (CTVarCacheMock *)variables.varCache; + + [variables define:varName with:initialValue kind:CT_KIND_STRING]; + + NSDictionary *diff = @{ + varName: overriddenValue, }; - NSString *varsJson = [CTUtils dictionaryToJsonString:updatedVarsFromServer]; - [varCacheMock applyVariableDiffs:updatedVarsFromServer]; - OCMVerify([varCacheMock saveDiffs]); - XCTAssertTrue([varCacheMock hasReceivedDiffs]); + [variables handleVariablesResponse:diff]; + XCTAssertEqual(1, varCache.saveCount); + XCTAssertTrue([varCache hasVarsRequestCompleted]); + + // Call original saveDiffs and write to file + [varCache originalSaveDiffs]; - [varCacheMock setSilent:YES]; - [varCacheMock loadDiffs]; - CTVar *loadedVar = varCacheMock.vars[varName]; - XCTAssertEqualObjects(loadedVar.value, updatedVarValue); + [varCache loadDiffs]; + CTVar *loadedVar = varCache.vars[varName]; + XCTAssertEqualObjects(loadedVar.value, overriddenValue); - NSString *fileName = [varCacheMock getArchiveFileName]; + [self deleteSavedFile:[varCache getArchiveFileName]]; +} + +- (void)deleteSavedFile:(NSString *)fileName { NSString *filePath = [CTPreferences filePathfromFileName:fileName]; NSError *error = nil; [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; } -- (void)testVarValues { - NSNumber *varValue = [NSNumber numberWithDouble:6.67345983745897]; - CTVar *var = [variables define:@"MyNumber" with:varValue kind:CT_KIND_FLOAT]; - - XCTAssertEqualObjects(var.stringValue,varValue.stringValue); - XCTAssertEqual(var.floatValue,varValue.floatValue); - XCTAssertEqual(var.intValue,varValue.intValue); - XCTAssertEqual(var.integerValue,varValue.integerValue); - XCTAssertEqual(var.doubleValue,varValue.doubleValue); - XCTAssertEqual(var.boolValue,varValue.boolValue); - XCTAssertEqual(var.longValue,varValue.longValue); - XCTAssertEqual(var.longLongValue,varValue.longLongValue); - XCTAssertEqual(var.unsignedIntValue,varValue.unsignedIntValue); - XCTAssertEqual(var.unsignedLongValue,varValue.unsignedLongValue); - XCTAssertEqual(var.unsignedIntegerValue,varValue.unsignedIntegerValue); - XCTAssertEqual(var.shortValue,varValue.shortValue); - XCTAssertEqual(var.unsignedShortValue,varValue.unsignedShortValue); - XCTAssertEqual(var.unsignedLongLongValue,varValue.unsignedLongLongValue); - XCTAssertEqual(var.cgFloatValue,varValue.doubleValue); - XCTAssertEqual(var.charValue,varValue.charValue); - XCTAssertEqual(var.unsignedCharValue,varValue.unsignedCharValue); - - CTVar *mapVar = [variables define:@"MyMap" with:@{@"MyMapNumber":varValue} kind:CT_KIND_DICTIONARY]; - XCTAssertTrue([[mapVar objectForKey:@"MyMapNumber"]isKindOfClass:[varValue class]]); - XCTAssertTrue([mapVar.value isKindOfClass:[NSDictionary class]]); - XCTAssertTrue([mapVar.defaultValue isKindOfClass:[NSDictionary class]]); -} - @end diff --git a/CleverTapSDKTests/CTVarTest.m b/CleverTapSDKTests/CTVarTest.m new file mode 100644 index 00000000..c02777df --- /dev/null +++ b/CleverTapSDKTests/CTVarTest.m @@ -0,0 +1,228 @@ +// +// CTVarTest.m +// CleverTapSDKTests +// +// Created by Nikola Zagorchev on 10.04.23. +// Copyright © 2023 CleverTap. All rights reserved. +// + +#import +#import +#import "CTVariables.h" +#import "CTVarCache.h" +#import "CTVarCacheMock.h" +#import "CTConstants.h" +#import "CTVariables+Tests.h" + +@interface CTVarDelegateImpl : NSObject + +typedef void(^Callback)(CTVar *); +@property Callback callback; + +@end + +@implementation CTVarDelegateImpl + +- (void)valueDidChange:(CTVar *)variable { + if ([self callback]) { + self.callback(variable); + } +} + +@end + +@interface CTVarTest : XCTestCase + +@property(strong, nonatomic) CTVariables *variables; + +@end + +@implementation CTVarTest + +- (void)setUp { + CleverTapInstanceConfig *config = [[CleverTapInstanceConfig alloc] initWithAccountId:@"id" accountToken:@"token" accountRegion:@"eu"]; + config.useCustomCleverTapId = YES; + CTDeviceInfo *deviceInfo = [[CTDeviceInfo alloc] initWithConfig:config andCleverTapID:@"test"]; + CTVarCacheMock *varCache = [[CTVarCacheMock alloc] initWithConfig:config deviceInfo:deviceInfo]; + self.variables = [[CTVariables alloc] initWithConfig:config deviceInfo:deviceInfo varCache:varCache]; +} + +- (void)tearDown { + self.variables = nil; +} + +- (void)testVariableName { + CTVar *var1 = [self.variables define:@"&" with:@"&" kind:CT_KIND_STRING]; + CTVar *var2 = [self.variables define:@"[var]" with:@"[var]" kind:CT_KIND_STRING]; + CTVar *var3 = [self.variables define:@" " with:@" " kind:CT_KIND_STRING]; + + CTVar *var4 = [self.variables define:@".group.var." with:@"value1" kind:CT_KIND_STRING]; + CTVar *var5 = [self.variables define:@"" with:@"" kind:CT_KIND_STRING]; + + XCTAssertEqualObjects(@"&", var1.name); + XCTAssertEqualObjects(@"[var]", var2.name); + XCTAssertEqualObjects(@" ", var3.name); + + XCTAssertNil(var4); + XCTAssertNil(var5); +} + +- (void)testCTVarDelegate { + CTVar *var1 = [self.variables define:@"var1" with:@1 kind:CT_KIND_INT]; + + XCTestExpectation *expect = [self expectationWithDescription:@"delegate"]; + CTVarDelegateImpl *del = [[CTVarDelegateImpl alloc] init]; + __block CTVar *varFromDelegate = nil; + [del setCallback:^(CTVar * variable) { + varFromDelegate = variable; + [expect fulfill]; + }]; + [var1 setDelegate:del]; + + NSDictionary *diffs = @{ + @"var1": @2, + }; + [self.variables handleVariablesResponse:diffs]; + + [self waitForExpectations:@[expect] timeout:DISPATCH_TIME_NOW + 5.0]; + XCTAssertEqual(@"var1", varFromDelegate.name); + XCTAssertEqual(@2, varFromDelegate.value); +} + +- (void)testCTVarDelegateOnError { + CTVar *var1 = [self.variables define:@"var1" with:@1 kind:CT_KIND_INT]; + + XCTestExpectation *expect = [self expectationWithDescription:@"delegate"]; + CTVarDelegateImpl *del = [[CTVarDelegateImpl alloc] init]; + __block CTVar *varFromDelegate = nil; + [del setCallback:^(CTVar * variable) { + varFromDelegate = variable; + [expect fulfill]; + }]; + [var1 setDelegate:del]; + + [self.variables handleVariablesError]; + + [self waitForExpectations:@[expect] timeout:DISPATCH_TIME_NOW + 5.0]; + XCTAssertEqual(@"var1", varFromDelegate.name); + XCTAssertEqual(@1, varFromDelegate.value); +} + +- (void)testCTVarDelegateAfterStart { + CTVar *var1 = [self.variables define:@"var1" with:@1 kind:CT_KIND_INT]; + + XCTestExpectation *expect = [self expectationWithDescription:@"delegate"]; + CTVarDelegateImpl *del = [[CTVarDelegateImpl alloc] init]; + __block CTVar *varFromDelegate = nil; + [del setCallback:^(CTVar * variable) { + varFromDelegate = variable; + [expect fulfill]; + }]; + + NSDictionary *diffs = @{ + @"var1": @2, + }; + [self.variables handleVariablesResponse:diffs]; + + // Set delegate after handling response + [var1 setDelegate:del]; + + [self waitForExpectations:@[expect] timeout:DISPATCH_TIME_NOW + 5.0]; + XCTAssertEqual(@"var1", varFromDelegate.name); + XCTAssertEqual(@2, varFromDelegate.value); +} + +- (void)testOnValueChangedNoDiff { + CTVar *var1 = [self.variables define:@"var1" with:@1 kind:CT_KIND_INT]; + + XCTestExpectation *expect = [self expectationWithDescription:@"delegate"]; + [var1 onValueChanged:^{ + [expect fulfill]; + }]; + + [self.variables handleVariablesResponse:@{}]; + + [self waitForExpectations:@[expect] timeout:DISPATCH_TIME_NOW + 5.0]; + XCTAssertEqualObjects(@1, var1.value); +} + +- (void)testOnValueChangedDiff { + CTVar *var1 = [self.variables define:@"var1" with:@1 kind:CT_KIND_INT]; + + XCTestExpectation *expect = [self expectationWithDescription:@"delegate"]; + [var1 onValueChanged:^{ + [expect fulfill]; + }]; + + NSDictionary *diffs = @{ + @"var1": @2, + }; + [self.variables handleVariablesResponse:diffs]; + + [self waitForExpectations:@[expect] timeout:DISPATCH_TIME_NOW + 5.0]; + XCTAssertEqual(@2, var1.value); +} + +- (void)testOnValueChangedOnError { + CTVar *var1 = [self.variables define:@"var1" with:@1 kind:CT_KIND_INT]; + + XCTestExpectation *expect = [self expectationWithDescription:@"delegate"]; + [var1 onValueChanged:^{ + [expect fulfill]; + }]; + [self.variables handleVariablesError]; + + [self waitForExpectations:@[expect] timeout:DISPATCH_TIME_NOW + 5.0]; + XCTAssertEqual(@1, var1.value); +} + +- (void)testHadStarted { + CTVar *var1 = [self.variables define:@"var1" with:@1 kind:CT_KIND_INT]; + XCTAssertFalse(var1.hadStarted); + self.variables.varCache.hasVarsRequestCompleted = @YES; + [self.variables.varCache applyVariableDiffs:@{}]; + XCTAssertTrue(var1.hadStarted); +} + +- (void)testHasChanged { + CTVar *var1 = [self.variables define:@"var1" with:@1 kind:CT_KIND_INT]; + self.variables.varCache.hasVarsRequestCompleted = @YES; + [self.variables.varCache applyVariableDiffs:@{}]; + XCTAssertFalse(var1.hasChanged); + + NSDictionary *diffs = @{ + @"var1": @2, + }; + [self.variables.varCache applyVariableDiffs:diffs]; + XCTAssertTrue(var1.hasChanged); +} + +- (void)testVarValues { + NSNumber *varValue = [NSNumber numberWithDouble:6.67345983745897]; + CTVar *var = [self.variables define:@"varNumber" with:varValue kind:CT_KIND_FLOAT]; + + XCTAssertEqualObjects(var.stringValue,varValue.stringValue); + XCTAssertEqual(var.floatValue,varValue.floatValue); + XCTAssertEqual(var.intValue,varValue.intValue); + XCTAssertEqual(var.integerValue,varValue.integerValue); + XCTAssertEqual(var.doubleValue,varValue.doubleValue); + XCTAssertEqual(var.boolValue,varValue.boolValue); + XCTAssertEqual(var.longValue,varValue.longValue); + XCTAssertEqual(var.longLongValue,varValue.longLongValue); + XCTAssertEqual(var.unsignedIntValue,varValue.unsignedIntValue); + XCTAssertEqual(var.unsignedLongValue,varValue.unsignedLongValue); + XCTAssertEqual(var.unsignedIntegerValue,varValue.unsignedIntegerValue); + XCTAssertEqual(var.shortValue,varValue.shortValue); + XCTAssertEqual(var.unsignedShortValue,varValue.unsignedShortValue); + XCTAssertEqual(var.unsignedLongLongValue,varValue.unsignedLongLongValue); + XCTAssertEqual(var.cgFloatValue,varValue.doubleValue); + XCTAssertEqual(var.charValue,varValue.charValue); + XCTAssertEqual(var.unsignedCharValue,varValue.unsignedCharValue); + + CTVar *groupVar = [self.variables define:@"group" with:@{@"number":varValue} kind:CT_KIND_DICTIONARY]; + XCTAssertTrue([[groupVar objectForKey:@"number"] isKindOfClass:[varValue class]]); + XCTAssertTrue([groupVar.value isKindOfClass:[NSDictionary class]]); + XCTAssertTrue([groupVar.defaultValue isKindOfClass:[NSDictionary class]]); +} + +@end diff --git a/CleverTapSDKTests/CTVariables+Tests.h b/CleverTapSDKTests/CTVariables+Tests.h index 662e3505..b06dbb2c 100644 --- a/CleverTapSDKTests/CTVariables+Tests.h +++ b/CleverTapSDKTests/CTVariables+Tests.h @@ -12,5 +12,5 @@ @interface CTVariables (Tests) - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo *)deviceInfo varCache: (CTVarCacheMock *)varCache; - +- (void)triggerVariablesChanged; @end diff --git a/CleverTapSDKTests/CTVariablesTest.m b/CleverTapSDKTests/CTVariablesTest.m index 29add7e2..1ae58173 100644 --- a/CleverTapSDKTests/CTVariablesTest.m +++ b/CleverTapSDKTests/CTVariablesTest.m @@ -477,5 +477,103 @@ - (void)testFlattenWithInvalidInputArray { XCTAssertEqualObjects(actualOutput, expectedOutput); } +#pragma mark Handle Response +- (void)testHandleVariablesResponse { + XCTAssertFalse([self.variables.varCache hasVarsRequestCompleted]); + [self.variables handleVariablesResponse:@{}]; + XCTAssertTrue([self.variables.varCache hasVarsRequestCompleted]); + XCTAssertTrue([(CTVarCacheMock *)self.variables.varCache loadCount] == 0); + XCTAssertTrue([(CTVarCacheMock *)self.variables.varCache applyCount] == 1); + XCTAssertTrue([(CTVarCacheMock *)self.variables.varCache saveCount] == 1); + + [self.variables handleVariablesResponse:@{}]; + XCTAssertTrue([(CTVarCacheMock *)self.variables.varCache loadCount] == 0); + XCTAssertTrue([(CTVarCacheMock *)self.variables.varCache applyCount] == 2); + XCTAssertTrue([(CTVarCacheMock *)self.variables.varCache saveCount] == 2); +} + +- (void)testHandleVariablesError { + XCTAssertFalse([self.variables.varCache hasVarsRequestCompleted]); + [self.variables handleVariablesError]; + XCTAssertTrue([self.variables.varCache hasVarsRequestCompleted]); + XCTAssertTrue([(CTVarCacheMock *)self.variables.varCache loadCount] == 1); + XCTAssertTrue([(CTVarCacheMock *)self.variables.varCache applyCount] == 1); +} + +- (void)testClearContent { + CTVar *var = [self.variables define:@"var1" with:@"value" kind:CT_KIND_STRING]; + XCTAssertFalse([self.variables.varCache hasVarsRequestCompleted]); + [self.variables handleVariablesResponse:@{ @"var1": @"new value"}]; + XCTAssertTrue([self.variables.varCache hasVarsRequestCompleted]); + XCTAssertTrue([var hadStarted]); + XCTAssertTrue([var hasChanged]); + + [self.variables clearUserContent]; + + XCTAssertFalse([var hadStarted]); + XCTAssertFalse([var hasChanged]); +} + +#pragma mark Callbacks +- (void)testOnVariablesChanged { + XCTestExpectation *expect = [self expectationWithDescription:@"delegate"]; + XCTestExpectation *expect2 = [self expectationWithDescription:@"delegate2"]; + + [self.variables onVariablesChanged:^{ + [expect fulfill]; + }]; + [self.variables onVariablesChanged:^{ + [expect2 fulfill]; + }]; + [self.variables handleVariablesResponse:@{}]; + + [self waitForExpectations:@[expect, expect2] timeout:DISPATCH_TIME_NOW + 5.0]; +} + +- (void)testOnceVariablesChanged { + XCTestExpectation *expect = [self expectationWithDescription:@"delegate"]; + [self.variables onceVariablesChanged:^{ + // Should be called once + [expect fulfill]; + }]; + // Call twice to trigger callbacks two times + [self.variables handleVariablesResponse:@{}]; + [self.variables handleVariablesResponse:@{}]; + + [self waitForExpectations:@[expect] timeout:DISPATCH_TIME_NOW + 5.0]; +} + +- (void)testOnVariablesChangedOnError { + XCTestExpectation *expect = [self expectationWithDescription:@"delegate"]; + [self.variables onVariablesChanged:^{ + [expect fulfill]; + }]; + [self.variables handleVariablesError]; + + [self waitForExpectations:@[expect] timeout:DISPATCH_TIME_NOW + 5.0]; +} + +- (void)testFetchVariables { + XCTestExpectation *expect = [self expectationWithDescription:@"delegate"]; + [self.variables setFetchVariablesBlock:^(BOOL success) { + XCTAssertTrue(success); + [expect fulfill]; + }]; + [self.variables handleVariablesResponse:@{}]; + + [self waitForExpectations:@[expect] timeout:DISPATCH_TIME_NOW + 5.0]; +} + +- (void)testFetchVariablesOnError { + XCTestExpectation *expect = [self expectationWithDescription:@"delegate"]; + [self.variables setFetchVariablesBlock:^(BOOL success) { + XCTAssertFalse(success); + [expect fulfill]; + }]; + [self.variables handleVariablesError]; + + [self waitForExpectations:@[expect] timeout:DISPATCH_TIME_NOW + 5.0]; +} + @end From c71e0720b0598b6bd817215c4318322bb6880010 Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Tue, 11 Apr 2023 20:18:27 +0300 Subject: [PATCH 69/86] Remove hardcoded endpoint --- CleverTapSDK/CTConstants.h | 1 + CleverTapSDK/CTConstants.m | 2 ++ CleverTapSDK/CleverTap.m | 9 +++------ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CleverTapSDK/CTConstants.h b/CleverTapSDK/CTConstants.h index 926ff9ba..7ae9d40e 100644 --- a/CleverTapSDK/CTConstants.h +++ b/CleverTapSDK/CTConstants.h @@ -11,6 +11,7 @@ extern NSString *CT_KIND_DICTIONARY; extern NSString *CLEVERTAP_DEFAULTS_VARIABLES_KEY; extern NSString *CLEVERTAP_DEFAULTS_VARS_JSON_KEY; +extern NSString *CT_PE_DEFINE_VARS_ENDPOINT; extern NSString *CT_PE_VARS_PAYLOAD_TYPE; extern NSString *CT_PE_VARS_PAYLOAD_KEY; extern NSString *CT_PE_VAR_TYPE; diff --git a/CleverTapSDK/CTConstants.m b/CleverTapSDK/CTConstants.m index bbe0f281..afae8fd7 100644 --- a/CleverTapSDK/CTConstants.m +++ b/CleverTapSDK/CTConstants.m @@ -12,6 +12,8 @@ NSString *CLEVERTAP_DEFAULTS_VARIABLES_KEY = @"__clevertap_variables"; NSString *CLEVERTAP_DEFAULTS_VARS_JSON_KEY = @"__clevertap_variables_json"; +NSString *CT_PE_DEFINE_VARS_ENDPOINT = @"defineVars"; + NSString *CT_PE_VARS_PAYLOAD_TYPE = @"varsPayload"; NSString *CT_PE_VARS_PAYLOAD_KEY = @"vars"; NSString *CT_PE_VAR_TYPE = @"type"; diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 6d968df8..b10ecbc3 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -692,7 +692,7 @@ - (instancetype)initWithConfig:(CleverTapInstanceConfig*)config andCleverTapID:( [self _initProductConfig]; - + // Initialise Variables self.variables = [[CTVariables alloc] initWithConfig:self.config deviceInfo:self.deviceInfo]; [self notifyUserProfileInitialized]; @@ -5076,17 +5076,14 @@ - (void)syncVariables:(BOOL)isProduction { } - (void)_syncVars { - // META NSDictionary *meta = [self batchHeader]; - // VARSPAYLOAD NSDictionary *varsPayload = [[self variables] varsPayload]; NSArray *payload = @[meta,varsPayload]; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - // TODO: REMOVE STATIC ENDPOINT -// CTRequest *ctRequest = [CTRequestFactory syncVarsRequestWithConfig:self.config params:payload url:[NSString stringWithFormat:@"https://%@/defineVars",self.domainFactory.redirectDomain]]; - CTRequest *ctRequest = [CTRequestFactory syncVarsRequestWithConfig:self.config params:payload url:@"https://sk1-staging-25.clevertap-prod.com/defineVars"]; + NSString *url = [NSString stringWithFormat:@"https://%@/%@",self.domainFactory.redirectDomain, CT_PE_DEFINE_VARS_ENDPOINT]; + CTRequest *ctRequest = [CTRequestFactory syncVarsRequestWithConfig:self.config params:payload url:url]; [ctRequest onResponse:^(NSData * _Nullable data, NSURLResponse * _Nullable response) { if ([response isKindOfClass:[NSHTTPURLResponse class]]) { From 81c0937dec70ccaad14be909a91eff768a1b6f6b Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 12 Apr 2023 00:39:34 +0300 Subject: [PATCH 70/86] Linting --- CleverTapSDK/CleverTap.m | 54 +++++++------------ CleverTapSDK/ProductExperiences/CTVar.m | 45 ++++++---------- CleverTapSDK/ProductExperiences/CTVarCache.m | 34 +++++------- CleverTapSDK/ProductExperiences/CTVariables.h | 4 +- CleverTapSDK/ProductExperiences/CTVariables.m | 22 ++++---- .../ProductExperiences/ContentMerger.m | 2 +- 6 files changed, 57 insertions(+), 104 deletions(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index b10ecbc3..9aa4b276 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -5134,81 +5134,69 @@ - (CTVar *)defineVar:(NSString *)name { return [self.variables define:name with:nil kind:nil]; } -- (CTVar *)defineVar:(NSString *)name withInt:(int)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withInt:(int)defaultValue { return [self.variables define:name with:[NSNumber numberWithInt:defaultValue] kind:CT_KIND_INT]; } -- (CTVar *)defineVar:(NSString *)name withFloat:(float)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withFloat:(float)defaultValue { return [self.variables define:name with:[NSNumber numberWithFloat:defaultValue] kind:CT_KIND_FLOAT]; } -- (CTVar *)defineVar:(NSString *)name withDouble:(double)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withDouble:(double)defaultValue { return [self.variables define:name with:[NSNumber numberWithDouble:defaultValue] kind:CT_KIND_FLOAT]; } -- (CTVar *)defineVar:(NSString *)name withCGFloat:(CGFloat)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withCGFloat:(CGFloat)defaultValue { return [self.variables define:name with:[NSNumber numberWithDouble:defaultValue] kind:CT_KIND_FLOAT]; } -- (CTVar *)defineVar:(NSString *)name withShort:(short)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withShort:(short)defaultValue { return [self.variables define:name with:[NSNumber numberWithShort:defaultValue] kind:CT_KIND_INT]; } -- (CTVar *)defineVar:(NSString *)name withChar:(char)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withChar:(char)defaultValue { return [self.variables define:name with:[NSNumber numberWithChar:defaultValue] kind:CT_KIND_INT]; } -- (CTVar *)defineVar:(NSString *)name withBool:(BOOL)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withBool:(BOOL)defaultValue { return [self.variables define:name with:[NSNumber numberWithBool:defaultValue] kind:CT_KIND_BOOLEAN]; } -- (CTVar *)defineVar:(NSString *)name withInteger:(NSInteger)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withInteger:(NSInteger)defaultValue { return [self.variables define:name with:[NSNumber numberWithInteger:defaultValue] kind:CT_KIND_INT]; } -- (CTVar *)defineVar:(NSString *)name withLong:(long)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withLong:(long)defaultValue { return [self.variables define:name with:[NSNumber numberWithLong:defaultValue] kind:CT_KIND_INT]; } -- (CTVar *)defineVar:(NSString *)name withLongLong:(long long)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withLongLong:(long long)defaultValue { return [self.variables define:name with:[NSNumber numberWithLongLong:defaultValue] kind:CT_KIND_INT]; } -- (CTVar *)defineVar:(NSString *)name withUnsignedChar:(unsigned char)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withUnsignedChar:(unsigned char)defaultValue { return [self.variables define:name with:[NSNumber numberWithUnsignedChar:defaultValue] kind:CT_KIND_INT]; } -- (CTVar *)defineVar:(NSString *)name withUnsignedInt:(unsigned int)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withUnsignedInt:(unsigned int)defaultValue { return [self.variables define:name with:[NSNumber numberWithUnsignedInt:defaultValue] kind:CT_KIND_INT]; @@ -5221,39 +5209,33 @@ - (CTVar *)defineVar:(NSString *)name withUnsignedInteger:(NSUInteger)defaultVal kind:CT_KIND_INT]; } -- (CTVar *)defineVar:(NSString *)name withUnsignedLong:(unsigned long)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withUnsignedLong:(unsigned long)defaultValue { return [self.variables define:name with:[NSNumber numberWithUnsignedLong:defaultValue] kind:CT_KIND_INT]; } -- (CTVar *)defineVar:(NSString *)name withUnsignedLongLong:(unsigned long long)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withUnsignedLongLong:(unsigned long long)defaultValue { return [self.variables define:name with:[NSNumber numberWithUnsignedLongLong:defaultValue] kind:CT_KIND_INT]; } -- (CTVar *)defineVar:(NSString *)name withUnsignedShort:(unsigned short)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withUnsignedShort:(unsigned short)defaultValue { return [self.variables define:name with:[NSNumber numberWithUnsignedShort:defaultValue] kind:CT_KIND_INT]; } -- (CTVar *)defineVar:(NSString *)name withString:(NSString *)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withString:(NSString *)defaultValue { return [self.variables define:name with:defaultValue kind:CT_KIND_STRING]; } -- (CTVar *)defineVar:(NSString *)name withNumber:(NSNumber *)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withNumber:(NSNumber *)defaultValue { return [self.variables define:name with:defaultValue kind:CT_KIND_FLOAT]; } -- (CTVar *)defineVar:(NSString *)name withDictionary:(NSDictionary *)defaultValue -{ +- (CTVar *)defineVar:(NSString *)name withDictionary:(NSDictionary *)defaultValue { return [self.variables define:name with:defaultValue kind:CT_KIND_DICTIONARY]; } diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index a979194c..b9bd9d09 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -59,8 +59,7 @@ - (void)setVarCache:(CTVarCache *)varCache { #pragma mark Updates -- (void) cacheComputedValues -{ +- (void) cacheComputedValues { // Cache computed values. if ([_value isKindOfClass:NSString.class]) { _stringValue = (NSString *) _value; @@ -74,8 +73,7 @@ - (void) cacheComputedValues } } -- (void)update -{ +- (void)update { NSObject *oldValue = _value; _value = [self.varCache getMergedValueFromComponentArray:_nameComponents]; @@ -96,8 +94,7 @@ - (void)update #pragma mark Callbacks -- (void)triggerValueChanged -{ +- (void)triggerValueChanged { if (self.delegate && [self.delegate respondsToSelector:@selector(valueDidChange:)]) { [self.delegate valueDidChange:self]; @@ -108,8 +105,7 @@ - (void)triggerValueChanged } } -- (void)onValueChanged:(CleverTapVariablesChangedBlock)block -{ +- (void)onValueChanged:(CleverTapVariablesChangedBlock)block { if (!block) { CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CTVar onValueChanged]."); return; @@ -126,8 +122,7 @@ - (void)onValueChanged:(CleverTapVariablesChangedBlock)block CT_END_TRY } -- (void)setDelegate:(id)delegate -{ +- (void)setDelegate:(id)delegate { CT_TRY _delegate = delegate; if ([[self varCache] hasVarsRequestCompleted]) { @@ -138,18 +133,15 @@ - (void)setDelegate:(id)delegate #pragma mark Dictionary handling -- (id) objectForKey:(NSString *)key -{ +- (id) objectForKey:(NSString *)key { return [self objectForKeyPath:key, nil]; } -- (id) objectAtIndex:(NSUInteger)index -{ +- (id) objectAtIndex:(NSUInteger)index { return [self objectForKeyPath:@(index), nil]; } -- (id) objectForKeyPath:(id)firstComponent, ... -{ +- (id) objectForKeyPath:(id)firstComponent, ... { CT_TRY [self warnIfNotStarted]; NSMutableArray *components = [_nameComponents mutableCopy]; @@ -165,8 +157,7 @@ - (id) objectForKeyPath:(id)firstComponent, ... return nil; } -- (id)objectForKeyPathComponents:(NSArray *)pathComponents -{ +- (id)objectForKeyPathComponents:(NSArray *)pathComponents { CT_TRY [self warnIfNotStarted]; NSMutableArray *components = [_nameComponents mutableCopy]; @@ -178,14 +169,12 @@ - (id)objectForKeyPathComponents:(NSArray *)pathComponents #pragma mark Value accessors -- (NSNumber *)numberValue -{ +- (NSNumber *)numberValue { [self warnIfNotStarted]; return _numberValue; } -- (NSString *)stringValue -{ +- (NSString *)stringValue { [self warnIfNotStarted]; return _stringValue; } @@ -209,18 +198,15 @@ - (unsigned long long)unsignedLongLongValue { return [[self numberValue] unsigne #pragma mark Utils -+ (BOOL)printedCallbackWarning -{ ++ (BOOL)printedCallbackWarning { return LPVAR_PRINTED_CALLBACK_WARNING; } -+ (void)setPrintedCallbackWarning:(BOOL)newPrintedCallbackWarning -{ ++ (void)setPrintedCallbackWarning:(BOOL)newPrintedCallbackWarning { LPVAR_PRINTED_CALLBACK_WARNING = newPrintedCallbackWarning; } -- (void)warnIfNotStarted -{ +- (void)warnIfNotStarted { if (!self.varCache.hasVarsRequestCompleted && ![CTVar printedCallbackWarning]) { CleverTapLogDebug(self.varCache.config.logLevel, @"%@: CleverTap hasn't finished retrieving values from the server. You " @"should use a callback to make sure the value for '%@' is ready. Otherwise, your " @@ -230,8 +216,7 @@ - (void)warnIfNotStarted } } -- (void)clearState -{ +- (void)clearState { _hadStarted = NO; _hasChanged = NO; } diff --git a/CleverTapSDK/ProductExperiences/CTVarCache.m b/CleverTapSDK/ProductExperiences/CTVarCache.m index 44225a23..959ccc9f 100644 --- a/CleverTapSDK/ProductExperiences/CTVarCache.m +++ b/CleverTapSDK/ProductExperiences/CTVarCache.m @@ -25,22 +25,19 @@ - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CT return self; } -- (void)initialize -{ +- (void)initialize { self.vars = [NSMutableDictionary dictionary]; self.diffs = [NSMutableDictionary dictionary]; self.valuesFromClient = [NSMutableDictionary dictionary]; self.hasVarsRequestCompleted = NO; } -- (NSArray *)getNameComponents:(NSString *)name -{ +- (NSArray *)getNameComponents:(NSString *)name { NSArray *nameComponents = [name componentsSeparatedByString:@"."]; return nameComponents; } -- (id)traverse:(id)collection withKey:(id)key autoInsert:(BOOL)autoInsert -{ +- (id)traverse:(id)collection withKey:(id)key autoInsert:(BOOL)autoInsert { id result = nil; if ([collection respondsToSelector:@selector(objectForKey:)]) { result = [collection objectForKey:key]; @@ -124,8 +121,7 @@ - (void)mergeVariable:(CTVar * _Nonnull)var { } } -- (void)registerVariable:(CTVar *)var -{ +- (void)registerVariable:(CTVar *)var { [self.vars setObject:var forKey:var.name]; [self updateValues:var.name @@ -136,13 +132,11 @@ - (void)registerVariable:(CTVar *)var [self mergeVariable:var]; } -- (CTVar *)getVariable:(NSString *)name -{ +- (CTVar *)getVariable:(NSString *)name { return [self.vars objectForKey:name]; } -- (id)getMergedValue:(NSString *)name -{ +- (id)getMergedValue:(NSString *)name { NSArray *components = [self getNameComponents:name]; id value = [self getMergedValueFromComponentArray:components]; if ([value conformsToProtocol:@protocol(NSCopying)] && [value respondsToSelector:@selector(copyWithZone:)]) { @@ -155,8 +149,7 @@ - (id)getMergedValue:(NSString *)name return value; } -- (id)getValueFromComponentArray:(NSArray *) components fromDict:(NSDictionary *)values -{ +- (id)getValueFromComponentArray:(NSArray *) components fromDict:(NSDictionary *)values { id mergedPtr = values; for (id component in components) { mergedPtr = [self traverse:mergedPtr withKey:component autoInsert:NO]; @@ -164,13 +157,11 @@ - (id)getValueFromComponentArray:(NSArray *) components fromDict:(NSDictionary * return mergedPtr; } -- (id)getMergedValueFromComponentArray:(NSArray *)components -{ +- (id)getMergedValueFromComponentArray:(NSArray *)components { return [self getValueFromComponentArray:components fromDict:self.merged ? self.merged : self.valuesFromClient]; } -- (void)loadDiffs -{ +- (void)loadDiffs { @try { NSString *fileName = [self dataArchiveFileName]; NSString *filePath = [CTPreferences filePathfromFileName:fileName]; @@ -202,8 +193,7 @@ - (void)loadDiffs } } -- (void)saveDiffs -{ +- (void)saveDiffs { // Stores the variables on the device in case we don't have a connection. // Restores next time when the app is opened. // Diffs need to be locked incase other thread changes the diffs @@ -230,8 +220,8 @@ - (NSString*)dataArchiveFileName { return [NSString stringWithFormat:@"clevertap-%@-%@-pe-vars.plist", _config.accountId, _deviceInfo.deviceId]; } -- (void)applyVariableDiffs:(NSDictionary *)diffs_ -{ +- (void)applyVariableDiffs:(NSDictionary *)diffs_ { + CleverTapLogDebug(self.config.logLevel, @"%@: Applying Variables: %@", self, diffs_); @synchronized (self.vars) { // Prevent overriding variables if API returns null // If no variables are defined, API returns {} diff --git a/CleverTapSDK/ProductExperiences/CTVariables.h b/CleverTapSDK/ProductExperiences/CTVariables.h index 97229cb0..251d23fa 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.h +++ b/CleverTapSDK/ProductExperiences/CTVariables.h @@ -28,8 +28,8 @@ NS_SWIFT_NAME(define(name:value:kind:)); - (void)handleVariablesResponse:(NSDictionary *)varsResponse; - (void)handleVariablesError; - (void)triggerFetchVariables:(BOOL)success; -- (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; -- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; +- (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull)block; +- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull)block; - (NSDictionary*)flatten:(NSDictionary*)map varName:(NSString*)varName; - (NSDictionary*)varsPayload; - (NSDictionary*)unflatten:(NSDictionary*)result; diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 3c2770f5..e19bc794 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -20,7 +20,7 @@ @interface CTVariables() @end @implementation CTVariables -// TODO: Linting: Brackets and spaces consistency + - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CTDeviceInfo*)deviceInfo { if ((self = [super init])) { self.varCache = [[CTVarCache alloc] initWithConfig:config deviceInfo:deviceInfo]; @@ -28,8 +28,7 @@ - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CT return self; } -- (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString *)kind -{ +- (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString *)kind { if ([CTUtils isNullOrEmpty:name]) { CleverTapLogDebug(_config.logLevel, @"%@: Empty name provided as parameter while defining a variable.", self); return nil; @@ -57,9 +56,9 @@ - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString } } -- (void)handleVariablesResponse:(NSDictionary *)varsResponse -{ +- (void)handleVariablesResponse:(NSDictionary *)varsResponse { if (varsResponse) { + CleverTapLogDebug(self.config.logLevel, @"%@: Handle Variables Response with: %@", self, varsResponse); [[self varCache] setHasVarsRequestCompleted:YES]; NSDictionary *values = [self unflatten:varsResponse]; [[self varCache] applyVariableDiffs:values]; @@ -68,8 +67,8 @@ - (void)handleVariablesResponse:(NSDictionary *)varsResponse } } -- (void)handleVariablesError -{ +- (void)handleVariablesError { + CleverTapLogDebug(self.config.logLevel, @"%@: Handle Variables Error", self); if (![[self varCache] hasVarsRequestCompleted]) { [[self varCache] setHasVarsRequestCompleted:YES]; // Ensure variables are loaded from cache. Triggers individual Vars update. @@ -103,8 +102,7 @@ - (void)triggerFetchVariables:(BOOL)success { } } -- (void)triggerVariablesChanged -{ +- (void)triggerVariablesChanged { if (![NSThread isMainThread]) { dispatch_async(dispatch_get_main_queue(), ^{ [self triggerVariablesChanged]; @@ -126,8 +124,7 @@ - (void)triggerVariablesChanged } } -- (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { - +- (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull)block { if (!block) { CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onVariablesChanged]."); return; @@ -145,8 +142,7 @@ - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { } } -- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { - +- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull)block { if (!block) { CleverTapLogStaticDebug(@"Nil block parameter provided while calling [CleverTap onceVariablesChanged]."); return; diff --git a/CleverTapSDK/ProductExperiences/ContentMerger.m b/CleverTapSDK/ProductExperiences/ContentMerger.m index be2eb049..9a391b5e 100644 --- a/CleverTapSDK/ProductExperiences/ContentMerger.m +++ b/CleverTapSDK/ProductExperiences/ContentMerger.m @@ -2,7 +2,7 @@ // ContentMerger.m // CleverTapSDK // -// Created by Akash Malhotra on 17/02/23. +// Created by Nikola Zagorchev on 12.03.23. // Copyright © 2023 CleverTap. All rights reserved. // From 79b713a9f841dfee7b85bcffd3884b01fd87da4a Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 12 Apr 2023 00:44:32 +0300 Subject: [PATCH 71/86] Move tests to ProductExperiences group --- CleverTapSDK.xcodeproj/project.pbxproj | 28 ++++++++++++------- CleverTapSDK/ProductExperiences/CTVariables.m | 4 +++ .../CTVarCache+Tests.h | 0 .../CTVarCache+Tests.m | 0 .../{ => ProductExperiences}/CTVarCacheMock.h | 0 .../{ => ProductExperiences}/CTVarCacheMock.m | 0 .../{ => ProductExperiences}/CTVarCacheTest.m | 0 .../{ => ProductExperiences}/CTVarTest.m | 0 .../CTVariables+Tests.h | 0 .../CTVariables+Tests.m | 0 .../CTVariablesTest.m | 0 .../ContentMergerTest.m | 0 12 files changed, 22 insertions(+), 10 deletions(-) rename CleverTapSDKTests/{ => ProductExperiences}/CTVarCache+Tests.h (100%) rename CleverTapSDKTests/{ => ProductExperiences}/CTVarCache+Tests.m (100%) rename CleverTapSDKTests/{ => ProductExperiences}/CTVarCacheMock.h (100%) rename CleverTapSDKTests/{ => ProductExperiences}/CTVarCacheMock.m (100%) rename CleverTapSDKTests/{ => ProductExperiences}/CTVarCacheTest.m (100%) rename CleverTapSDKTests/{ => ProductExperiences}/CTVarTest.m (100%) rename CleverTapSDKTests/{ => ProductExperiences}/CTVariables+Tests.h (100%) rename CleverTapSDKTests/{ => ProductExperiences}/CTVariables+Tests.m (100%) rename CleverTapSDKTests/{ => ProductExperiences}/CTVariablesTest.m (100%) rename CleverTapSDKTests/{ => ProductExperiences}/ContentMergerTest.m (100%) diff --git a/CleverTapSDK.xcodeproj/project.pbxproj b/CleverTapSDK.xcodeproj/project.pbxproj index 4b59ec28..374e2248 100644 --- a/CleverTapSDK.xcodeproj/project.pbxproj +++ b/CleverTapSDK.xcodeproj/project.pbxproj @@ -1066,9 +1066,27 @@ path = ProductExperiences; sourceTree = ""; }; + 6A7BB8DE29E60BE900651584 /* ProductExperiences */ = { + isa = PBXGroup; + children = ( + 4EF1CF9C29B076A300E3CB6A /* CTVarCache+Tests.h */, + 4EF1CF9D29B076A300E3CB6A /* CTVarCache+Tests.m */, + 4EED219A29AF6368006CEA19 /* CTVarCacheTest.m */, + 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */, + 6A2E0B9229D0A5CE00FCEA5F /* CTVariablesTest.m */, + 6A2E0B9429D49D0200FCEA5F /* CTVariables+Tests.m */, + 6A2E0B9629D49D5100FCEA5F /* CTVarCacheMock.h */, + 6A2E0B9729D49D5100FCEA5F /* CTVarCacheMock.m */, + 6A2E0B9929D49E4700FCEA5F /* CTVariables+Tests.h */, + 6A7BB8DB29E47CFF00651584 /* CTVarTest.m */, + ); + path = ProductExperiences; + sourceTree = ""; + }; D02AC2D9276044F70031C1BE /* CleverTapSDKTests */ = { isa = PBXGroup; children = ( + 6A7BB8DE29E60BE900651584 /* ProductExperiences */, 4EDCDE4D278AC4DF0065E699 /* Info.plist */, 4E1F1560277090D6009387AE /* Stub Responses */, D02AC2EC2767F4D10031C1BE /* BaseTestCase.h */, @@ -1077,21 +1095,11 @@ 4E1F155A276B662C009387AE /* EventDetail.m */, D02AC2E6276402160031C1BE /* CleverTap+Tests.h */, D02AC2E8276402CF0031C1BE /* CleverTap+Tests.m */, - 4EF1CF9C29B076A300E3CB6A /* CTVarCache+Tests.h */, - 4EF1CF9D29B076A300E3CB6A /* CTVarCache+Tests.m */, D02AC2DA276044F70031C1BE /* CleverTapSDKTests.m */, 4E1F154E27691CA0009387AE /* CleverTapInstanceTests.m */, 4E1F155027692042009387AE /* EventTests.m */, 4EC2D084278AAD8000F4DE54 /* IdentityManagementTests.m */, 6A2E4C17291E8A4A00385536 /* CleverTapInstanceConfigTests.m */, - 4EED219A29AF6368006CEA19 /* CTVarCacheTest.m */, - 6A2E0B9029CCCC8500FCEA5F /* ContentMergerTest.m */, - 6A2E0B9229D0A5CE00FCEA5F /* CTVariablesTest.m */, - 6A2E0B9429D49D0200FCEA5F /* CTVariables+Tests.m */, - 6A2E0B9629D49D5100FCEA5F /* CTVarCacheMock.h */, - 6A2E0B9729D49D5100FCEA5F /* CTVarCacheMock.m */, - 6A2E0B9929D49E4700FCEA5F /* CTVariables+Tests.h */, - 6A7BB8DB29E47CFF00651584 /* CTVarTest.m */, ); path = CleverTapSDKTests; sourceTree = ""; diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index e19bc794..7a38ef9e 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -28,6 +28,7 @@ - (instancetype)initWithConfig:(CleverTapInstanceConfig *)config deviceInfo: (CT return self; } +#pragma mark Define Var - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString *)kind { if ([CTUtils isNullOrEmpty:name]) { CleverTapLogDebug(_config.logLevel, @"%@: Empty name provided as parameter while defining a variable.", self); @@ -56,6 +57,7 @@ - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString } } +#pragma mark Handle Response - (void)handleVariablesResponse:(NSDictionary *)varsResponse { if (varsResponse) { CleverTapLogDebug(self.config.logLevel, @"%@: Handle Variables Response with: %@", self, varsResponse); @@ -85,6 +87,7 @@ - (void)clearUserContent { [self.varCache clearUserContent]; } +#pragma mark Triggers - (void)triggerFetchVariables:(BOOL)success { if (self.fetchVariablesBlock) { CleverTapFetchVariablesBlock block = [self.fetchVariablesBlock copy]; @@ -163,6 +166,7 @@ - (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull)block { } } +#pragma mark Vars Payload - (NSDictionary*)varsPayload { NSMutableDictionary *payload = [NSMutableDictionary dictionary]; payload[@"type"] = CT_PE_VARS_PAYLOAD_TYPE; diff --git a/CleverTapSDKTests/CTVarCache+Tests.h b/CleverTapSDKTests/ProductExperiences/CTVarCache+Tests.h similarity index 100% rename from CleverTapSDKTests/CTVarCache+Tests.h rename to CleverTapSDKTests/ProductExperiences/CTVarCache+Tests.h diff --git a/CleverTapSDKTests/CTVarCache+Tests.m b/CleverTapSDKTests/ProductExperiences/CTVarCache+Tests.m similarity index 100% rename from CleverTapSDKTests/CTVarCache+Tests.m rename to CleverTapSDKTests/ProductExperiences/CTVarCache+Tests.m diff --git a/CleverTapSDKTests/CTVarCacheMock.h b/CleverTapSDKTests/ProductExperiences/CTVarCacheMock.h similarity index 100% rename from CleverTapSDKTests/CTVarCacheMock.h rename to CleverTapSDKTests/ProductExperiences/CTVarCacheMock.h diff --git a/CleverTapSDKTests/CTVarCacheMock.m b/CleverTapSDKTests/ProductExperiences/CTVarCacheMock.m similarity index 100% rename from CleverTapSDKTests/CTVarCacheMock.m rename to CleverTapSDKTests/ProductExperiences/CTVarCacheMock.m diff --git a/CleverTapSDKTests/CTVarCacheTest.m b/CleverTapSDKTests/ProductExperiences/CTVarCacheTest.m similarity index 100% rename from CleverTapSDKTests/CTVarCacheTest.m rename to CleverTapSDKTests/ProductExperiences/CTVarCacheTest.m diff --git a/CleverTapSDKTests/CTVarTest.m b/CleverTapSDKTests/ProductExperiences/CTVarTest.m similarity index 100% rename from CleverTapSDKTests/CTVarTest.m rename to CleverTapSDKTests/ProductExperiences/CTVarTest.m diff --git a/CleverTapSDKTests/CTVariables+Tests.h b/CleverTapSDKTests/ProductExperiences/CTVariables+Tests.h similarity index 100% rename from CleverTapSDKTests/CTVariables+Tests.h rename to CleverTapSDKTests/ProductExperiences/CTVariables+Tests.h diff --git a/CleverTapSDKTests/CTVariables+Tests.m b/CleverTapSDKTests/ProductExperiences/CTVariables+Tests.m similarity index 100% rename from CleverTapSDKTests/CTVariables+Tests.m rename to CleverTapSDKTests/ProductExperiences/CTVariables+Tests.m diff --git a/CleverTapSDKTests/CTVariablesTest.m b/CleverTapSDKTests/ProductExperiences/CTVariablesTest.m similarity index 100% rename from CleverTapSDKTests/CTVariablesTest.m rename to CleverTapSDKTests/ProductExperiences/CTVariablesTest.m diff --git a/CleverTapSDKTests/ContentMergerTest.m b/CleverTapSDKTests/ProductExperiences/ContentMergerTest.m similarity index 100% rename from CleverTapSDKTests/ContentMergerTest.m rename to CleverTapSDKTests/ProductExperiences/ContentMergerTest.m From 83b257b00c6e4886237e6e7d1de1f68d249365cc Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 12 Apr 2023 01:06:36 +0300 Subject: [PATCH 72/86] Remove commented out code --- CleverTapSDK/CTRequestSender.h | 1 - CleverTapSDK/CTRequestSender.m | 6 ------ CleverTapSDK/CleverTap+CTVar.h | 2 -- CleverTapSDK/CleverTap.m | 6 +----- 4 files changed, 1 insertion(+), 14 deletions(-) diff --git a/CleverTapSDK/CTRequestSender.h b/CleverTapSDK/CTRequestSender.h index e939df61..62c66ee3 100644 --- a/CleverTapSDK/CTRequestSender.h +++ b/CleverTapSDK/CTRequestSender.h @@ -8,7 +8,6 @@ #import #import "CTRequest.h" -//#import "CTDomainFactory.h" #if CLEVERTAP_SSL_PINNING #import "CTPinnedNSURLSessionDelegate.h" #endif diff --git a/CleverTapSDK/CTRequestSender.m b/CleverTapSDK/CTRequestSender.m index 2795d8cd..352eb9bd 100644 --- a/CleverTapSDK/CTRequestSender.m +++ b/CleverTapSDK/CTRequestSender.m @@ -19,16 +19,12 @@ @interface CTRequestSender () @property (nonatomic, strong) NSString *redirectDomain; @property (nonatomic, assign, readonly) BOOL sslPinningEnabled; - #if CLEVERTAP_SSL_PINNING @property(nonatomic, strong) CTPinnedNSURLSessionDelegate *urlSessionDelegate; @property (nonatomic, strong) NSArray *sslCertNames; #endif @end -//NSURLSession *urlSession; -//BOOL sslPinningEnabled; - @implementation CTRequestSender - (instancetype _Nonnull)initWithConfig:(CleverTapInstanceConfig *_Nonnull)config redirectDomain:(NSString* _Nonnull)redirectDomain { @@ -100,6 +96,4 @@ - (void)send:(CTRequest *_Nonnull)ctRequest { [task resume]; } - - @end diff --git a/CleverTapSDK/CleverTap+CTVar.h b/CleverTapSDK/CleverTap+CTVar.h index b984ac8e..acc9d89f 100644 --- a/CleverTapSDK/CleverTap+CTVar.h +++ b/CleverTapSDK/CleverTap+CTVar.h @@ -26,8 +26,6 @@ NS_SWIFT_NAME(defineVar(name:double:)); NS_SWIFT_NAME(defineVar(name:cgFloat:)); - (CTVar *)defineVar:(NSString *)name withShort:(short)defaultValue NS_SWIFT_NAME(defineVar(name:short:)); -//- (LPVar *)defineVar:(NSString *)name withChar:(char)defaultValue -//NS_SWIFT_NAME(defineVar(name:integer:)); - (CTVar *)defineVar:(NSString *)name withBool:(BOOL)defaultValue NS_SWIFT_NAME(defineVar(name:boolean:)); - (CTVar *)defineVar:(NSString *)name withString:(nullable NSString *)defaultValue diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index e790b54d..10c8b140 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -206,10 +206,6 @@ @interface CleverTap () { @property (nonatomic, strong) NSMutableArray *profileQueue; @property (nonatomic, strong) NSMutableArray *notificationsQueue; @property (nonatomic, strong) NSURLSession *urlSession; -//@property (nonatomic, strong) NSString *redirectDomain; -//@property (nonatomic, strong) NSString *explictEndpointDomain; -//@property (nonatomic, strong) NSString *redirectNotifViewedDomain; -//@property (nonatomic, strong) NSString *explictNotifViewedEndpointDomain; @property (nonatomic, strong) CTDomainFactory *domainFactory; @property (nonatomic, strong) CTRequestSender *requestSender; @property (nonatomic, assign) NSTimeInterval lastMutedTs; @@ -1341,7 +1337,7 @@ - (void)recordAppLaunched:(NSString *)caller { return; } - // LOAD VARS FROM CACHE BEFORE APP LAUNCHED + // Load Vars from cache before App Launched [self.variables.varCache loadDiffs]; self.appLaunchProcessed = YES; From 3a08a3c39b7a27c9a4a97e414c4f942e5641049d Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Wed, 12 Apr 2023 01:18:43 +0300 Subject: [PATCH 73/86] Add docs --- CleverTapSDK/CleverTap.h | 52 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index 40677a07..725adf76 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -1331,12 +1331,42 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; #pragma mark Product Experiences - Vars +/*! + @method + + @abstract + Adds a callback to be invoked when variables are initialised with server values. Will be called each time new values are fetched. + + @param block a callback to add. + */ - (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; +/*! + @method + + @abstract + Adds a callback to be invoked only once when variables are initialised with server values. + + @param block a callback to add. + */ - (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block; - + +/*! + @method + + @abstract + Uploads variables to the server. Requires Development/Debug build/configuration. + */ - (void)syncVariables; +/*! + @method + + @abstract + Uploads variables to the server. + + @param isProduction Provide `true` if variables must be sync in Productuon build/configuration. + */ - (void)syncVariables:(BOOL)isProduction; /*! @@ -1354,8 +1384,28 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; */ - (void)fetchVariables:(CleverTapFetchVariablesBlock _Nullable)block; +/*! + @method + + @abstract + Get an instance of a variable or a group. + + @param name The name of the variable or the group. + + @return + The instance of the variable or the group, or nil if not created yet. + + */ - (CTVar * _Nullable)getVariable:(NSString * _Nonnull)name; +/*! + @method + + @abstract + Get a copy of the current value of a variable or a group. + + @param name The name of the variable or the group. + */ - (id _Nullable)getVariableValue:(NSString * _Nonnull)name; @end From 4af9ed6abd64d650b1b67211a0d6e7dc0e14746c Mon Sep 17 00:00:00 2001 From: Nikola Zagorchev Date: Tue, 18 Apr 2023 10:23:42 +0300 Subject: [PATCH 74/86] Remove components from CTVar init --- CleverTapSDK/ProductExperiences/CTVar-Internal.h | 1 - CleverTapSDK/ProductExperiences/CTVar.m | 4 ++-- CleverTapSDK/ProductExperiences/CTVariables.m | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/CleverTapSDK/ProductExperiences/CTVar-Internal.h b/CleverTapSDK/ProductExperiences/CTVar-Internal.h index 8c058885..10954bc5 100644 --- a/CleverTapSDK/ProductExperiences/CTVar-Internal.h +++ b/CleverTapSDK/ProductExperiences/CTVar-Internal.h @@ -6,7 +6,6 @@ NS_ASSUME_NONNULL_BEGIN @interface CTVar () - (instancetype)initWithName:(NSString *)name - withComponents:(NSArray *)components withDefaultValue:(NSObject *)defaultValue withKind:(NSString *)kind varCache:(CTVarCache *)cache; diff --git a/CleverTapSDK/ProductExperiences/CTVar.m b/CleverTapSDK/ProductExperiences/CTVar.m index b9bd9d09..53499d53 100644 --- a/CleverTapSDK/ProductExperiences/CTVar.m +++ b/CleverTapSDK/ProductExperiences/CTVar.m @@ -20,8 +20,8 @@ @interface CTVar (PrivateProperties) @implementation CTVar -- (instancetype)initWithName:(NSString *)name withComponents:(NSArray *)components - withDefaultValue:(NSNumber *)defaultValue withKind:(NSString *)kind varCache:(CTVarCache *)cache +- (instancetype)initWithName:(NSString *)name withDefaultValue:(NSNumber *)defaultValue + withKind:(NSString *)kind varCache:(CTVarCache *)cache { self = [super init]; if (self) { diff --git a/CleverTapSDK/ProductExperiences/CTVariables.m b/CleverTapSDK/ProductExperiences/CTVariables.m index 7a38ef9e..fc607283 100644 --- a/CleverTapSDK/ProductExperiences/CTVariables.m +++ b/CleverTapSDK/ProductExperiences/CTVariables.m @@ -47,9 +47,7 @@ - (CTVar *)define:(NSString *)name with:(NSObject *)defaultValue kind:(NSString return existing; } CT_END_TRY - NSArray *nameComponents = [self.varCache getNameComponents:name]; CTVar *var = [[CTVar alloc] initWithName:name - withComponents:nameComponents withDefaultValue:defaultValue withKind:kind varCache:self.varCache]; From 81e40e70fa1ae14b8a316c7c7b94fed19a30f147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=90=8C=E5=BE=B7?= Date: Sun, 23 Apr 2023 16:25:21 +0800 Subject: [PATCH 75/86] fix Non-null judgment handling, it crashes all over the place when you pass nil --- CleverTapSDK/CleverTap.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 3158830f..6b9eac17 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -4104,7 +4104,9 @@ + (void)getLocationWithSuccess:(void (^)(CLLocationCoordinate2D location))succes else { NSString *errorMsg = @"To Enable CleverTap Location services/apis please build the SDK with the CLEVERTAP_LOCATION macro or use enableLocation method"; CleverTapLogStaticDebug(@"%@",errorMsg); - error(errorMsg); + if (error) { + error(errorMsg); + } } #endif } From e012d282d23cbc6501c3d1a3a1bbc9ded735145c Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Tue, 2 May 2023 16:26:27 +0530 Subject: [PATCH 76/86] updated hello request URL --- CleverTapSDK/CleverTap.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index 3158830f..1a154fda 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -85,7 +85,7 @@ NSString *const kQUEUE_NAME_EVENTS = @"events"; NSString *const kQUEUE_NAME_NOTIFICATIONS = @"notifications"; -NSString *const kHANDSHAKE_URL = @"https://eu1.clevertap-prod.com/hello"; +NSString *const kHANDSHAKE_URL = @"https://clevertap-prod.com/hello"; NSString *const kREDIRECT_DOMAIN_KEY = @"CLTAP_REDIRECT_DOMAIN_KEY"; NSString *const kREDIRECT_NOTIF_VIEWED_DOMAIN_KEY = @"CLTAP_REDIRECT_NOTIF_VIEWED_DOMAIN_KEY"; From 1f1929b19a7d9b175135b0fef524e4c24cb05954 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Wed, 3 May 2023 14:36:04 +0530 Subject: [PATCH 77/86] Update CHANGELOG.md --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b59361b..5f2a3d12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Change Log All notable changes to this project will be documented in this file. +### [Version 5.0.0](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/5.0.0) (May 05, 2023) + +#### Added +- Introducing PE variables. + +#### Fixed +- Fixes a bug where the `getLocationWithSuccess` method would cause crashes. + ### [Version 4.2.2](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/4.2.2) (April 03, 2023) #### Fixed From ba3b8c0ec88860ab15638c0289d84d73236833c9 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Wed, 3 May 2023 14:44:55 +0530 Subject: [PATCH 78/86] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f2a3d12..cecf6d0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. #### Fixed - Fixes a bug where the `getLocationWithSuccess` method would cause crashes. +- Minor improvements to saving session data in background state. ### [Version 4.2.2](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/4.2.2) (April 03, 2023) From c65053f2a3c3dab308b55e587d3da3db7c74cc22 Mon Sep 17 00:00:00 2001 From: Nishant Kumar Date: Wed, 3 May 2023 16:13:32 +0530 Subject: [PATCH 79/86] refactor(SDK-2943): Updated recordEventWithProps argument key - Updated to "properties" to make key consistent with profilePush and onUserLogin. - Tested on Starter app in which we were already passing key as "properties" instead of "props". --- CleverTapSDK/CleverTapJSInterface.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CleverTapSDK/CleverTapJSInterface.m b/CleverTapSDK/CleverTapJSInterface.m index 5d32f950..2c9231d5 100644 --- a/CleverTapSDK/CleverTapJSInterface.m +++ b/CleverTapSDK/CleverTapJSInterface.m @@ -41,7 +41,7 @@ - (void)userContentController:(nonnull WKUserContentController *)userContentCont - (void)handleMessageFromWebview:(NSDictionary *)message forInstance:(CleverTap *)cleverTap { NSString *action = [message objectForKey:@"action"]; if ([action isEqual:@"recordEventWithProps"]) { - [cleverTap recordEvent: message[@"event"] withProps: message[@"props"]]; + [cleverTap recordEvent: message[@"event"] withProps: message[@"properties"]]; } else if ([action isEqual: @"profilePush"]) { [cleverTap profilePush: message[@"properties"]]; } else if ([action isEqual: @"profileSetMultiValues"]) { From 943874cf115ff689e68da0e4d3eac120919cba12 Mon Sep 17 00:00:00 2001 From: Nishant Kumar Date: Wed, 3 May 2023 17:12:02 +0530 Subject: [PATCH 80/86] doc: Updated Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cecf6d0e..a420260e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. #### Fixed - Fixes a bug where the `getLocationWithSuccess` method would cause crashes. - Minor improvements to saving session data in background state. +- Streamlined the argument key of `recordEventWithProps` in CleverTapJSInterface. ### [Version 4.2.2](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/4.2.2) (April 03, 2023) From eb3c61e9ac7a43b8d4db88e94663f3778d21d036 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Fri, 5 May 2023 13:59:33 +0530 Subject: [PATCH 81/86] added docs for PE variables --- README.md | 4 + docs/Variables.md | 319 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100644 docs/Variables.md diff --git a/README.md b/README.md index fe514d6e..1c560a99 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,10 @@ CleverTap Geofence SDK provides Geofencing capabilities to CleverTap iOS SDK. To CleverTap iOS SDK supports Push Primer for push notification runtime permission, refer [Push Primer doc](/docs/PushPrimer.md) for more details. +## Remote Config Variables + +CleverTap iOS SDK supports creating remote config variables, refer [Remote Config Variables](/docs/Variables.md) for more details and usage examples. + ## 𝌡 Example Usage * A [demo application](/ObjCStarter) showing the integration of our SDK in Objective-C language. * A [demo application](/SwiftStarter) showing the integration of our SDK in Swift language. diff --git a/docs/Variables.md b/docs/Variables.md new file mode 100644 index 00000000..6598a26a --- /dev/null +++ b/docs/Variables.md @@ -0,0 +1,319 @@ +# Overview +You can define variables using the CleverTap iOS SDK. When you define a variable in your code, you can sync them to the CleverTap Dashboard via the provided SDK methods. + +# Supported Variable Types + +Currently, CleverTap SDK supports the following variable types: + +- String +- boolean +- Dictionary +- int +- float +- double +- short +- long +- Number + +# Define Variables + +Variables can be defined using a shared or custom CleverTap instance. The Variable is defined using the `defineVar` method, which returns an instance of a `CTVar` variable. You must provide the name and default value of the variable. + +```swift +// Primitive types +let var_string = CleverTap.sharedInstance()?.defineVar(name: "var_string", string: "hello, world") +let var_int = CleverTap.sharedInstance()?.defineVar(name: "var_int", integer: 10) +let var_bool = CleverTap.sharedInstance()?.defineVar(name: "var_bool", boolean: true) +let var_float = CleverTap.sharedInstance()?.defineVar(name: "var_float", float: 6.0) +let var_double = CleverTap.sharedInstance()?.defineVar(name: "var_double", double: 60.999) +let var_short = CleverTap.sharedInstance()?.defineVar(name: "var_short", short: 1) +let var_number = CleverTap.sharedInstance()?.defineVar(name: "var_number", number: NSNumber(value: 32)) +let var_long = CleverTap.sharedInstance()?.defineVar(name: "var_long", long: 64) +// Dictionary +let var_dict = CleverTap.sharedInstance()?.defineVar(name: "var_dict", dictionary: [ + "nested_string": "hello, nested", + "nested_double": 10.5 + ]) + +let var_dict_nested = CleverTap.sharedInstance()?.defineVar(name: "var_dict_complex", dictionary: [ + "nested_int": 1, + "nested_string": "hello, world", + "nested_map": [ + "nested_map_int": 15, + "nested_map_string": "hello, nested map", + ] + ]) + + + +``` +```objectivec +#import + +// Primitive types + CTVar *var_string = [[CleverTap sharedInstance] defineVar:@"var_string" withString:@"hello, world"]; + CTVar *var_int = [[CleverTap sharedInstance] defineVar:@"var_int" withInt:10]; + CTVar *var_bool = [[CleverTap sharedInstance] defineVar:@"var_bool" withBool:YES]; + CTVar *var_float = [[CleverTap sharedInstance] defineVar:@"var_float" withFloat:6.0]; + CTVar *var_double = [[CleverTap sharedInstance] defineVar:@"var_double" withDouble:60.999]; + CTVar *var_short = [[CleverTap sharedInstance] defineVar:@"var_short" withShort:1]; + CTVar *var_number = [[CleverTap sharedInstance] defineVar:@"var_number" withNumber:[[NSNumber alloc] initWithInt:32]]; + CTVar *var_long = [[CleverTap sharedInstance] defineVar:@"var_long" withLong:64]; + // Dictionary + CTVar *var_dict = [[CleverTap sharedInstance] defineVar:@"var_dict" withDictionary:@{ + @"nested_string": @"hello, nested", + @"nested_double": @10.5 + }]; + CTVar *var_dict_nested = [[CleverTap sharedInstance] defineVar:@"var_dict_complex" withDictionary:@{ + @"nested_int": @1, + @"nested_string": @"hello, world", + @"nested_map": @{ + @"nested_map_int": @15, + @"nested_map_string": @"hello, nested map", + } + }]; + +``` + + + +# Setup Callbacks + +CleverTap iOS SDK provides several callbacks for the developer to receive feedback from the SDK. You can use them as per your requirement, using all of them is not mandatory. They are as follows: + +- Status of fetch variables request +- `onVariablesChanged` +- `onceVariablesChanged` +- `onValueChanged` +- Variables Delegate + +## Status of Variables Fetch Request + +This method provides a boolean flag to ensure that the variables are successfully fetched from the server. + +```swift +CleverTap.sharedInstance()?.fetchVariables({ success in + print(success) +}) +``` +```objectivec +#import + +[[CleverTap sharedInstance] fetchVariables:^(BOOL success) { + + }]; +``` + + + +## `onVariablesChanged` + +This callback is invoked when variables are initialized with values fetched from the server. It is called each time new values are fetched. + +```swift +let var_string = CleverTap.sharedInstance()?.defineVar(name: "myString", string: "hello,world") +CleverTap.sharedInstance()?.onVariablesChanged { + print("CleverTap.onVariablesChanged: \(var_string?.value ?? "")") +} + + +``` +```objectivec +#import + +CTVar *var_string = [[CleverTap sharedInstance] defineVar:@"var_string" withString:@"hello, world"]; + [[CleverTap sharedInstance] onVariablesChanged:^{ + NSLog(@"CleverTap.onVariablesChanged: %@", [var_string value]); + }]; +``` + + + +## `onceVariablesChanged` + +This callback is invoked when variables are initialized with values fetched from the server. It is called only once. + +```swift +let var_string = CleverTap.sharedInstance()?.defineVar(name: "myString", string: "hello,world") +CleverTap.sharedInstance()?.onceVariablesChanged { + print("CleverTap.onceVariablesChanged: \(var_string?.value ?? "")") +} + +``` +```objectivec +#import + +CTVar *var_string = [[CleverTap sharedInstance] defineVar:@"var_string" withString:@"hello, world"]; + + + [[CleverTap sharedInstance] onceVariablesChanged:^{ + // Executed only once + NSLog(@"CleverTap.onceVariablesChanged: %@", [var_string value]); + }]; + +``` + + + +## `onValueChanged` + +This callback is invoked when the value of the variable changes. + +```swift +let var_string = CleverTap.sharedInstance()?.defineVar(name: "myString", string: "hello,world") +var_string?.onValueChanged { + print("var_string.onValueChanged: \(var_string?.value ?? "")") +} + +``` +```objectivec +#import + +CTVar *var_string = [[CleverTap sharedInstance] defineVar:@"var_string" withString:@"hello, world"]; + [var_string onValueChanged:^{ + NSLog(@"var_string.onValueChanged: %@", [var_string value]); + }]; +``` + + + +## Variables Delegate + +The `VarDelegate` method is implemented to be invoked when the variable value is changed. + +```swift +@objc class VarDelegateImpl: NSObject, VarDelegate { + func valueDidChange(_ variable: CleverTapSDK.Var) { + print("CleverTap \(String(describing: variable.name)):valueDidChange to: \(variable.value!)") + } +} + +var_string?.setDelegate(self) +``` +```objectivec +#import + +@interface CTVarDelegateImpl : NSObject +@end + + +@implementation CTVarDelegateImpl +- (void)valueDidChange:(CTVar *)variable { +// valueDidChange +} +@end + +CTVarDelegateImpl *del = [[CTVarDelegateImpl alloc] init]; +[var_string setDelegate:del]; +``` + + + +# Sync Defined Variables + +After defining your variables in the code, you must send/sync variables to the server. To do so, the app must be in DEBUG mode and mark a particular CleverTap user profile as a test profile from the CleverTap dashboard. [Learn how to mark a profile as **Test Profile**](doc:concepts-user-profiles#mark-a-user-profile-as-a-test-profile) + +After marking the profile as a test profile, you must sync the app variables in DEBUG mode: + +```swift +// 1. Define CleverTap variables +// … +// 2. Add variables/values changed callbacks +// … + +// 3. Sync CleverTap Variables from DEBUG mode/builds +CleverTap.sharedInstance()?.syncVariables(); +``` +```objectivec +// 1. Define CleverTap variables +// … +// 2. Add variables/values changed callbacks +// … + +// 3. Sync CleverTap Variables from DEBUG mode/builds +[[CleverTap sharedInstance] syncVariables]; +``` + + + +> 📘 Key Points to Remember +> +> - In a scenario where there is already a draft created by another user profile in the dashboard, the sync call will fail to avoid overriding important changes made by someone else. In this case, Publish or Dismiss the existing draft before you proceed with syncing variables again. However, you can override a draft you created via the sync method previously to optimize the integration experience. +> - You can receive the following Logcat logs from the CleverTap SDK: +> - Variables synced successfully. +> - Unauthorized access from a non-test profile. To address this, mark the profile as a test profile from the CleverTap dashboard. + +# Fetch Variables During a Session + +You can fetch the updated values for your CleverTap variables from the server during a session. If variables have changed, the appropriate callbacks will be fired. The provided callback provides a boolean flag that indicates if the fetch call was successful. The callback is fired regardless of whether the variables have changed or not. + +```swift +CleverTap.sharedInstance()?.fetchVariables({ success in + print(success) +}) +``` +```objectivec + [[CleverTap sharedInstance] fetchVariables:^(BOOL success) { + + }]; +``` + + + +# Use Fetched Variables Values + +This process involves the following two major steps: + +1. Fetch variable values. +2. Access variable values. + +## Fetch Variable Values + +Variables are updated automatically when server values are received. If you want to receive feedback when a specific variable is updated, use the individual callback: + +```swift +variable?.onValueChanged { + print("variable.onValueChanged: \(variable?.value ?? "")") +} +``` +```objectivec + [variable onValueChanged:^{ + NSLog(@"variable.onValueChanged: %@", [variable value]); + }]; +``` + + + +## Access Variable Values + +You can access these fetched values in the following three ways: + +### From `Var` instance + +You can use several methods on the `Var` instance as shown in the following code: + +```swift +variable?.defaultValue // returns default value +variable?.value // returns current value +variable?.numberValue // returns value as NSNumber if applicable +variable?.stringValue // returns value as String +``` +```objectivec +variable.defaultValue; // returns default value +variable.value; // returns current value +variable.numberValue; // returns value as NSNumber if applicable +variable.stringValue; // returns value as String +``` + + + +### Using `CleverTap` Instance method + +You can use the `CleverTap` instance method to get the current value of a variable. If the variable is nonexistent, the method returns `null`: + +```swift +CleverTap.sharedInstance()?.getVariableValue("variable name") +``` +```objectivec +[[CleverTap sharedInstance]getVariableValue:@"variable name"]; +``` \ No newline at end of file From b800ccb1dfffa3b318c76abdbb08d1d8398082f8 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Fri, 5 May 2023 14:04:58 +0530 Subject: [PATCH 82/86] minor code snippet changes --- docs/Variables.md | 54 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/docs/Variables.md b/docs/Variables.md index 6598a26a..57df3f89 100644 --- a/docs/Variables.md +++ b/docs/Variables.md @@ -20,6 +20,8 @@ Currently, CleverTap SDK supports the following variable types: Variables can be defined using a shared or custom CleverTap instance. The Variable is defined using the `defineVar` method, which returns an instance of a `CTVar` variable. You must provide the name and default value of the variable. ```swift +// Swift + // Primitive types let var_string = CleverTap.sharedInstance()?.defineVar(name: "var_string", string: "hello, world") let var_int = CleverTap.sharedInstance()?.defineVar(name: "var_int", integer: 10) @@ -48,6 +50,8 @@ let var_dict_nested = CleverTap.sharedInstance()?.defineVar(name: "var_dict_comp ``` ```objectivec +// Objective-C + #import // Primitive types @@ -92,11 +96,15 @@ CleverTap iOS SDK provides several callbacks for the developer to receive feedba This method provides a boolean flag to ensure that the variables are successfully fetched from the server. ```swift +// Swift + CleverTap.sharedInstance()?.fetchVariables({ success in print(success) }) ``` ```objectivec +// Objective-C + #import [[CleverTap sharedInstance] fetchVariables:^(BOOL success) { @@ -111,6 +119,8 @@ CleverTap.sharedInstance()?.fetchVariables({ success in This callback is invoked when variables are initialized with values fetched from the server. It is called each time new values are fetched. ```swift +// Swift + let var_string = CleverTap.sharedInstance()?.defineVar(name: "myString", string: "hello,world") CleverTap.sharedInstance()?.onVariablesChanged { print("CleverTap.onVariablesChanged: \(var_string?.value ?? "")") @@ -119,6 +129,8 @@ CleverTap.sharedInstance()?.onVariablesChanged { ``` ```objectivec +// Objective-C + #import CTVar *var_string = [[CleverTap sharedInstance] defineVar:@"var_string" withString:@"hello, world"]; @@ -134,6 +146,8 @@ CTVar *var_string = [[CleverTap sharedInstance] defineVar:@"var_string" withStri This callback is invoked when variables are initialized with values fetched from the server. It is called only once. ```swift +// Swift + let var_string = CleverTap.sharedInstance()?.defineVar(name: "myString", string: "hello,world") CleverTap.sharedInstance()?.onceVariablesChanged { print("CleverTap.onceVariablesChanged: \(var_string?.value ?? "")") @@ -141,6 +155,8 @@ CleverTap.sharedInstance()?.onceVariablesChanged { ``` ```objectivec +// Objective-C + #import CTVar *var_string = [[CleverTap sharedInstance] defineVar:@"var_string" withString:@"hello, world"]; @@ -160,6 +176,8 @@ CTVar *var_string = [[CleverTap sharedInstance] defineVar:@"var_string" withStri This callback is invoked when the value of the variable changes. ```swift +// Swift + let var_string = CleverTap.sharedInstance()?.defineVar(name: "myString", string: "hello,world") var_string?.onValueChanged { print("var_string.onValueChanged: \(var_string?.value ?? "")") @@ -167,6 +185,8 @@ var_string?.onValueChanged { ``` ```objectivec +// Objective-C + #import CTVar *var_string = [[CleverTap sharedInstance] defineVar:@"var_string" withString:@"hello, world"]; @@ -182,6 +202,8 @@ CTVar *var_string = [[CleverTap sharedInstance] defineVar:@"var_string" withStri The `VarDelegate` method is implemented to be invoked when the variable value is changed. ```swift +// Swift + @objc class VarDelegateImpl: NSObject, VarDelegate { func valueDidChange(_ variable: CleverTapSDK.Var) { print("CleverTap \(String(describing: variable.name)):valueDidChange to: \(variable.value!)") @@ -191,6 +213,8 @@ The `VarDelegate` method is implemented to be invoked when the variable value is var_string?.setDelegate(self) ``` ```objectivec +// Objective-C + #import @interface CTVarDelegateImpl : NSObject @@ -216,6 +240,8 @@ After defining your variables in the code, you must send/sync variables to the s After marking the profile as a test profile, you must sync the app variables in DEBUG mode: ```swift +// Swift + // 1. Define CleverTap variables // … // 2. Add variables/values changed callbacks @@ -225,6 +251,8 @@ After marking the profile as a test profile, you must sync the app variables in CleverTap.sharedInstance()?.syncVariables(); ``` ```objectivec +// Objective-C + // 1. Define CleverTap variables // … // 2. Add variables/values changed callbacks @@ -248,14 +276,18 @@ CleverTap.sharedInstance()?.syncVariables(); You can fetch the updated values for your CleverTap variables from the server during a session. If variables have changed, the appropriate callbacks will be fired. The provided callback provides a boolean flag that indicates if the fetch call was successful. The callback is fired regardless of whether the variables have changed or not. ```swift +// Swift + CleverTap.sharedInstance()?.fetchVariables({ success in print(success) }) ``` ```objectivec - [[CleverTap sharedInstance] fetchVariables:^(BOOL success) { +// Objective-C + +[[CleverTap sharedInstance] fetchVariables:^(BOOL success) { - }]; +}]; ``` @@ -272,14 +304,18 @@ This process involves the following two major steps: Variables are updated automatically when server values are received. If you want to receive feedback when a specific variable is updated, use the individual callback: ```swift +// Swift + variable?.onValueChanged { print("variable.onValueChanged: \(variable?.value ?? "")") } ``` ```objectivec - [variable onValueChanged:^{ - NSLog(@"variable.onValueChanged: %@", [variable value]); - }]; +// Objective-C + +[variable onValueChanged:^{ + NSLog(@"variable.onValueChanged: %@", [variable value]); +}]; ``` @@ -293,12 +329,16 @@ You can access these fetched values in the following three ways: You can use several methods on the `Var` instance as shown in the following code: ```swift +// Swift + variable?.defaultValue // returns default value variable?.value // returns current value variable?.numberValue // returns value as NSNumber if applicable variable?.stringValue // returns value as String ``` ```objectivec +// Objective-C + variable.defaultValue; // returns default value variable.value; // returns current value variable.numberValue; // returns value as NSNumber if applicable @@ -312,8 +352,12 @@ variable.stringValue; // returns value as String You can use the `CleverTap` instance method to get the current value of a variable. If the variable is nonexistent, the method returns `null`: ```swift +// Swift + CleverTap.sharedInstance()?.getVariableValue("variable name") ``` ```objectivec +// Objective-C + [[CleverTap sharedInstance]getVariableValue:@"variable name"]; ``` \ No newline at end of file From 75e5411b941db02ccef4059c7f601b88fcd3e0b7 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Fri, 5 May 2023 14:14:31 +0530 Subject: [PATCH 83/86] Update Variables.md --- docs/Variables.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Variables.md b/docs/Variables.md index 57df3f89..d0329315 100644 --- a/docs/Variables.md +++ b/docs/Variables.md @@ -235,7 +235,7 @@ CTVarDelegateImpl *del = [[CTVarDelegateImpl alloc] init]; # Sync Defined Variables -After defining your variables in the code, you must send/sync variables to the server. To do so, the app must be in DEBUG mode and mark a particular CleverTap user profile as a test profile from the CleverTap dashboard. [Learn how to mark a profile as **Test Profile**](doc:concepts-user-profiles#mark-a-user-profile-as-a-test-profile) +After defining your variables in the code, you must send/sync variables to the server. To do so, the app must be in DEBUG mode and mark a particular CleverTap user profile as a test profile from the CleverTap dashboard. [Learn how to mark a profile as **Test Profile**](https://developer.clevertap.com/docs/concepts-user-profiles#mark-a-user-profile-as-a-test-profile) After marking the profile as a test profile, you must sync the app variables in DEBUG mode: @@ -360,4 +360,4 @@ CleverTap.sharedInstance()?.getVariableValue("variable name") // Objective-C [[CleverTap sharedInstance]getVariableValue:@"variable name"]; -``` \ No newline at end of file +``` From ecaff173e20ca39207148540ab50d6184230505b Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Fri, 5 May 2023 14:32:08 +0530 Subject: [PATCH 84/86] Update CHANGELOG.md --- CHANGELOG.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a420260e..e22a6902 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,32 @@ All notable changes to this project will be documented in this file. ### [Version 5.0.0](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/5.0.0) (May 05, 2023) #### Added -- Introducing PE variables. +- Adds support for Remote Config Variables. Please refer to the [Remote Config Variables doc](/docs/Variables.md) to read more on how to integrate this to your app. #### Fixed - Fixes a bug where the `getLocationWithSuccess` method would cause crashes. -- Minor improvements to saving session data in background state. -- Streamlined the argument key of `recordEventWithProps` in CleverTapJSInterface. +- Adds minor improvements to saving session data in background state. +- Streamlines the argument key of `recordEventWithProps` in `CleverTapJSInterface`. + +#### Deprecated +- The following methods related to Product Config and Feature Flags have been marked as deprecated in this release. These methods will be removed in the future with prior notice + - Feature Flags + - `- (void)ctFeatureFlagsUpdated;` + - `- (BOOL)get:(NSString* _Nonnull)key withDefaultValue:(BOOL)defaultValue` + - Product Config + - `- (void)ctProductConfigFetched` + - `- (void)ctProductConfigActivated` + - `- (void)ctProductConfigInitialized` + - `- (void)fetch` + - `- (void)fetchWithMinimumInterval:(NSTimeInterval)minimumInterval` + - `- (void)setMinimumFetchInterval:(NSTimeInterval)minimumFetchInterval` + - `- (void)activate` + - `- (void)fetchAndActivate` + - `- (void)setDefaults:(NSDictionary *_Nullable)defaults` + - `- (void)setDefaultsFromPlistFileName:(NSString *_Nullable)fileName` + - `- (CleverTapConfigValue *_Nullable)get:(NSString* _Nonnull)key` + - `- (NSDate *_Nullable)getLastFetchTimeStamp` + - `- (void)reset` ### [Version 4.2.2](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/4.2.2) (April 03, 2023) From 7d51e19698ec4c09093b8744de883af2d8cb7979 Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Fri, 5 May 2023 14:45:13 +0530 Subject: [PATCH 85/86] Update Variables.md --- docs/Variables.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Variables.md b/docs/Variables.md index d0329315..36b554bc 100644 --- a/docs/Variables.md +++ b/docs/Variables.md @@ -267,9 +267,9 @@ CleverTap.sharedInstance()?.syncVariables(); > 📘 Key Points to Remember > > - In a scenario where there is already a draft created by another user profile in the dashboard, the sync call will fail to avoid overriding important changes made by someone else. In this case, Publish or Dismiss the existing draft before you proceed with syncing variables again. However, you can override a draft you created via the sync method previously to optimize the integration experience. -> - You can receive the following Logcat logs from the CleverTap SDK: +> - You can receive the following console logs from the CleverTap SDK: > - Variables synced successfully. -> - Unauthorized access from a non-test profile. To address this, mark the profile as a test profile from the CleverTap dashboard. +> - Unauthorized access from a non-test profile. Please mark this profile as a test profile from the CleverTap dashboard. # Fetch Variables During a Session From 45ddb09de45ee88c6764f9d9cefc43f78bb72e06 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Fri, 5 May 2023 15:01:24 +0530 Subject: [PATCH 86/86] minor fix --- CleverTapSDK/CleverTap+FeatureFlags.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CleverTapSDK/CleverTap+FeatureFlags.h b/CleverTapSDK/CleverTap+FeatureFlags.h index f4275ab9..cf25a192 100644 --- a/CleverTapSDK/CleverTap+FeatureFlags.h +++ b/CleverTapSDK/CleverTap+FeatureFlags.h @@ -4,7 +4,7 @@ __attribute__((deprecated("This protocol has been deprecated and will be removed in the future versions of this SDK."))) @protocol CleverTapFeatureFlagsDelegate @optional -- (void)ctFeatureFlagsUpdated; +- (void)ctFeatureFlagsUpdated __attribute__((deprecated("This protocol method has been deprecated and will be removed in the future versions of this SDK."))); @end