diff --git a/CleverTapSDK/CTConstants.h b/CleverTapSDK/CTConstants.h index c21fd5b2..a9bd0c70 100644 --- a/CleverTapSDK/CTConstants.h +++ b/CleverTapSDK/CTConstants.h @@ -97,7 +97,6 @@ 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; @@ -106,7 +105,6 @@ extern NSString *CT_PE_BOOL_TYPE; extern NSString *CT_PE_DEFAULT_VALUE; extern NSString *CLTAP_PROFILE_IDENTITY_KEY; -#define CLTAP_DEFINE_VARS_URL @"/defineVars" #pragma mark Constants for In-App Notifications #define CLTAP_INAPP_JSON_RESPONSE_KEY @"inapp_notifs" diff --git a/CleverTapSDK/CTConstants.m b/CleverTapSDK/CTConstants.m index 6813fe58..44a660da 100644 --- a/CleverTapSDK/CTConstants.m +++ b/CleverTapSDK/CTConstants.m @@ -16,8 +16,6 @@ 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/CTRequestFactory.h b/CleverTapSDK/CTRequestFactory.h index 9586ba7b..634d71b7 100644 --- a/CleverTapSDK/CTRequestFactory.h +++ b/CleverTapSDK/CTRequestFactory.h @@ -3,7 +3,7 @@ // CleverTapSDK // // Created by Akash Malhotra on 09/01/23. -// Copyright © 2023 CleverTap. All rights reserved. +// Copyright © 2024 CleverTap. All rights reserved. // #import @@ -14,7 +14,9 @@ + (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; ++ (CTRequest *_Nonnull)syncVarsRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config params:(id _Nullable)params domain:(NSString *_Nonnull)domain; ++ (CTRequest *_Nonnull)syncTemplatesRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config params:(id _Nullable)params domain:(NSString *_Nonnull)domain; + @end diff --git a/CleverTapSDK/CTRequestFactory.m b/CleverTapSDK/CTRequestFactory.m index 5c2923c5..9edc1c49 100644 --- a/CleverTapSDK/CTRequestFactory.m +++ b/CleverTapSDK/CTRequestFactory.m @@ -3,7 +3,7 @@ // CleverTapSDK // // Created by Akash Malhotra on 09/01/23. -// Copyright © 2023 CleverTap. All rights reserved. +// Copyright © 2024 CleverTap. All rights reserved. // #import "CTRequestFactory.h" @@ -12,15 +12,25 @@ @implementation CTRequestFactory + (CTRequest *_Nonnull)helloRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config { - return [[CTRequest alloc]initWithHttpMethod:@"GET" config:config params:nil url:kHANDSHAKE_URL]; + 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 { - return [[CTRequest alloc]initWithHttpMethod:@"POST" config:config params: params url: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]; ++ (CTRequest *_Nonnull)syncVarsRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config params:(id _Nullable)params domain:(NSString *_Nonnull)domain { + NSString *url = [self urlWithDomain:domain andPath:@"defineVars"]; + return [[CTRequest alloc] initWithHttpMethod:@"POST" config:config params: params url:url]; +} + ++ (CTRequest *_Nonnull)syncTemplatesRequestWithConfig:(CleverTapInstanceConfig *_Nonnull)config params:(id _Nullable)params domain:(NSString *_Nonnull)domain { + NSString *url = [self urlWithDomain:domain andPath:@"defineTemplates"]; + return [[CTRequest alloc] initWithHttpMethod:@"POST" config:config params: params url:url]; +} + ++ (NSString *)urlWithDomain:(NSString *)domain andPath:(NSString *)path { + return [NSString stringWithFormat:@"https://%@/%@", domain, path]; } @end diff --git a/CleverTapSDK/CleverTap.h b/CleverTapSDK/CleverTap.h index c9e9081a..c6705aa2 100644 --- a/CleverTapSDK/CleverTap.h +++ b/CleverTapSDK/CleverTap.h @@ -1417,6 +1417,26 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification; */ - (id _Nullable)getVariableValue:(NSString * _Nonnull)name; +#pragma mark Custom Templates and Functions + +/*! + @method + + @abstract + Uploads Custom in-app templates and app functions to the server. Requires Development/Debug build/configuration. + */ +- (void)syncCustomTemplates; + +/*! + @method + + @abstract + Uploads Custom in-app templates and app functions to the server. + + @param isProduction Provide `true` if Custom in-app templates and app functions must be sync in Productuon build/configuration. + */ +- (void)syncCustomTemplates:(BOOL)isProduction; + @end #pragma clang diagnostic pop diff --git a/CleverTapSDK/CleverTap.m b/CleverTapSDK/CleverTap.m index cf86d25f..ac669dc5 100644 --- a/CleverTapSDK/CleverTap.m +++ b/CleverTapSDK/CleverTap.m @@ -57,6 +57,7 @@ #import "CTInAppEvaluationManager.h" #import "CTInAppTriggerManager.h" #import "CTInAppImagePrefetchManager.h" +#import "CTCustomTemplatesManager.h" #endif #if !CLEVERTAP_NO_INBOX_SUPPORT @@ -244,6 +245,8 @@ @interface CleverTap () { @property (nonatomic, strong, readwrite) CTImpressionManager *impressionManager; @property (nonatomic, strong, readwrite) CTInAppImagePrefetchManager *imagePrefetchManager; @property (nonatomic, strong, readwrite) CTInAppStore * _Nullable inAppStore; + +@property (nonatomic, strong) CTCustomTemplatesManager *customTemplatesManager; #endif @property (atomic, strong) NSString *processingLoginUserIdentifier; @@ -529,6 +532,9 @@ - (void)initializeInAppSupport { CTInAppEvaluationManager *evaluationManager = [[CTInAppEvaluationManager alloc] initWithAccountId:self.config.accountId deviceId:self.deviceInfo.deviceId delegateManager:self.delegateManager impressionManager:impressionManager inAppDisplayManager:displayManager inAppStore:inAppStore inAppTriggerManager:triggerManager]; + CTCustomTemplatesManager *templatesManager = [[CTCustomTemplatesManager alloc] initWithConfig:self.config]; + + self.customTemplatesManager = templatesManager; self.inAppFCManager = inAppFCManager; self.impressionManager = impressionManager; self.inAppEvaluationManager = evaluationManager; @@ -731,6 +737,21 @@ - (void)doHandshakeAsyncWithCompletion:(void (^ _Nullable )(void))taskBlock { }]; } +- (void)runSerialAsyncEnsureHandshake:(void(^)(void))block { + if ([self needHandshake]) { + [self.dispatchQueueManager runSerialAsync:^{ + [self doHandshakeAsyncWithCompletion:^{ + block(); + }]; + }]; + } + else { + [self.dispatchQueueManager runSerialAsync:^{ + block(); + }]; + } +} + - (BOOL)updateStateFromResponseHeadersShouldRedirectForNotif:(NSDictionary *)headers { CleverTapLogInternal(self.config.logLevel, @"%@: processing response with headers:%@", self, headers); BOOL shouldRedirect = NO; @@ -4272,88 +4293,94 @@ + (BOOL)isValidCleverTapId:(NSString *_Nullable)cleverTapID { return [CTValidator isValidCleverTapId:cleverTapID]; } -#pragma mark - Product Experiences +#pragma mark - Sync PE and Custom Templates -- (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { - [[self variables] onVariablesChanged:block]; +- (void)syncVariables { + [self syncVariables:NO]; } -- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { - [[self variables] onceVariablesChanged:block]; +- (void)syncVariables:(BOOL)isProduction { + [self syncWithBlock:^{ + NSDictionary *meta = [self batchHeaderForQueue:CTQueueTypeUndefined]; + NSDictionary *varsPayload = [[self variables] varsPayload]; + CTRequest *ctRequest = [CTRequestFactory syncVarsRequestWithConfig:self.config params:@[meta, varsPayload] domain:self.domainFactory.redirectDomain]; + [self syncRequest:ctRequest logMessage:@"Vars sync"]; + } methodName:NSStringFromSelector(_cmd) isProduction:isProduction]; } -- (void)syncVariables { - [self syncVariables:NO]; +- (void)syncCustomTemplates { + [self syncCustomTemplates:NO]; } -- (void)syncVariablesEnsureHandshake { - if ([self needHandshake]) { - [self.dispatchQueueManager runSerialAsync:^{ - [self doHandshakeAsyncWithCompletion:^{ - [self _syncVars]; - }]; - }]; - } - else { - [self.dispatchQueueManager runSerialAsync:^{ - [self _syncVars]; - }]; - } +- (void)syncCustomTemplates:(BOOL)isProduction { + [self syncWithBlock:^{ + NSDictionary *meta = [self batchHeaderForQueue:CTQueueTypeUndefined]; + NSDictionary *syncPayload = [[self customTemplatesManager] syncPayload]; + CTRequest *ctRequest = [CTRequestFactory syncTemplatesRequestWithConfig:self.config params:@[meta, syncPayload] domain:self.domainFactory.redirectDomain]; + [self syncRequest:ctRequest logMessage:@"Define Custom Templates"]; + } methodName:NSStringFromSelector(_cmd) isProduction:isProduction]; } - -- (void)syncVariables:(BOOL)isProduction { + +- (void)syncWithBlock:(void(^)(void))syncBlock methodName:(NSString *)methodName isProduction:(BOOL)isProduction { if (isProduction) { #if DEBUG - CleverTapLogInfo(_config.logLevel, @"%@: Calling syncVariables: with isProduction:YES from Debug configuration/build. Use syncVariables in this case", self); + CleverTapLogInfo(_config.logLevel, @"%@: Calling %@: with isProduction:YES from Debug configuration/build. Use syncVariables in this case", self, methodName); #else - CleverTapLogInfo(_config.logLevel, @"%@: Calling syncVariables: with isProduction:YES from Release configuration/build. Do not release this build and use with caution", self); + CleverTapLogInfo(_config.logLevel, @"%@: Calling %@: with isProduction:YES from Release configuration/build. Do not release this build and use with caution", self, methodName); #endif - [self syncVariablesEnsureHandshake]; + [self runSerialAsyncEnsureHandshake:syncBlock]; } else { #if DEBUG - [self syncVariablesEnsureHandshake]; + [self runSerialAsyncEnsureHandshake:syncBlock]; #else - CleverTapLogInfo(_config.logLevel, @"%@: syncVariables can only be called from Debug configurations/builds", self); + CleverTapLogInfo(_config.logLevel, @"%@: %@ can only be called from Debug configurations/builds", self, methodName); #endif } } -- (void)_syncVars { - NSDictionary *meta = [self batchHeaderForQueue:CTQueueTypeUndefined]; - NSDictionary *varsPayload = [[self variables] varsPayload]; - NSArray *payload = @[meta,varsPayload]; - +- (void)syncRequest:(CTRequest *)request logMessage:(NSString *)logMessage { dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - - 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]]) { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - if (httpResponse.statusCode == 200) { - CleverTapLogDebug(self->_config.logLevel, @"%@: Vars synced successfully", self); - } - else if (httpResponse.statusCode == 401) { - 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 - 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 + [request onResponse:^(NSData * _Nullable data, NSURLResponse * _Nullable response) { + [self handleSyncOnResponse:data response:response logMessage:logMessage]; dispatch_semaphore_signal(semaphore); }]; - [ctRequest onError:^(NSError * _Nullable error) { - CleverTapLogDebug(self->_config.logLevel, @"%@: error syncing vars: %@", self, error.debugDescription); + [request onError:^(NSError * _Nullable error) { + CleverTapLogDebug(self->_config.logLevel, @"%@: Error %@: %@", self, logMessage, error.debugDescription); dispatch_semaphore_signal(semaphore); }]; - [self.requestSender send:ctRequest]; + [self.requestSender send:request]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); } +- (void)handleSyncOnResponse:(NSData * _Nullable)data response:(NSURLResponse * _Nullable)response + logMessage:(NSString *)logMessage { + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + if (httpResponse.statusCode == 200) { + CleverTapLogDebug(self->_config.logLevel, @"%@: %@ successful.", self, logMessage); + } + else if (httpResponse.statusCode == 401) { + 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 + id jsonResp = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; + if (jsonResp[@"error"]) { + CleverTapLogDebug(self->_config.logLevel, @"%@: %@ error: %@", self, logMessage, jsonResp[@"error"]); + } + CT_END_TRY +} + +#pragma mark - Product Experiences + +- (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { + [[self variables] onVariablesChanged:block]; +} + +- (void)onceVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull )block { + [[self variables] onceVariablesChanged:block]; +} + - (void)fetchVariables:(CleverTapFetchVariablesBlock)block { [[self variables] setFetchVariablesBlock:block]; [self queueEvent:@{CLTAP_EVENT_NAME: CLTAP_WZRK_FETCH_EVENT, CLTAP_EVENT_DATA: @{@"t": @4}} withType:CleverTapEventTypeFetch];