Skip to content

Commit bd64ee9

Browse files
Merge pull request #500 from CleverTap/SDK-5392-7.4.1
SDK-5392: Variants and bug fixes
2 parents dabb3f8 + 872df96 commit bd64ee9

File tree

9 files changed

+145
-31
lines changed

9 files changed

+145
-31
lines changed

CleverTapSDK/CTConstants.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ extern NSString *const kSessionId;
8686
#define CLTAP_PRODUCT_CONFIG_JSON_RESPONSE_KEY @"pc_notifs"
8787
#define CLTAP_GEOFENCES_JSON_RESPONSE_KEY @"geofences"
8888
#define CLTAP_PE_VARS_RESPONSE_KEY @"vars"
89+
#define CLTAP_PE_VARIANTS_RESPONSE_KEY @"abVariantInfo"
8990
#define CLTAP_DISCARDED_EVENT_JSON_KEY @"d_e"
9091
#define CLTAP_INAPP_CLOSE_IV_WIDTH 40
9192
#define CLTAP_NOTIFICATION_ID_TAG @"wzrk_id"
@@ -138,6 +139,7 @@ extern NSString *CT_KIND_BOOLEAN;
138139
extern NSString *CT_KIND_DICTIONARY;
139140
extern NSString *CT_KIND_FILE;
140141
extern NSString *CLEVERTAP_DEFAULTS_VARIABLES_KEY;
142+
extern NSString *CLEVERTAP_DEFAULTS_VARIANTS_KEY;
141143
extern NSString *CLEVERTAP_DEFAULTS_VARS_JSON_KEY;
142144

143145
extern NSString *CT_PE_VARS_PAYLOAD_TYPE;

CleverTapSDK/CTConstants.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
NSString *CT_KIND_FILE = @"file";
2626
NSString *CT_KIND_DICTIONARY = @"group";
2727
NSString *CLEVERTAP_DEFAULTS_VARIABLES_KEY = @"__clevertap_variables";
28+
NSString *CLEVERTAP_DEFAULTS_VARIANTS_KEY = @"__clevertap_variants";
2829
NSString *CLEVERTAP_DEFAULTS_VARS_JSON_KEY = @"__clevertap_variables_json";
2930

3031
NSString *CT_PE_VARS_PAYLOAD_TYPE = @"varsPayload";

CleverTapSDK/CleverTap.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,6 +1531,19 @@ extern NSString * _Nonnull const CleverTapProfileDidInitializeNotification;
15311531
*/
15321532
- (id _Nullable)getVariableValue:(NSString * _Nonnull)name;
15331533

