Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Payload Info & Traits Fixes #912

Merged
merged 4 commits into from
Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions Analytics/Classes/SEGAnalytics.m
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,19 @@ - (void)identify:(NSString *)userId traits:(NSDictionary *)traits options:(NSDic
}
// configure traits to match what is seen on android.
NSMutableDictionary *newTraits = [traits mutableCopy];
// if no traits were passed in, need to create.
if (newTraits == nil) {
newTraits = [[NSMutableDictionary alloc] init];
}
newTraits[@"anonymousId"] = anonId;
if (userId != nil) {
newTraits[@"userId"] = userId;
[SEGState sharedInstance].userInfo.userId = userId;
}
// merge w/ existing traits and set them.
NSDictionary *existingTraits = [SEGState sharedInstance].userInfo.traits;
[newTraits addEntriesFromDictionary:existingTraits];
[SEGState sharedInstance].userInfo.traits = newTraits;

[self run:SEGEventTypeIdentify payload:
[[SEGIdentifyPayload alloc] initWithUserId:userId
Expand Down Expand Up @@ -481,8 +489,12 @@ - (void)run:(SEGEventType)eventType payload:(SEGPayload *)payload
ctx.eventType = eventType;
ctx.payload = payload;
ctx.payload.messageId = GenerateUUIDString();
ctx.anonymousId = [SEGState sharedInstance].userInfo.anonymousId;
ctx.userId = [SEGState sharedInstance].userInfo.userId;
if (ctx.payload.userId == nil) {
ctx.payload.userId = [SEGState sharedInstance].userInfo.userId;
}
if (ctx.payload.anonymousId == nil) {
ctx.payload.anonymousId = [SEGState sharedInstance].userInfo.anonymousId;
}
}];

// Could probably do more things with callback later, but we don't use it yet.
Expand Down
4 changes: 0 additions & 4 deletions Analytics/Classes/SEGContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ NS_SWIFT_NAME(Context)
@property (nonatomic, readonly, nonnull) SEGAnalytics *_analytics;
@property (nonatomic, readonly) SEGEventType eventType;

@property (nonatomic, readonly, nullable) NSString *userId;
@property (nonatomic, readonly, nullable) NSString *anonymousId;
@property (nonatomic, readonly, nullable) NSError *error;
@property (nonatomic, readonly, nullable) SEGPayload *payload;
@property (nonatomic, readonly) BOOL debug;
Expand All @@ -74,8 +72,6 @@ NS_SWIFT_NAME(Context)
@protocol SEGMutableContext <NSObject>

