From 06b2003206c6ea532d645f0544ff39e50b3bc32d Mon Sep 17 00:00:00 2001 From: Prateek Srivastava Date: Thu, 29 Nov 2018 09:09:39 -0800 Subject: [PATCH] fix: allow setting anonymousId (#799) Historically, the iOS library has allowed setting a custom anonymous identifier (https://github.com/segmentio/analytics-ios/pull/485). During adding middlewares, a bug was introduced which started ignoring the anonymousId option. This fixes the bug and adds a test to validate the fix. This was also tested in the example app end to end and I've verified that events show up with the custom anonymousId. Test snipet: ``` [[SEGAnalytics sharedAnalytics] identify:@"Prateek" traits:nil options: @{ @"anonymousId":@"test_anonymousId" }]; ``` Event in debugger: https://cloudup.com/cFyVH4NPR_f Subsequent events will automatically attach the new anonymous ID https://cloudup.com/cK-i_SCbwsN --- .../Integrations/SEGIntegrationsManager.m | 10 ++++++- Analytics/Classes/SEGAnalytics.m | 2 +- AnalyticsTests/TrackingTests.swift | 28 ++++++++++++++----- .../CocoapodsExample/AppDelegate.m | 4 +++ 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/Analytics/Classes/Integrations/SEGIntegrationsManager.m b/Analytics/Classes/Integrations/SEGIntegrationsManager.m index ad9b4677e..b597ee168 100644 --- a/Analytics/Classes/Integrations/SEGIntegrationsManager.m +++ b/Analytics/Classes/Integrations/SEGIntegrationsManager.m @@ -541,7 +541,15 @@ - (void)context:(SEGContext *)context next:(void (^_Nonnull)(SEGContext *_Nullab switch (context.eventType) { case SEGEventTypeIdentify: { SEGIdentifyPayload *p = (SEGIdentifyPayload *)context.payload; - [self identify:p.userId traits:p.traits options:p.options]; + NSDictionary *options; + if (p.anonymousId) { + NSMutableDictionary *mutableOptions = [[NSMutableDictionary alloc] initWithDictionary:p.options]; + mutableOptions[@"anonymousId"] = p.anonymousId; + options = [mutableOptions copy]; + } else { + options = p.options; + } + [self identify:p.userId traits:p.traits options:options]; break; } case SEGEventTypeTrack: { diff --git a/Analytics/Classes/SEGAnalytics.m b/Analytics/Classes/SEGAnalytics.m index d855cc00b..134ecd94f 100644 --- a/Analytics/Classes/SEGAnalytics.m +++ b/Analytics/Classes/SEGAnalytics.m @@ -201,7 +201,7 @@ - (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); [self run:SEGEventTypeIdentify payload: [[SEGIdentifyPayload alloc] initWithUserId:userId - anonymousId:nil + anonymousId:[options objectForKey:@"anonymousId"] traits:SEGCoerceDictionary(traits) context:SEGCoerceDictionary([options objectForKey:@"context"]) integrations:[options objectForKey:@"integrations"]]]; diff --git a/AnalyticsTests/TrackingTests.swift b/AnalyticsTests/TrackingTests.swift index 7a5bab552..77c2bf9e8 100644 --- a/AnalyticsTests/TrackingTests.swift +++ b/AnalyticsTests/TrackingTests.swift @@ -15,7 +15,7 @@ class TrackingTests: QuickSpec { override func spec() { var passthrough: SEGPassthroughMiddleware! var analytics: SEGAnalytics! - + beforeEach { let config = SEGAnalyticsConfiguration(writeKey: "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE") passthrough = SEGPassthroughMiddleware() @@ -24,11 +24,11 @@ class TrackingTests: QuickSpec { ] analytics = SEGAnalytics(configuration: config) } - + afterEach { analytics.reset() } - + it("handles identify:") { analytics.identify("testUserId1", traits: [ "firstName": "Peter" @@ -36,9 +36,23 @@ class TrackingTests: QuickSpec { expect(passthrough.lastContext?.eventType) == SEGEventType.identify let identify = passthrough.lastContext?.payload as? SEGIdentifyPayload expect(identify?.userId) == "testUserId1" + expect(identify?.anonymousId).to(beNil()) + expect(identify?.traits?["firstName"] as? String) == "Peter" + } + + it("handles identify with custom anonymousId:") { + analytics.identify("testUserId1", traits: [ + "firstName": "Peter" + ], options: [ + "anonymousId": "a_custom_anonymous_id" + ]) + expect(passthrough.lastContext?.eventType) == SEGEventType.identify + let identify = passthrough.lastContext?.payload as? SEGIdentifyPayload + expect(identify?.userId) == "testUserId1" + expect(identify?.anonymousId) == "a_custom_anonymous_id" expect(identify?.traits?["firstName"] as? String) == "Peter" } - + it("handles track:") { analytics.track("User Signup", properties: [ "method": "SSO" @@ -48,14 +62,14 @@ class TrackingTests: QuickSpec { expect(payload?.event) == "User Signup" expect(payload?.properties?["method"] as? String) == "SSO" } - + it("handles alias:") { analytics.alias("persistentUserId") expect(passthrough.lastContext?.eventType) == SEGEventType.alias let payload = passthrough.lastContext?.payload as? SEGAliasPayload expect(payload?.theNewId) == "persistentUserId" } - + it("handles screen:") { analytics.screen("Home", properties: [ "referrer": "Google" @@ -65,7 +79,7 @@ class TrackingTests: QuickSpec { expect(screen?.name) == "Home" expect(screen?.properties?["referrer"] as? String) == "Google" } - + it("handles group:") { analytics.group("acme-company", traits: [ "employees": 2333 diff --git a/Examples/CocoapodsExample/CocoapodsExample/AppDelegate.m b/Examples/CocoapodsExample/CocoapodsExample/AppDelegate.m index e9c882a1e..72fed531c 100644 --- a/Examples/CocoapodsExample/CocoapodsExample/AppDelegate.m +++ b/Examples/CocoapodsExample/CocoapodsExample/AppDelegate.m @@ -29,7 +29,11 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( configuration.trackAttributionData = YES; configuration.flushAt = 1; [SEGAnalytics setupWithConfiguration:configuration]; + [[SEGAnalytics sharedAnalytics] identify:@"Prateek" traits:nil options: @{ + @"anonymousId":@"test_anonymousId" + }]; [[SEGAnalytics sharedAnalytics] track:@"Cocoapods Example Launched"]; + [[SEGAnalytics sharedAnalytics] flush]; NSLog(@"application:didFinishLaunchingWithOptions: %@", launchOptions); return YES;