Skip to content

Commit

Permalink
Add feature flag and update logic to clear cache on modify and logout
Browse files Browse the repository at this point in the history
  • Loading branch information
einsteinx2 committed Jan 9, 2024
1 parent 6141eeb commit 3eacfb9
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 36 deletions.
64 changes: 39 additions & 25 deletions mParticle-Apple-SDK/Network/MPNetworkCommunication.m
Original file line number Diff line number Diff line change
Expand Up @@ -798,27 +798,38 @@ - (void)identityApiRequestWithURL:(NSURL*)url identityRequest:(MPIdentityHTTPBas
NSString *responseString = nil;
NSInteger responseCode = 0;

MPIdentityCachedResponse *cachedResponse = [MPIdentityCaching getCachedIdentityResponseForEndpoint:endpointType identityRequest:identityRequest];
if (cachedResponse) {
@try {
NSError *serializationError = nil;
responseString = [[NSString alloc] initWithData:cachedResponse.bodyData encoding:NSUTF8StringEncoding];
responseDictionary = [NSJSONSerialization JSONObjectWithData:cachedResponse.bodyData options:0 error:&serializationError];

if (serializationError) {
BOOL enableIdentityCaching = MParticle.sharedInstance.stateMachine.enableIdentityCaching;
BOOL usedCachedResponse = NO;

// Try to use the cache if enabled
if (enableIdentityCaching) {
MPIdentityCachedResponse *cachedResponse = [MPIdentityCaching getCachedIdentityResponseForEndpoint:endpointType identityRequest:identityRequest];
if (cachedResponse) {
@try {
NSError *serializationError = nil;
responseString = [[NSString alloc] initWithData:cachedResponse.bodyData encoding:NSUTF8StringEncoding];
responseDictionary = [NSJSONSerialization JSONObjectWithData:cachedResponse.bodyData options:0 error:&serializationError];

if (serializationError) {
responseDictionary = nil;
success = NO;
usedCachedResponse = NO;
MPILogError(@"Identity response serialization error: %@", [serializationError localizedDescription]);
} else {
responseCode = cachedResponse.statusCode;
success = YES;
usedCachedResponse = YES;
}
} @catch (NSException *exception) {
responseDictionary = nil;
success = NO;
MPILogError(@"Identity response serialization error: %@", [serializationError localizedDescription]);
} else {
responseCode = cachedResponse.statusCode;
success = YES;
usedCachedResponse = NO;
MPILogError(@"Identity response serialization error: %@", [exception reason]);
}
} @catch (NSException *exception) {
responseDictionary = nil;
success = NO;
MPILogError(@"Identity response serialization error: %@", [exception reason]);
}
} else {
}

if (!usedCachedResponse) {
__block UIBackgroundTaskIdentifier backgroundTaskIdentifier = UIBackgroundTaskInvalid;

if (![MPStateMachine isAppExtension]) {
Expand Down Expand Up @@ -864,14 +875,17 @@ - (void)identityApiRequestWithURL:(NSURL*)url identityRequest:(MPIdentityHTTPBas
responseDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&serializationError];

if (responseDictionary && !serializationError) {
// Cache response if it contains a the custom max age header
NSInteger maxAgeSeconds = [response.httpResponse.allHeaderFields[kMPIdentityCachingMaxAgeHeader] integerValue];
if (maxAgeSeconds > 0) {
NSDate *expires = [[NSDate date] dateByAddingTimeInterval:(NSTimeInterval)maxAgeSeconds];
MPIdentityCachedResponse *cachedResponse = [[MPIdentityCachedResponse alloc] initWithBodyData:responseData
statusCode:responseCode
expires:expires];
[MPIdentityCaching cacheIdentityResponse:cachedResponse endpoint:endpointType identityRequest:identityRequest];
// Cache response if it contains the custom max age header and the feature is enabled
if (enableIdentityCaching) {
NSInteger maxAgeSeconds = [response.httpResponse.allHeaderFields[kMPIdentityCachingMaxAgeHeader] integerValue];
MPILogVerbose(@"Identity Caching - max age header value (in seconds): %li", (long)maxAgeSeconds);
if (maxAgeSeconds > 0) {
NSDate *expires = [[NSDate date] dateByAddingTimeInterval:(NSTimeInterval)maxAgeSeconds];
MPIdentityCachedResponse *cachedResponse = [[MPIdentityCachedResponse alloc] initWithBodyData:responseData
statusCode:responseCode
expires:expires];
[MPIdentityCaching cacheIdentityResponse:cachedResponse endpoint:endpointType identityRequest:identityRequest];
}
}
} else {
responseDictionary = nil;
Expand Down
47 changes: 36 additions & 11 deletions mParticle-Apple-SDK/Persistence/MPIdentityCaching.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#import "MPIdentityCaching.h"
#import "MPIUserDefaults.h"
#import "MPILogger.h"
#import "mParticle.h"
#import <CommonCrypto/CommonCrypto.h>

// User defaults key
Expand Down Expand Up @@ -89,20 +91,31 @@ + (void)cacheIdentityResponse:(nonnull MPIdentityCachedResponse *)cachedResponse
}

+ (nullable MPIdentityCachedResponse *)getCachedIdentityResponseForEndpoint:(MPEndpoint)endpoint identityRequest:(nonnull MPIdentityHTTPBaseRequest *)identityRequest {
NSDictionary *identities = [self identitiesFromIdentityRequest:identityRequest];
return [self getCachedIdentityResponseForEndpoint:endpoint identities:identities];
if (endpoint == MPEndpointIdentityIdentify || endpoint == MPEndpointIdentityLogin) {
// Cache identify and login calls
NSDictionary *identities = [self identitiesFromIdentityRequest:identityRequest];
return [self getCachedIdentityResponseForEndpoint:endpoint identities:identities];
} else if (endpoint == MPEndpointIdentityModify || endpoint == MPEndpointIdentityLogout) {
// Clear cache on modify and logout calls
[self clearAllCache];
}
return nil;
}

+ (void)cacheIdentityResponse:(nonnull MPIdentityCachedResponse *)cachedResponse endpoint:(MPEndpoint)endpoint identities:(nonnull NSDictionary *)identities {
NSString *key = [self keyWithEndpoint:endpoint identities:identities];
if (key.length == 0) {
return;
// Only cache identify and login calls
if (endpoint == MPEndpointIdentityIdentify || endpoint == MPEndpointIdentityLogin) {
NSString *key = [self keyWithEndpoint:endpoint identities:identities];
if (key.length == 0) {
return;
}

NSDictionary *cache = [self getCache] ?: @{};
NSMutableDictionary *mutableCache = [cache mutableCopy];
[mutableCache setObject:cachedResponse.dictionaryRepresentation forKey:key];
[self setCache:mutableCache];
MPILogVerbose(@"Identity Caching - Cached response for endpoint %ld, key: %@, expires: %@, bodyData.length: %lu", (long)endpoint, key, cachedResponse.expires, (unsigned long)cachedResponse.bodyData.length);
}

NSDictionary *cache = [self getCache] ?: @{};
NSMutableDictionary *mutableCache = [cache mutableCopy];
[mutableCache setObject:cachedResponse.dictionaryRepresentation forKey:key];
[self setCache:mutableCache];
}

+ (nullable MPIdentityCachedResponse *)getCachedIdentityResponseForEndpoint:(MPEndpoint)endpoint identities:(nonnull NSDictionary *)identities {
Expand All @@ -118,31 +131,43 @@ + (nullable MPIdentityCachedResponse *)getCachedIdentityResponseForEndpoint:(MPE
}

MPIdentityCachedResponse *cachedResponse = [[MPIdentityCachedResponse alloc] initWithDictionary:dictionary];
if (!cachedResponse || [[NSDate date] timeIntervalSinceDate:cachedResponse.expires] > 0) {
NSDate *now = [NSDate date];
if (!cachedResponse) {
MPILogVerbose(@"Identity Caching - No cached response found for key: %@", key);
}
if (!cachedResponse || [now timeIntervalSinceDate:cachedResponse.expires] > 0) {
MPILogVerbose(@"Identity Caching - Expired cached response found for key: %@, expired: %@, seconds since expired: %.1f", key, cachedResponse.expires, [now timeIntervalSinceDate:cachedResponse.expires]);
return nil;
}
MPILogVerbose(@"Identity Caching - Valid cached response found for key: %@, expires: %@, seconds left: %.1f", key, cachedResponse.expires, [cachedResponse.expires timeIntervalSinceDate:now]);
return cachedResponse;
}

+ (void)clearAllCache {
[self setCache:nil];
MPILogVerbose(@"Identity Caching - Removed all cached responses");
}

+ (void)clearExpiredCache {
NSDictionary *cache = [self getCache];
NSMutableDictionary *mutableCache = [cache mutableCopy];
__block int numberRemoved = 0;
[cache enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDate *expires = [obj objectForKey:kMPIdentityCachingExpires];
if (!expires || [expires timeIntervalSinceDate:[NSDate date]] < 0) {
[mutableCache removeObjectForKey:key];
numberRemoved++;
}
} else {
// Invalid cache data, remove from cache
[mutableCache removeObjectForKey:key];
numberRemoved++;
}
}];

MPILogVerbose(@"Identity Caching - Removed %d expired cached responses", numberRemoved);

if ([cache count] != [mutableCache count]) {
[self setCache:mutableCache];
}
Expand Down
1 change: 1 addition & 0 deletions mParticle-Apple-SDK/Utils/MPStateMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
@property (nonatomic) BOOL allowASR;
@property (nonatomic, nullable) MPDataPlanOptions *dataPlanOptions;
@property (nonatomic) BOOL enableDirectRouting;
@property (nonatomic) BOOL enableIdentityCaching;

+ (MPEnvironment)environment;
+ (void)setEnvironment:(MPEnvironment)environment;
Expand Down

0 comments on commit 3eacfb9

Please sign in to comment.