Skip to content

Commit

Permalink
[MC-1467] Custom templates Sync (Custom Templates Part 2) (#329)
Browse files Browse the repository at this point in the history
* Sync Templates
  • Loading branch information
nzagorchev authored Jun 13, 2024
1 parent 17e0ff9 commit 27ea20a
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 66 deletions.
2 changes: 0 additions & 2 deletions CleverTapSDK/CTConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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"
Expand Down
2 changes: 0 additions & 2 deletions CleverTapSDK/CTConstants.m
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
6 changes: 4 additions & 2 deletions CleverTapSDK/CTRequestFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Foundation/Foundation.h>
Expand All @@ -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


20 changes: 15 additions & 5 deletions CleverTapSDK/CTRequestFactory.m
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
20 changes: 20 additions & 0 deletions CleverTapSDK/CleverTap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
137 changes: 82 additions & 55 deletions CleverTapSDK/CleverTap.m
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#import "CTInAppEvaluationManager.h"
#import "CTInAppTriggerManager.h"
#import "CTInAppImagePrefetchManager.h"
#import "CTCustomTemplatesManager.h"
#endif

#if !CLEVERTAP_NO_INBOX_SUPPORT
Expand Down Expand Up @@ -244,6 +245,8 @@ @interface CleverTap () <UIApplicationDelegate> {
@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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand Down

0 comments on commit 27ea20a

Please sign in to comment.