1534+
/**
1535+
* Returns experiment variant information for the current user.
1536+
*
1537+
* Each dictionary contains details about an assigned A/B test variant.
1538+
* Common keys include:
1539+
* - "name": Variant name
1540+
* - "abTestId": Experiment identifier
1541+
* - "abTestName": Experiment name
1542+
*
1543+
* @return Array of variant dictionaries with string keys. Returns empty array if no variants assigned.
1544+
*/
1545+
- (NSArray<NSDictionary<NSString *, id> *> * _Nonnull)variants;
1546+
15341547
/*!
15351548
@method
15361549

CleverTapSDK/CleverTap.m

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,16 +1128,24 @@ - (void)_appEnteredForegroundWithLaunchingOptions:(NSDictionary *)launchOptions
11281128

11291129
- (void)_appEnteredForeground {
11301130
if ([CTUIUtils runningInsideAppExtension]) return;
1131+
1132+
// Check application state
1133+
UIApplication *application = [CTUIUtils getSharedApplication];
1134+
BOOL isActuallyInForeground = (application.applicationState == UIApplicationStateActive);
11311135
[self.sessionManager updateSessionStateOnLaunch];
1132-
if (!self.isAppForeground) {
1136+
1137+
// Only record app launched if app is transitioning to foreground and app is active
1138+
if (!self.isAppForeground && isActuallyInForeground) {
11331139
[self recordAppLaunched:@"appEnteredForeground"];
11341140
[self scheduleQueueFlush];
11351141
CleverTapLogInternal(self.config.logLevel, @"%@: app is in foreground", self);
11361142
}
1137-
self.isAppForeground = YES;
1143+
1144+
// Set flag based on actual state
1145+
self.isAppForeground = isActuallyInForeground;
11381146

11391147
#if !CLEVERTAP_NO_INAPP_SUPPORT
1140-
if (!_config.analyticsOnly && ![CTUIUtils runningInsideAppExtension]) {
1148+
if (isActuallyInForeground && !_config.analyticsOnly && ![CTUIUtils runningInsideAppExtension]) {
11411149
[self.inAppFCManager checkUpdateDailyLimits];
11421150
}
11431151
#endif
@@ -1189,6 +1197,7 @@ - (void)recordAppLaunched:(NSString *)caller {
11891197

11901198
// Load Vars from cache before App Launched
11911199
[self.variables.varCache loadDiffs];
1200+
[self.variables.varCache loadVariants];
11921201

11931202
self.sessionManager.appLaunchProcessed = YES;
11941203

@@ -2553,6 +2562,17 @@ - (void)parseResponse:(NSData *)responseData responseEncrypted:(BOOL)responseEnc
25532562
}
25542563
}
25552564

2565+
// Handle Variants
2566+
NSArray *variantsResponse = jsonResp[CLTAP_PE_VARIANTS_RESPONSE_KEY];
2567+
if (variantsResponse) {
2568+
if (!self.isUserSwitching) {
2569+
[[self variables] handleVariantsResponse:variantsResponse];
2570+
}
2571+
else {
2572+
CleverTapLogDebug(self.config.logLevel, @"%@: Variants will not be handled due to user switch", self);
2573+
}
2574+
}
2575+
25562576
// Handle events/profiles sync data
25572577
@try {
25582578
NSDictionary *evpr = jsonResp[@"evpr"];
@@ -4645,6 +4665,17 @@ - (id _Nullable)getVariableValue:(NSString * _Nonnull)name {
46454665
return [[self.variables varCache] getMergedValue:name];
46464666
}
46474667

4668+
- (NSArray<NSDictionary<NSString *, id> *> *)variants
4669+
{
4670+
CT_TRY
4671+
NSArray *variants = [self.variables.varCache variants];
4672+
if (variants) {
4673+
return [variants copy];
4674+
}
4675+
CT_END_TRY
4676+
return [NSArray array];
4677+
}
4678+
46484679
- (void)onVariablesChangedAndNoDownloadsPending:(CleverTapVariablesChangedBlock _Nonnull )block {
46494680
[[self variables] onVariablesChangedAndNoDownloadsPending:block];
46504681
}

CleverTapSDK/InApps/resources/image_interstitial.html

Lines changed: 1 addition & 28 deletions
Large diffs are not rendered by default.

CleverTapSDK/ProductExperiences/CTVarCache.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,13 @@ NS_SWIFT_NAME(VarCache)
2727
@property (assign, nonatomic) BOOL hasVarsRequestCompleted;
2828
@property (assign, nonatomic) BOOL hasPendingDownloads;
2929
@property (nonatomic, weak) id<CTFileVarDelegate> delegate;
30+
@property (nonatomic, strong) NSArray<NSDictionary<NSString *, id> *> *variants;
3031

3132
- (nullable NSDictionary<NSString *, id> *)diffs;
3233
- (void)loadDiffs;
34+
- (void)loadVariants;
3335
- (void)applyVariableDiffs:(nullable NSDictionary<NSString *, id> *)diffs_;
36+
- (void)handleVariantsData:(NSArray<NSDictionary<NSString *, id> *> *)variantsData;
3437

3538
- (void)registerVariable:(CTVar *)var;
3639
- (nullable CTVar *)getVariable:(NSString *)name;

CleverTapSDK/ProductExperiences/CTVarCache.m

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,63 @@ - (void)saveDiffs {
242242
}
243243
}
244244

245+
- (void)loadVariants {
246+
@try {
247+
NSString *fileName = [self dataArchiveVariantsFileName];
248+
NSString *filePath = [CTPreferences filePathfromFileName:fileName];
249+
NSData *variantsData = [NSData dataWithContentsOfFile:filePath];
250+
251+
if (!variantsData) {
252+
@synchronized (self) {
253+
self.variants = @[];
254+
}
255+
CleverTapLogDebug(self.config.logLevel, @"%@: No cached variants found", self);
256+
return;
257+
}
258+
259+
NSKeyedUnarchiver *unarchiver;
260+
if (@available(iOS 12.0, tvOS 11.0, *)) {
261+
NSError *error = nil;
262+
unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:variantsData error:&error];
263+
if (error != nil) {
264+
CleverTapLogDebug(self.config.logLevel, @"%@: Error loading variants: %@", self, error.localizedDescription);
265+
@synchronized (self) {
266+
self.variants = @[];
267+
}
268+
return;
269+
}
270+
unarchiver.requiresSecureCoding = NO;
271+
} else {
272+
#pragma clang diagnostic push
273+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
274+
unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:variantsData];
275+
#pragma clang diagnostic pop
276+
}
277+
278+
NSArray *loadedVariants = (NSArray *)[unarchiver decodeObjectForKey:CLEVERTAP_DEFAULTS_VARIANTS_KEY];
279+
280+
@synchronized (self) {
281+
self.variants = loadedVariants ?: @[];
282+
}
283+
284+
CleverTapLogDebug(self.config.logLevel, @"%@: Loaded %lu variants from cache", self, (unsigned long)[self.variants count]);
285+
286+
} @catch (NSException *exception) {
287+
CleverTapLogDebug(self.config.logLevel, @"%@: Exception loading variants: %@", self, exception.debugDescription);
288+
@synchronized (self) {
289+
self.variants = @[];
290+
}
291+
}
292+
}
293+
245294
- (NSString*)dataArchiveFileName {
246295
return [NSString stringWithFormat:@"clevertap-%@-%@-pe-vars.plist", _config.accountId, _deviceInfo.deviceId];
247296
}
248297

298+
- (NSString*)dataArchiveVariantsFileName {
299+
return [NSString stringWithFormat:@"clevertap-%@-%@-pe-variants.plist", _config.accountId, _deviceInfo.deviceId];
300+
}
301+
249302
- (void)applyVariableDiffs:(NSDictionary *)diffs_ {
250303
CleverTapLogDebug(self.config.logLevel, @"%@: Applying Variables: %@", self, diffs_);
251304
@synchronized (self.vars) {
@@ -291,9 +344,39 @@ - (void)applyVariableDiffs:(NSDictionary *)diffs_ {
291344
}
292345
}
293346

347+
- (void)handleVariantsData:(NSArray<NSDictionary<NSString *, id> *> *)variantsData {
348+
@synchronized (self) {
349+
self.variants = variantsData;
350+
}
351+
352+
NSMutableData *diffsData = [[NSMutableData alloc] init];
353+
#pragma clang diagnostic push
354+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
355+
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:diffsData];
356+
#pragma clang diagnostic pop
357+
[archiver encodeObject:variantsData forKey:CLEVERTAP_DEFAULTS_VARIANTS_KEY];
358+
[archiver finishEncoding];
359+
360+
NSError *writeError = nil;
361+
NSString *variantsFileName = [self dataArchiveVariantsFileName];
362+
NSString *filePath = [CTPreferences filePathfromFileName:variantsFileName];
363+
NSDataWritingOptions fileProtectionOption = _config.enableFileProtection ?
364+
NSDataWritingFileProtectionComplete : NSDataWritingAtomic;
365+
[diffsData writeToFile:filePath options:fileProtectionOption error:&writeError];
366+
if (writeError) {
367+
CleverTapLogStaticInternal(@"%@ failed to write data at %@: %@", self, filePath, writeError);
368+
}
369+
}
370+
294371
- (void)clearUserContent {
295372
// Disable callbacks and wait until fetch is finished
296373
[self setHasVarsRequestCompleted:NO];
374+
375+
// Clear variants for the old user
376+
@synchronized (self) {
377+
self.variants = @[];
378+
}
379+
297380
// Clear Var state to allow callback invocation when server values are downloaded
298381
[self.vars enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
299382
CTVar *var = (CTVar *)obj;

CleverTapSDK/ProductExperiences/CTVariables.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ NS_ASSUME_NONNULL_BEGIN
2929
NS_SWIFT_NAME(define(name:value:kind:));
3030

3131
- (void)handleVariablesResponse:(NSDictionary *)varsResponse;
32+
- (void)handleVariantsResponse:(NSArray<NSDictionary<NSString *, id> *> *)variantsResponse;
3233
- (void)handleVariablesError;
3334
- (void)triggerFetchVariables:(BOOL)success;
3435
- (void)onVariablesChanged:(CleverTapVariablesChangedBlock _Nonnull)block;

CleverTapSDK/ProductExperiences/CTVariables.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ - (void)handleVariablesResponse:(NSDictionary *)varsResponse {
7373
}
7474
}
7575

76+
- (void)handleVariantsResponse:(NSArray<NSDictionary<NSString *, id> *> *)variantsResponse {
77+
if (variantsResponse && variantsResponse.count >= 0) {
78+
CleverTapLogDebug(self.config.logLevel, @"%@: Handle Variants Response with: %@", self, variantsResponse);
79+
[[self varCache] handleVariantsData:variantsResponse];
80+
}
81+
}
82+
7683
- (void)handleVariablesError {
7784
CleverTapLogDebug(self.config.logLevel, @"%@: Handle Variables Error", self);
7885
if (![[self varCache] hasVarsRequestCompleted]) {

0 commit comments

Comments
 (0)