@property (nonatomic) SEGEventType eventType;
@property (nonatomic, nullable) NSString *userId;
@property (nonatomic, nullable) NSString *anonymousId;
@property (nonatomic, nullable) SEGPayload *payload;
@property (nonatomic, nullable) NSError *error;
@property (nonatomic) BOOL debug;
Expand Down
2 changes: 0 additions & 2 deletions Analytics/Classes/SEGContext.m
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ - (id)copyWithZone:(NSZone *)zone
{
SEGContext *ctx = [[SEGContext allocWithZone:zone] initWithAnalytics:self._analytics];
ctx.eventType = self.eventType;
ctx.userId = self.userId;
ctx.anonymousId = self.anonymousId;
ctx.payload = self.payload;
ctx.error = self.error;
ctx.debug = self.debug;
Expand Down
4 changes: 0 additions & 4 deletions Analytics/Classes/SEGIdentifyPayload.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(IdentifyPayload)
@interface SEGIdentifyPayload : SEGPayload

@property (nonatomic, readonly, nullable) NSString *userId;

@property (nonatomic, readonly, nullable) NSString *anonymousId;

@property (nonatomic, readonly, nullable) JSON_DICT traits;

- (instancetype)initWithUserId:(NSString *)userId
Expand Down
8 changes: 2 additions & 6 deletions Analytics/Classes/SEGIdentifyPayload.m
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
#import "SEGIdentifyPayload.h"

@interface SEGIdentifyPayload ()
@property (nonatomic, readwrite, nullable) NSString *anonymousId;
@end

@implementation SEGIdentifyPayload

- (instancetype)initWithUserId:(NSString *)userId
Expand All @@ -13,9 +9,9 @@ - (instancetype)initWithUserId:(NSString *)userId
integrations:(NSDictionary *)integrations
{
if (self = [super initWithContext:context integrations:integrations]) {
_userId = [userId copy];
_anonymousId = [anonymousId copy];
_traits = [traits copy];
self.anonymousId = [anonymousId copy];
self.userId = [userId copy];
}
return self;
}
Expand Down
2 changes: 2 additions & 0 deletions Analytics/Classes/SEGPayload.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ NS_SWIFT_NAME(Payload)
@property (nonatomic, readonly) JSON_DICT integrations;
@property (nonatomic, strong) NSString *timestamp;
@property (nonatomic, strong) NSString *messageId;
@property (nonatomic, strong) NSString *anonymousId;
bsneed marked this conversation as resolved.
Show resolved Hide resolved
@property (nonatomic, strong) NSString *userId;

- (instancetype)initWithContext:(JSON_DICT)context integrations:(JSON_DICT)integrations;

Expand Down
10 changes: 6 additions & 4 deletions Analytics/Classes/SEGPayload.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

@implementation SEGPayload

@synthesize userId = _userId;
@synthesize anonymousId = _anonymousId;

- (instancetype)initWithContext:(NSDictionary *)context integrations:(NSDictionary *)integrations
{
if (self = [super init]) {
Expand All @@ -15,6 +18,9 @@ - (instancetype)initWithContext:(NSDictionary *)context integrations:(NSDictiona

_context = [combinedContext copy];
_integrations = [integrations copy];
_messageId = nil;
_userId = nil;
_anonymousId = nil;
}
return self;
}
Expand All @@ -23,20 +29,16 @@ - (instancetype)initWithContext:(NSDictionary *)context integrations:(NSDictiona


@implementation SEGApplicationLifecyclePayload

@end


@implementation SEGRemoteNotificationPayload

@end


@implementation SEGContinueUserActivityPayload

@end


@implementation SEGOpenURLPayload

@end
89 changes: 22 additions & 67 deletions Analytics/Classes/SEGSegmentIntegration.m
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ @interface SEGSegmentIntegration ()
@property (nonatomic, strong) NSTimer *flushTimer;
@property (nonatomic, strong) dispatch_queue_t serialQueue;
@property (nonatomic, strong) dispatch_queue_t backgroundTaskQueue;
@property (nonatomic, strong) NSMutableDictionary *traits;
@property (nonatomic, strong) NSDictionary *traits;
@property (nonatomic, assign) SEGAnalytics *analytics;
@property (nonatomic, assign) SEGAnalyticsConfiguration *configuration;
@property (atomic, copy) NSDictionary *referrer;
Expand Down Expand Up @@ -68,6 +68,9 @@ - (id)initWithAnalytics:(SEGAnalytics *)analytics httpClient:(SEGHTTPClient *)ht
self.serialQueue = seg_dispatch_queue_create_specific("io.segment.analytics.segmentio", DISPATCH_QUEUE_SERIAL);
self.backgroundTaskQueue = seg_dispatch_queue_create_specific("io.segment.analytics.backgroundTask", DISPATCH_QUEUE_SERIAL);
self.flushTaskID = UIBackgroundTaskInvalid;

// load traits from disk.
[self loadTraits];

[self dispatchBackground:^{
// Check for previous queue data in NSUserDefaults and remove if present.
Expand Down Expand Up @@ -97,63 +100,6 @@ - (id)initWithAnalytics:(SEGAnalytics *)analytics httpClient:(SEGHTTPClient *)ht
return self;
}

/*
* There is an iOS bug that causes instances of the CTTelephonyNetworkInfo class to
* sometimes get notifications after they have been deallocated.
* Instead of instantiating, using, and releasing instances you * must instead retain
* and never release them to work around the bug.
*
* Ref: http://stackoverflow.com/questions/14238586/coretelephony-crash
*/

#if TARGET_OS_IOS
static CTTelephonyNetworkInfo *_telephonyNetworkInfo;
#endif

- (NSDictionary *)liveContext
{
NSMutableDictionary *context = [[NSMutableDictionary alloc] init];
context[@"locale"] = [NSString stringWithFormat:
@"%@-%@",
[NSLocale.currentLocale objectForKey:NSLocaleLanguageCode],
[NSLocale.currentLocale objectForKey:NSLocaleCountryCode]];

context[@"timezone"] = [[NSTimeZone localTimeZone] name];

context[@"network"] = ({
NSMutableDictionary *network = [[NSMutableDictionary alloc] init];

if (self.reachability.isReachable) {
network[@"wifi"] = @(self.reachability.isReachableViaWiFi);
network[@"cellular"] = @(self.reachability.isReachableViaWWAN);
}

#if TARGET_OS_IOS
static dispatch_once_t networkInfoOnceToken;
dispatch_once(&networkInfoOnceToken, ^{
_telephonyNetworkInfo = [[CTTelephonyNetworkInfo alloc] init];
});

CTCarrier *carrier = [_telephonyNetworkInfo subscriberCellularProvider];
if (carrier.carrierName.length)
network[@"carrier"] = carrier.carrierName;
#endif

network;
});

context[@"traits"] = ({
NSMutableDictionary *traits = [[NSMutableDictionary alloc] initWithDictionary:[self traits]];
traits;
});

if (self.referrer) {
context[@"referrer"] = [self.referrer copy];
}

return [context copy];
}

- (void)dispatchBackground:(void (^)(void))block
{
seg_dispatch_specific_async(_serialQueue, block);
Expand Down Expand Up @@ -217,10 +163,19 @@ - (void)saveUserId:(NSString *)userId
}];
}

- (void)addTraits:(NSDictionary *)traits
- (NSDictionary *)traits
{
return [SEGState sharedInstance].userInfo.traits;
}

- (void)setTraits:(NSDictionary *)traits
{
[self saveTraits:traits];
}

- (void)saveTraits:(NSDictionary *)traits
{
[self dispatchBackground:^{
[self.traits addEntriesFromDictionary:traits];
[SEGState sharedInstance].userInfo.traits = traits;
#if TARGET_OS_TV
[self.userDefaultsStorage setDictionary:[self.traits copy] forKey:SEGTraitsKey];
Expand All @@ -236,7 +191,7 @@ - (void)identify:(SEGIdentifyPayload *)payload
{
[self dispatchBackground:^{
[self saveUserId:payload.userId];
[self addTraits:payload.traits];
[self saveTraits:payload.traits];
}];

NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
Expand Down Expand Up @@ -474,17 +429,17 @@ - (NSMutableArray *)queue
return _queue;
}

- (NSMutableDictionary *)traits
- (void)loadTraits
{
if (!_traits) {
if (![SEGState sharedInstance].userInfo.traits) {
NSDictionary *traits = nil;
#if TARGET_OS_TV
_traits = [[self.userDefaultsStorage dictionaryForKey:SEGTraitsKey] ?: @{} mutableCopy];
traits = [[self.userDefaultsStorage dictionaryForKey:SEGTraitsKey] ?: @{} mutableCopy];
#else
_traits = [[self.fileStorage dictionaryForKey:kSEGTraitsFilename] ?: @{} mutableCopy];
traits = [[self.fileStorage dictionaryForKey:kSEGTraitsFilename] ?: @{} mutableCopy];
#endif
[SEGState sharedInstance].userInfo.traits = traits;
}
[SEGState sharedInstance].userInfo.traits = _traits;
return _traits;
}

- (NSUInteger)maxBatchSize
Expand Down
9 changes: 5 additions & 4 deletions Analytics/Internal/SEGIntegrationsManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,13 @@ - (void)identify:(SEGIdentifyPayload *)payload
NSCAssert2(payload.userId.length > 0 || payload.traits.count > 0, @"either userId (%@) or traits (%@) must be provided.", payload.userId, payload.traits);

NSString *anonymousId = payload.anonymousId;
if (anonymousId) {
NSString *existingAnonymousId = self.cachedAnonymousId;

if (anonymousId == nil) {
payload.anonymousId = anonymousId;
} else if (![anonymousId isEqualToString:existingAnonymousId]) {
[self saveAnonymousId:anonymousId];
} else {
anonymousId = self.cachedAnonymousId;
}
payload.anonymousId = anonymousId;

[self callIntegrationsWithSelector:NSSelectorFromString(@"identify:")
arguments:@[ payload ]
Expand Down
44 changes: 44 additions & 0 deletions AnalyticsTests/AnalyticsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,50 @@ class AnalyticsTests: XCTestCase {
XCTAssertEqual(analytics2.test_integrationsManager()?.test_segmentIntegration()?.test_userId(), "testUserId1")
}

func testPersistsTraits() {
analytics.identify("testUserId1", traits: ["trait1": "someTrait"])

let analytics2 = Analytics(configuration: config)
analytics2.test_integrationsManager()?.test_setCachedSettings(settings: cachedSettings)

XCTAssertEqual(analytics.test_integrationsManager()?.test_segmentIntegration()?.test_userId(), "testUserId1")
XCTAssertEqual(analytics2.test_integrationsManager()?.test_segmentIntegration()?.test_userId(), "testUserId1")

var traits = analytics.test_integrationsManager()?.test_segmentIntegration()?.test_traits()
var storedTraits = analytics2.test_integrationsManager()?.test_segmentIntegration()?.test_traits()

if let trait1 = traits?["trait1"] as? String {
XCTAssertEqual(trait1, "someTrait")
} else {
XCTAssert(false, "Traits are nil!")
}

if let storedTrait1 = storedTraits?["trait1"] as? String {
XCTAssertEqual(storedTrait1, "someTrait")
} else {
XCTAssert(false, "Traits were not stored!")
}

analytics.identify("testUserId1", traits: ["trait2": "someOtherTrait"])

traits = analytics.test_integrationsManager()?.test_segmentIntegration()?.test_traits()
storedTraits = analytics2.test_integrationsManager()?.test_segmentIntegration()?.test_traits()

if let trait1 = traits?["trait2"] as? String {
XCTAssertEqual(trait1, "someOtherTrait")
} else {
XCTAssert(false, "Traits are nil!")
}

if let storedTrait1 = storedTraits?["trait2"] as? String {
XCTAssertEqual(storedTrait1, "someOtherTrait")
} else {
XCTAssert(false, "Traits were not stored!")
}


}

func testClearsUserData() {
analytics.identify("testUserId1", traits: [ "Test trait key" : "Test trait value"])
analytics.reset()
Expand Down
Loading