From b8aed9692e82ad1dbbecfae0ad5fc353a9eb2220 Mon Sep 17 00:00:00 2001 From: Prateek Srivastava Date: Mon, 13 Jun 2016 09:40:39 +0530 Subject: [PATCH] Add anonymous ID retrieval method --- Example/Podfile.lock | 4 +- Pod/Classes/Internal/SEGAnalyticsUtils.h | 1 + Pod/Classes/Internal/SEGAnalyticsUtils.m | 8 +++ Pod/Classes/Internal/SEGSegmentIntegration.h | 1 - Pod/Classes/Internal/SEGSegmentIntegration.m | 49 +----------------- Pod/Classes/SEGAnalytics.h | 3 ++ Pod/Classes/SEGAnalytics.m | 53 +++++++++++++++++++- 7 files changed, 68 insertions(+), 51 deletions(-) diff --git a/Example/Podfile.lock b/Example/Podfile.lock index cd7baf536..976b1b6cb 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - Analytics (3.2.2) + - Analytics (3.2.4) - Expecta (1.0.3) - Specta (1.0.4) @@ -13,7 +13,7 @@ EXTERNAL SOURCES: :path: ../ SPEC CHECKSUMS: - Analytics: d7a22ef3048638755e74337f4c31eba35be680d3 + Analytics: d801f32615853ca3108216423dbaa829c74f5927 Expecta: 9d1bff6c8b0eeee73a166a2ee898892478927a15 Specta: 69bb134672aae190a1379ff91df07dad8dd1f869 diff --git a/Pod/Classes/Internal/SEGAnalyticsUtils.h b/Pod/Classes/Internal/SEGAnalyticsUtils.h index 59c4a2710..cab49495f 100644 --- a/Pod/Classes/Internal/SEGAnalyticsUtils.h +++ b/Pod/Classes/Internal/SEGAnalyticsUtils.h @@ -4,6 +4,7 @@ #import NSURL *SEGAnalyticsURLForFilename(NSString *filename); +NSString *GenerateUUIDString(); // Date Utils NSString *iso8601FormattedString(NSDate *date); diff --git a/Pod/Classes/Internal/SEGAnalyticsUtils.m b/Pod/Classes/Internal/SEGAnalyticsUtils.m index 1755f0345..c055d9610 100644 --- a/Pod/Classes/Internal/SEGAnalyticsUtils.m +++ b/Pod/Classes/Internal/SEGAnalyticsUtils.m @@ -24,6 +24,14 @@ return [[NSURL alloc] initFileURLWithPath:[supportPath stringByAppendingPathComponent:filename]]; } +NSString *GenerateUUIDString() +{ + CFUUIDRef theUUID = CFUUIDCreate(NULL); + NSString *UUIDString = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, theUUID); + CFRelease(theUUID); + return UUIDString; +} + // Date Utils NSString *iso8601FormattedString(NSDate *date) { diff --git a/Pod/Classes/Internal/SEGSegmentIntegration.h b/Pod/Classes/Internal/SEGSegmentIntegration.h index e0d80000c..5c9fda3c9 100644 --- a/Pod/Classes/Internal/SEGSegmentIntegration.h +++ b/Pod/Classes/Internal/SEGSegmentIntegration.h @@ -11,7 +11,6 @@ extern NSString *const SEGSegmentRequestDidFailNotification; @interface SEGSegmentIntegration : NSObject -@property (nonatomic, copy) NSString *anonymousId; @property (nonatomic, copy) NSString *userId; @property (nonatomic, strong) NSURL *apiURL; diff --git a/Pod/Classes/Internal/SEGSegmentIntegration.m b/Pod/Classes/Internal/SEGSegmentIntegration.m index 4bce1f873..fa58361ad 100644 --- a/Pod/Classes/Internal/SEGSegmentIntegration.m +++ b/Pod/Classes/Internal/SEGSegmentIntegration.m @@ -24,18 +24,9 @@ NSString *const SEGADClientClass = @"ADClient"; NSString *const SEGUserIdKey = @"SEGUserId"; -NSString *const SEGAnonymousIdKey = @"SEGAnonymousId"; NSString *const SEGQueueKey = @"SEGQueue"; NSString *const SEGTraitsKey = @"SEGTraits"; -static NSString *GenerateUUIDString() -{ - CFUUIDRef theUUID = CFUUIDCreate(NULL); - NSString *UUIDString = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, theUUID); - CFRelease(theUUID); - return UUIDString; -} - static NSString *GetDeviceModel() { size_t size; @@ -84,7 +75,6 @@ - (id)initWithAnalytics:(SEGAnalytics *)analytics if (self = [super init]) { self.configuration = [analytics configuration]; self.apiURL = [NSURL URLWithString:@"https://api.segment.io/v1/import"]; - self.anonymousId = [self getAnonymousId:NO]; self.userId = [self getUserId]; self.bluetooth = [[SEGBluetooth alloc] init]; self.reachability = [SEGReachability reachabilityWithHostname:@"google.com"]; @@ -284,15 +274,6 @@ - (void)saveUserId:(NSString *)userId }]; } -- (void)saveAnonymousId:(NSString *)anonymousId -{ - [self dispatchBackground:^{ - self.anonymousId = anonymousId; - [[NSUserDefaults standardUserDefaults] setValue:anonymousId forKey:SEGAnonymousIdKey]; - [self.anonymousId writeToURL:self.anonymousIDURL atomically:YES encoding:NSUTF8StringEncoding error:NULL]; - }]; -} - - (void)addTraits:(NSDictionary *)traits { [self dispatchBackground:^{ @@ -308,9 +289,6 @@ - (void)identify:(SEGIdentifyPayload *)payload [self dispatchBackground:^{ [self saveUserId:payload.userId]; [self addTraits:payload.traits]; - if (payload.anonymousId) { - [self saveAnonymousId:payload.anonymousId]; - } }]; NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; @@ -351,7 +329,7 @@ - (void)alias:(SEGAliasPayload *)payload { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary setValue:payload.theNewId forKey:@"userId"]; - [dictionary setValue:self.userId ?: self.anonymousId forKey:@"previousId"]; + [dictionary setValue:self.userId ?: [self.analytics getAnonymousId] forKey:@"previousId"]; [self enqueueAction:@"alias" dictionary:dictionary context:payload.context integrations:payload.integrations]; } @@ -402,7 +380,7 @@ - (void)enqueueAction:(NSString *)action dictionary:(NSMutableDictionary *)paylo if (![action isEqualToString:@"alias"]) { [payload setValue:self.userId forKey:@"userId"]; } - [payload setValue:self.anonymousId forKey:@"anonymousId"]; + [payload setValue:[self.analytics getAnonymousId] forKey:@"anonymousId"]; [payload setValue:[self integrationsDictionary:integrations] forKey:@"integrations"]; @@ -506,14 +484,12 @@ - (void)reset { [self dispatchBackgroundAndWait:^{ [[NSUserDefaults standardUserDefaults] setValue:nil forKey:SEGUserIdKey]; - [[NSUserDefaults standardUserDefaults] setValue:nil forKey:SEGAnonymousIdKey]; [[NSFileManager defaultManager] removeItemAtURL:self.userIDURL error:NULL]; [[NSFileManager defaultManager] removeItemAtURL:self.traitsURL error:NULL]; [[NSFileManager defaultManager] removeItemAtURL:self.queueURL error:NULL]; self.userId = nil; self.traits = [NSMutableDictionary dictionary]; self.queue = [NSMutableArray array]; - self.anonymousId = [self getAnonymousId:YES]; self.request.completion = nil; self.request = nil; }]; @@ -602,11 +578,6 @@ - (NSURL *)userIDURL return SEGAnalyticsURLForFilename(@"segmentio.userId"); } -- (NSURL *)anonymousIDURL -{ - return SEGAnalyticsURLForFilename(@"segment.anonymousId"); -} - - (NSURL *)queueURL { return SEGAnalyticsURLForFilename(@"segmentio.queue.plist"); @@ -617,22 +588,6 @@ - (NSURL *)traitsURL return SEGAnalyticsURLForFilename(@"segmentio.traits.plist"); } -- (NSString *)getAnonymousId:(BOOL)reset -{ - // We've chosen to generate a UUID rather than use the UDID (deprecated in iOS 5), - // identifierForVendor (iOS6 and later, can't be changed on logout), - // or MAC address (blocked in iOS 7). For more info see https://segment.io/libraries/ios#ids - NSURL *url = self.anonymousIDURL; - NSString *anonymousId = [[NSUserDefaults standardUserDefaults] valueForKey:SEGAnonymousIdKey] ?: [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:NULL]; - if (!anonymousId || reset) { - anonymousId = GenerateUUIDString(); - SEGLog(@"New anonymousId: %@", anonymousId); - [[NSUserDefaults standardUserDefaults] setObject:anonymousId forKey:SEGAnonymousIdKey]; - [anonymousId writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:NULL]; - } - return anonymousId; -} - - (NSString *)getUserId { return [[NSUserDefaults standardUserDefaults] valueForKey:SEGUserIdKey] ?: [[NSString alloc] initWithContentsOfURL:self.userIDURL encoding:NSUTF8StringEncoding error:NULL]; diff --git a/Pod/Classes/SEGAnalytics.h b/Pod/Classes/SEGAnalytics.h index fa5989780..d13823ab1 100644 --- a/Pod/Classes/SEGAnalytics.h +++ b/Pod/Classes/SEGAnalytics.h @@ -266,6 +266,9 @@ extern NSString *SEGAnalyticsIntegrationDidStart; */ - (NSDictionary *)bundledIntegrations; +/** Returns the anonymous ID. */ +- (NSString *)getAnonymousId; + /** Returns the configuration used to create the analytics client. */ - (SEGAnalyticsConfiguration *)configuration; diff --git a/Pod/Classes/SEGAnalytics.m b/Pod/Classes/SEGAnalytics.m index 1c73010dd..35b7612cf 100644 --- a/Pod/Classes/SEGAnalytics.m +++ b/Pod/Classes/SEGAnalytics.m @@ -14,6 +14,7 @@ static SEGAnalytics *__sharedInstance = nil; NSString *SEGAnalyticsIntegrationDidStart = @"io.segment.analytics.integration.did.start"; +NSString *const SEGAnonymousIdKey = @"SEGAnonymousId"; @interface SEGAnalyticsConfiguration () @@ -77,6 +78,7 @@ @interface SEGAnalytics () @property (nonatomic, strong) NSMutableDictionary *registeredIntegrations; @property (nonatomic) volatile BOOL initialized; @property (nonatomic, strong) SEGStoreKitTracker *storeKitTracker; +@property (nonatomic, copy) NSString *cachedAnonymousId; @end @@ -106,6 +108,7 @@ - (instancetype)initWithConfiguration:(SEGAnalyticsConfiguration *)configuration self.integrations = [NSMutableDictionary dictionaryWithCapacity:self.factories.count]; self.registeredIntegrations = [NSMutableDictionary dictionaryWithCapacity:self.factories.count]; self.configuration = configuration; + self.cachedAnonymousId = [self loadOrGenerateAnonymousID:NO]; // Update settings on each integration immediately [self refreshSettings]; @@ -242,12 +245,20 @@ - (void)identify:(NSString *)userId traits:(NSDictionary *)traits options:(NSDic { NSCAssert2(userId.length > 0 > traits.count > 0, @"either userId (%@) or traits (%@) must be provided.", userId, traits); + NSString *anonymousId = [options objectForKey:@"anonymousId"]; + if (anonymousId) { + [self saveAnonymousId:anonymousId]; + } else { + anonymousId = self.cachedAnonymousId; + } + SEGIdentifyPayload *payload = [[SEGIdentifyPayload alloc] initWithUserId:userId - anonymousId:[options objectForKey:@"anonymousId"] + anonymousId:anonymousId traits:SEGCoerceDictionary(traits) context:SEGCoerceDictionary([options objectForKey:@"context"]) integrations:[options objectForKey:@"integrations"]]; + [self callIntegrationsWithSelector:NSSelectorFromString(@"identify:") arguments:@[ payload ] options:options @@ -376,9 +387,49 @@ - (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification: - (void)reset { + [self resetAnonymousId]; [self callIntegrationsWithSelector:_cmd arguments:nil options:nil sync:false]; } +- (void)resetAnonymousId +{ + [[NSUserDefaults standardUserDefaults] setValue:nil forKey:SEGAnonymousIdKey]; + self.cachedAnonymousId = [self loadOrGenerateAnonymousID:YES]; +} + +- (NSString *)getAnonymousId; +{ + return self.cachedAnonymousId; +} + +- (NSURL *)anonymousIDURL +{ + return SEGAnalyticsURLForFilename(@"segment.anonymousId"); +} + +- (NSString *)loadOrGenerateAnonymousID:(BOOL)reset +{ + // We've chosen to generate a UUID rather than use the UDID (deprecated in iOS 5), + // identifierForVendor (iOS6 and later, can't be changed on logout), + // or MAC address (blocked in iOS 7). For more info see https://segment.io/libraries/ios#ids + NSURL *url = self.anonymousIDURL; + NSString *anonymousId = [[NSUserDefaults standardUserDefaults] valueForKey:SEGAnonymousIdKey] ?: [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:NULL]; + if (!anonymousId || reset) { + anonymousId = GenerateUUIDString(); + SEGLog(@"New anonymousId: %@", anonymousId); + [[NSUserDefaults standardUserDefaults] setObject:anonymousId forKey:SEGAnonymousIdKey]; + [anonymousId writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:NULL]; + } + return anonymousId; +} + +- (void)saveAnonymousId:(NSString *)anonymousId +{ + self.cachedAnonymousId = anonymousId; + [[NSUserDefaults standardUserDefaults] setValue:anonymousId forKey:SEGAnonymousIdKey]; + [self.cachedAnonymousId writeToURL:self.anonymousIDURL atomically:YES encoding:NSUTF8StringEncoding error:NULL]; +} + - (void)flush { [self callIntegrationsWithSelector:_cmd arguments:nil options:nil sync:false];