Skip to content

Commit

Permalink
Merge pull request #677 from snowplow/release/3.1.1
Browse files Browse the repository at this point in the history
Release/3.1.1
  • Loading branch information
AlexBenny authored Mar 3, 2022
2 parents d223aa3 + a94591c commit c873da2
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 39 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
Version 3.1.1 (2022-03-03)
--------------------------
Fix object cannot be nil (key: userId) when initialising Snowplow tracker (#675)

Version 3.1.0 (2022-02-25)
--------------------------
Update copyright headers to 2022 (#669)
Expand Down
42 changes: 41 additions & 1 deletion Snowplow iOSTests/TestSession.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ @implementation TestSession
- (void)setUp {
[super setUp];
[self cleanSessionFileWithNamespace:@"tracker"];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kSPInstallationUserId];
}

- (void)tearDown {
Expand Down Expand Up @@ -432,10 +433,49 @@ - (void)testMultipleTrackersUpdateDifferentSessions {
XCTAssertNotEqualObjects(id1, previousId2b);
}

/// Service methods
- (void)testMigrateSessionFromV3_0 {
[self cleanSessionFileWithNamespace:@"tracker"];
[self storeSessionAsV3_0WithNamespace:@"tracker" eventId:@"eventId" sessionId:@"sessionId" sessionIndex:123 userId:@"userId"];

SPEmitter *emitter = [SPEmitter build:^(id<SPEmitterBuilder> builder) {
[builder setUrlEndpoint:@""];
}];
SPTracker *tracker = [SPTracker build:^(id<SPTrackerBuilder> _Nonnull builder) {
[builder setTrackerNamespace:@"tracker"];
[builder setEmitter:emitter];
[builder setSessionContext:YES];
}];
SPEvent *event = [[SPStructured alloc] initWithCategory:@"c" action:@"a"];
[tracker track:event];

SPSessionState *sessionState = tracker.session.state;
XCTAssertEqualObjects(@"sessionId", sessionState.previousSessionId);
XCTAssertEqual(124, sessionState.sessionIndex);
XCTAssertEqualObjects(@"userId", sessionState.userId);
XCTAssertNotEqualObjects(@"eventId", sessionState.firstEventId);
}



// Service methods

- (void)cleanSessionFileWithNamespace:(NSString *)namespace {
[SPDataPersistence removeDataPersistenceWithNamespace:namespace];
}

// Migration methods

- (void)storeSessionAsV3_0WithNamespace:(NSString *)namespace eventId:(NSString *)eventId sessionId:(NSString *)sessionId sessionIndex:(int)sessionIndex userId:(NSString *)userId {
SPDataPersistence *dataPersistence = [SPDataPersistence dataPersistenceForNamespace:namespace];
NSMutableDictionary *newSessionDict = [NSMutableDictionary new];
[newSessionDict setObject:eventId forKey:kSPSessionFirstEventId];
[newSessionDict setObject:sessionId forKey:kSPSessionId];
[newSessionDict setObject:[NSNumber numberWithInt:sessionIndex] forKey:kSPSessionIndex];
dataPersistence.session = newSessionDict;

//Store userId
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:userId forKey:kSPInstallationUserId];
}

@end
8 changes: 4 additions & 4 deletions Snowplow/Internal/SPTrackerConstants.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ @implementation SPTrackerConstants
// --- Version

#if SNOWPLOW_TARGET_IOS
NSString * const kSPVersion = @"ios-3.1.0";
NSString * const kSPVersion = @"ios-3.1.1";
#elif SNOWPLOW_TARGET_TV
NSString * const kSPVersion = @"tvos-3.1.0";
NSString * const kSPVersion = @"tvos-3.1.1";
#elif SNOWPLOW_TARGET_WATCHOS
NSString * const kSPVersion = @"watchos-3.1.0";
NSString * const kSPVersion = @"watchos-3.1.1";
#else
NSString * const kSPVersion = @"osx-3.1.0";
NSString * const kSPVersion = @"osx-3.1.1";
#endif

// --- Session Dictionary keys
Expand Down
15 changes: 8 additions & 7 deletions Snowplow/Internal/Session/SPSession.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#import "SPWeakTimerTarget.h"
#import "SPTracker.h"
#import "SPLogger.h"
#import "NSDictionary+SP_TypeMethods.h"

#import "SPBackground.h"
#import "SPForeground.h"
Expand Down Expand Up @@ -74,15 +75,15 @@ - (instancetype)initWithForegroundTimeout:(NSInteger)foregroundTimeout andBackgr
_isNewSession = YES;
self.tracker = tracker;
self.dataPersistence = [SPDataPersistence dataPersistenceForNamespace:tracker.trackerNamespace];
NSDictionary *storedSessionDict = self.dataPersistence.session;

if (storedSessionDict) {
NSMutableDictionary *storedSessionDict = self.dataPersistence.session.mutableCopy;
_userId = [self retrieveUserIdWithSessionDict:storedSessionDict];
if (storedSessionDict && _userId) {
[storedSessionDict setObject:_userId forKey:kSPSessionUserId];
_state = [[SPSessionState alloc] initWithStoredState:storedSessionDict];
}
if (!_state) {
SPLogTrack(nil, @"No previous session info available");
}
_userId = [self retrieveUserIdWithState:_state];

// Start session check
self.lastSessionCheck = [SPUtilities getTimestamp];
Expand Down Expand Up @@ -173,8 +174,8 @@ - (SPTracker *) getTracker {

// MARK: - Private

- (NSString *)retrieveUserIdWithState:(SPSessionState *)state {
NSString *userId = state.userId ?: [SPUtilities getUUIDString];
- (NSString *)retrieveUserIdWithSessionDict:(NSDictionary *)sessionDict {
NSString *userId = [sessionDict sp_stringForKey:kSPSessionUserId defaultValue:nil] ?: [SPUtilities getUUIDString];
// Session_UserID is available only if the session context is enabled.
// In a future version we would like to make it available even if the session context is disabled.
// For this reason, we store the Session_UserID in a separate storage (decoupled by session values)
Expand All @@ -186,7 +187,7 @@ - (NSString *)retrieveUserIdWithState:(SPSessionState *)state {
NSString *storedUserId = [userDefaults stringForKey:kSPInstallationUserId];
if (storedUserId) {
userId = storedUserId;
} else if (_userId) {
} else {
[userDefaults setObject:userId forKey:kSPInstallationUserId];
}
return userId;
Expand Down
62 changes: 37 additions & 25 deletions Snowplow/Internal/Session/SPSessionState.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ @interface SPSessionState ()

@implementation SPSessionState

+ (NSMutableDictionary<NSString *, NSObject *> *)buildSessionDictionaryWithFirstEventId:(NSString *)firstEventId currentSessionId:(NSString *)currentSessionId previousSessionId:(NSString *)previousSessionId sessionIndex:(NSInteger)sessionIndex userId:(NSString *)userId storage:(NSString *)storage
{
NSMutableDictionary *dictionary = [NSMutableDictionary new];
[dictionary setObject:previousSessionId ?: [NSNull null] forKey:kSPSessionPreviousId];
[dictionary setObject:currentSessionId forKey:kSPSessionId];
[dictionary setObject:firstEventId forKey:kSPSessionFirstEventId];
[dictionary setObject:[NSNumber numberWithInteger:sessionIndex] forKey:kSPSessionIndex];
[dictionary setObject:storage forKey:kSPSessionStorage];
[dictionary setObject:userId forKey:kSPSessionUserId];
return dictionary;
}

- (instancetype)initWithFirstEventId:(NSString *)firstEventId currentSessionId:(NSString *)currentSessionId previousSessionId:(NSString *)previousSessionId sessionIndex:(NSInteger)sessionIndex userId:(NSString *)userId storage:(NSString *)storage {
if (self = [super init]) {
self.firstEventId = firstEventId;
Expand All @@ -46,48 +58,48 @@ - (instancetype)initWithFirstEventId:(NSString *)firstEventId currentSessionId:(
self.userId = userId;
self.storage = storage;

NSMutableDictionary *dictionary = [NSMutableDictionary new];
[dictionary setObject:previousSessionId ?: [NSNull null] forKey:kSPSessionPreviousId];
[dictionary setObject:currentSessionId forKey:kSPSessionId];
[dictionary setObject:firstEventId forKey:kSPSessionFirstEventId];
[dictionary setObject:[NSNumber numberWithInteger:sessionIndex] forKey:kSPSessionIndex];
[dictionary setObject:storage forKey:kSPSessionStorage];
[dictionary setObject:userId forKey:kSPSessionUserId];
self.sessionDictionary = dictionary;
self.sessionDictionary = [SPSessionState buildSessionDictionaryWithFirstEventId:firstEventId
currentSessionId:currentSessionId
previousSessionId:previousSessionId
sessionIndex:sessionIndex
userId:userId
storage:storage];
}
return self;
}

- (instancetype)initWithStoredState:(NSDictionary<NSString *,NSObject *> *)storedState {
if (self = [super init]) {
self.sessionDictionary = [storedState mutableCopy];

self.firstEventId = [self.sessionDictionary sp_stringForKey:kSPSessionFirstEventId defaultValue:nil];
if (!self.firstEventId) return nil;

self.sessionId = [self.sessionDictionary sp_stringForKey:kSPSessionId defaultValue:nil];
self.sessionId = [storedState sp_stringForKey:kSPSessionId defaultValue:nil];
if (!self.sessionId) return nil;

self.previousSessionId = [self.sessionDictionary sp_stringForKey:kSPSessionPreviousId defaultValue:nil];

NSNumber *sessionIndexNumber = [self.sessionDictionary sp_numberForKey:kSPSessionIndex defaultValue:nil];
NSNumber *sessionIndexNumber = [storedState sp_numberForKey:kSPSessionIndex defaultValue:nil];
if (!sessionIndexNumber) return nil;
self.sessionIndex = sessionIndexNumber.integerValue;

self.userId = [self.sessionDictionary sp_stringForKey:kSPSessionUserId defaultValue:nil];
self.userId = [storedState sp_stringForKey:kSPSessionUserId defaultValue:nil];
if (!self.userId) return nil;

self.storage = [self.sessionDictionary sp_stringForKey:kSPSessionStorage defaultValue:nil];
if (!self.storage) return nil;
self.previousSessionId = [storedState sp_stringForKey:kSPSessionPreviousId defaultValue:nil];

// The FirstEventId should be stored in legacy persisted sessions even
// if it wasn't used. Anyway we provide a default value in order to be
// defensive and exclude any possible issue with a missing value.
self.firstEventId = [storedState sp_stringForKey:kSPSessionFirstEventId
defaultValue:@"00000000-0000-0000-0000-000000000000"];

self.storage = [storedState sp_stringForKey:kSPSessionStorage defaultValue:@"LOCAL_STORAGE"];

self.sessionDictionary = [SPSessionState buildSessionDictionaryWithFirstEventId:self.firstEventId
currentSessionId:self.sessionId
previousSessionId:self.previousSessionId
sessionIndex:self.sessionIndex
userId:self.userId
storage:self.storage];
}
return self;
}

- (void)setUserId:(NSString *)userId {
_userId = userId;
[self.sessionDictionary setObject:userId forKey:kSPSessionUserId];
}

- (NSDictionary<NSString *,NSObject *> *)sessionContext {
return [self.sessionDictionary mutableCopy];
}
Expand Down
2 changes: 1 addition & 1 deletion SnowplowTracker.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SnowplowTracker"
s.version = "3.1.0"
s.version = "3.1.1"
s.summary = "Snowplow event tracker for iOS, macOS, tvOS, watchOS for apps and games."
s.description = <<-DESC
Snowplow is a mobile and event analytics platform with a difference: rather than tell our users how they should analyze their data, we deliver their event-level data in their own data warehouse, on their own Amazon Redshift or Postgres database, so they can analyze it any way they choose. Snowplow mobile is used by data-savvy games companies and app developers to better understand their users and how they engage with their games and applications. Snowplow is open source using the business-friendly Apache License, Version 2.0 and scales horizontally to many billions of events.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.1.0
3.1.1

0 comments on commit c873da2

Please sign in to comment.