Skip to content

Commit

Permalink
fix first app open not respecting Autmatic Events setting from server
Browse files Browse the repository at this point in the history
  • Loading branch information
zihejia committed Feb 2, 2022
1 parent b7d33fb commit 3ec64e1
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 19 deletions.
94 changes: 94 additions & 0 deletions HelloMixpanel/HelloMixpanelTests/AutomaticEventsTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
#import <StoreKit/StoreKit.h>
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"

@interface Mixpanel()

- (void)handlingAutomaticEventsWith:(BOOL)decideTrackAutomaticEvents;

@end


@interface AutomaticEventsTests : MixpanelBaseTests

//@property (nonatomic, strong) AutomaticEvents *automaticEvents;
Expand Down Expand Up @@ -51,6 +58,93 @@ - (void)testUpdated {
[self removeDBfile:testMixpanel.apiToken];
}

- (void)testTrackAutomaticEventsIfNetworkNotAvailable {
// since the token does not exist, it will simulate decide being not available
Mixpanel *testMixpanel = [[Mixpanel alloc] initWithToken:[self randomTokenId] andFlushInterval:60];
testMixpanel.minimumSessionDuration = 0;
[testMixpanel.automaticEvents performSelector:NSSelectorFromString(@"appWillResignActive:") withObject:nil];
[self waitForMixpanelQueues:testMixpanel];
NSDictionary *event = [[self eventQueue:testMixpanel.apiToken] lastObject];
XCTAssertEqual([self eventQueue:testMixpanel.apiToken].count, 1, @"automatic events should be accumulated if check decide is offline(decideInstance.automaticEventsEnabled is nil)");
XCTAssertTrue([event[@"event"] isEqualToString:@"$ae_session"], @"should be app session event");
[self removeDBfile:testMixpanel.apiToken];
}

- (void)testDiscardAutomaticEventsIftrackAutomaticEventsEnabledIsFalse {
// since the token does not exist, it will simulate decide being not available
Mixpanel *testMixpanel = [[Mixpanel alloc] initWithToken:[self randomTokenId] andFlushInterval:60];
testMixpanel.trackAutomaticEventsEnabled = NO;
testMixpanel.minimumSessionDuration = 0;
[testMixpanel.automaticEvents performSelector:NSSelectorFromString(@"appWillResignActive:") withObject:nil];
[self waitForMixpanelQueues:testMixpanel];
XCTAssertEqual([self eventQueue:testMixpanel.apiToken].count, 0, @"automatic events should not be tracked");
[self removeDBfile:testMixpanel.apiToken];
}

- (void)testTrackAutomaticEventsIftrackAutomaticEventsEnabledIsTrue {
// since the token does not exist, it will simulate decide being not available
Mixpanel *testMixpanel = [[Mixpanel alloc] initWithToken:[self randomTokenId] andFlushInterval:60];
testMixpanel.trackAutomaticEventsEnabled = YES;
testMixpanel.minimumSessionDuration = 0;
[testMixpanel.automaticEvents performSelector:NSSelectorFromString(@"appWillResignActive:") withObject:nil];
[self waitForMixpanelQueues:testMixpanel];
[testMixpanel handlingAutomaticEventsWith:YES];
XCTAssertEqual([self eventQueue:testMixpanel.apiToken].count, 1, @"automatic events should be tracked");
[self removeDBfile:testMixpanel.apiToken];
}

- (void)testDiscardAutomaticEventsIftrackAutomaticEventsEnabledIsNotSet {
// since the token does not exist, it will simulate decide being not available
Mixpanel *testMixpanel = [[Mixpanel alloc] initWithToken:[self randomTokenId] andFlushInterval:60];
testMixpanel.minimumSessionDuration = 0;
[testMixpanel.automaticEvents performSelector:NSSelectorFromString(@"appWillResignActive:") withObject:nil];
[self waitForMixpanelQueues:testMixpanel];
XCTAssertEqual([self eventQueue:testMixpanel.apiToken].count, 1, @"by default, automatic events should be tracked");
[self removeDBfile:testMixpanel.apiToken];
}

- (void)testTrackAutomaticEventsIfDecideIsTrue {
// since the token does not exist, it will simulate decide being not available
Mixpanel *testMixpanel = [[Mixpanel alloc] initWithToken:[self randomTokenId] andFlushInterval:60];
testMixpanel.minimumSessionDuration = 0;

// simulate a decide api returning for tracking automatic events
[testMixpanel handlingAutomaticEventsWith: YES];
[testMixpanel.automaticEvents performSelector:NSSelectorFromString(@"appWillResignActive:") withObject:nil];
[self waitForMixpanelQueues:testMixpanel];
XCTAssertEqual([self eventQueue:testMixpanel.apiToken].count, 1, @"automatic events should be tracked");
[self removeDBfile:testMixpanel.apiToken];
}

- (void)testDiscardAutomaticEventsIfDecideIsFalse {
// since the token does not exist, it will simulate decide being not available
Mixpanel *testMixpanel = [[Mixpanel alloc] initWithToken:[self randomTokenId] andFlushInterval:60];
testMixpanel.minimumSessionDuration = 0;
// simulate a decide api returning for tracking automatic events
[testMixpanel handlingAutomaticEventsWith: NO];

[testMixpanel.automaticEvents performSelector:NSSelectorFromString(@"appWillResignActive:") withObject:nil];
[self waitForMixpanelQueues:testMixpanel];
XCTAssertEqual([self eventQueue:testMixpanel.apiToken].count, 0, @"automatic events should not be tracked");
[self removeDBfile:testMixpanel.apiToken];
}


- (void)testDiscardAutomaticEventsIfDecideIsTrueAutomaticEventIsFalse {
// since the token does not exist, it will simulate decide being not available
Mixpanel *testMixpanel = [[Mixpanel alloc] initWithToken:[self randomTokenId] andFlushInterval:60];
testMixpanel.minimumSessionDuration = 0;
testMixpanel.trackAutomaticEventsEnabled = NO;

[MixpanelPersistence saveAutomaticEventsEnabledFlag:YES fromDecide:YES apiToken:testMixpanel.apiToken];
[testMixpanel.automaticEvents performSelector:NSSelectorFromString(@"appWillResignActive:") withObject:nil];
[self waitForMixpanelQueues:testMixpanel];
XCTAssertEqual([self eventQueue:testMixpanel.apiToken].count, 0, @"automatic events should not be tracked");
[self removeDBfile:testMixpanel.apiToken];
}



- (void)testMultipleInstances {
Mixpanel *mp = [[Mixpanel alloc] initWithToken:[self randomTokenId] andFlushInterval:60];
mp.minimumSessionDuration = 0;
Expand Down
10 changes: 7 additions & 3 deletions HelloMixpanel/HelloMixpanelTests/HelloMixpanelTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ - (void)test5XXResponse {
// Failure count should be 3
NSTimeInterval waitTime = testMixpanel.network.requestsDisabledUntilTime - [[NSDate date] timeIntervalSince1970];
NSLog(@"Delta wait time is %.3f", waitTime);
XCTAssert(waitTime >= 120.f, "Network backoff time is less than 2 minutes.");
XCTAssert(waitTime >= 110.f, "Network backoff time is less than 2 minutes.");
XCTAssert(testMixpanel.network.consecutiveFailures == 2, @"Network failures did not equal 2");
XCTAssert([self eventQueue:testMixpanel.apiToken].count == 1, @"Removed an event from the queue that was not sent");
[self removeDBfile:testMixpanel.apiToken];
Expand Down Expand Up @@ -78,6 +78,7 @@ - (void)testFlushPeople {

- (void)testFlushNetworkFailure {
Mixpanel *testMixpanel = [[Mixpanel alloc] initWithToken:[self randomTokenId] andFlushInterval:60];
testMixpanel.trackAutomaticEventsEnabled = NO;
[testMixpanel setServerURL:kFakeServerUrl];
for (NSUInteger i=0, n=50; i<n; i++) {
[testMixpanel track:[NSString stringWithFormat:@"event %lu", (unsigned long)i]];
Expand All @@ -94,6 +95,7 @@ - (void)testIdentify {
for (NSInteger i = 0; i < 2; i++) { // run this twice to test reset works correctly wrt to distinct ids
NSString *testToken = [self randomTokenId];
Mixpanel *testMixpanel = [[Mixpanel alloc] initWithToken:testToken andFlushInterval:60];
testMixpanel.trackAutomaticEventsEnabled = NO;
NSString *distinctId = @"d1";
#if defined(MIXPANEL_UNIQUE_DISTINCT_ID)
XCTAssertEqualObjects(testMixpanel.distinctId, testMixpanel.defaultDistinctId, @"mixpanel identify failed to set default distinct id");
Expand Down Expand Up @@ -409,8 +411,7 @@ - (void)testReset {
XCTAssertTrue([self eventQueue:testMixpanel.apiToken].count == 0, @"events queue failed to reset");
XCTAssertTrue([self peopleQueue:testMixpanel.apiToken].count == 0, @"people queue failed to reset");

NSString *testToken1 = [self randomTokenId];
testMixpanel = [[Mixpanel alloc] initWithToken:testToken1 andFlushInterval:60];
testMixpanel = [[Mixpanel alloc] initWithToken:testToken andFlushInterval:60];
[self waitForMixpanelQueues:testMixpanel];
#if defined(MIXPANEL_UNIQUE_DISTINCT_ID)
XCTAssertEqualObjects(testMixpanel.distinctId, [testMixpanel defaultDistinctId], @"distinct id failed to reset after archive");
Expand All @@ -425,6 +426,7 @@ - (void)testReset {
- (void)testArchive {
NSString *testToken = [self randomTokenId];
Mixpanel *testMixpanel = [[Mixpanel alloc] initWithToken:testToken andFlushInterval:60];
testMixpanel.serverURL = kFakeServerUrl;
[testMixpanel archive];
testMixpanel = [[Mixpanel alloc] initWithToken:testToken andFlushInterval:60];
#if defined(MIXPANEL_UNIQUE_DISTINCT_ID)
Expand All @@ -449,6 +451,7 @@ - (void)testArchive {

[testMixpanel archive];
testMixpanel = [[Mixpanel alloc] initWithToken:testToken andFlushInterval:60];
testMixpanel.serverURL = kFakeServerUrl;
[self waitForMixpanelQueues:testMixpanel];
XCTAssertEqualObjects(testMixpanel.distinctId, @"d1", @"custom distinct archive failed");

Expand All @@ -465,6 +468,7 @@ - (void)testArchive {
XCTAssertEqualObjects(testMixpanel.timedEvents[@"e2"], @5.0, @"timedEvents archive failed");

testMixpanel = [[Mixpanel alloc] initWithToken:testToken andFlushInterval:60];
testMixpanel.serverURL = kFakeServerUrl;
eventQueue = [self eventQueue:testMixpanel.apiToken];
peopleQueue = [self peopleQueue:testMixpanel.apiToken];
groupQueue = [self groupQueue:testMixpanel.apiToken];
Expand Down
6 changes: 4 additions & 2 deletions HelloMixpanel/HelloMixpanelTests/MixpanelBaseTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ @implementation MixpanelBaseTests

- (void)setUp {
[super setUp];

NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"Mixpanel"];
[defaults setBool:YES forKey:@"MPFirstOpen"];
self.mixpanelWillFlush = NO;
}

- (void)tearDown {
[NSThread sleepForTimeInterval:1.0];
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"Mixpanel"];
[defaults removeObjectForKey:@"MPFirstOpen"];
[super tearDown];
}

Expand Down
13 changes: 11 additions & 2 deletions HelloMixpanel/HelloMixpanelTests/MixpanelOptOutTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
#import "TestConstants.h"
#import "MixpanelPeoplePrivate.h"

@interface Mixpanel()

- (void)handlingAutomaticEventsWith:(BOOL)decideTrackAutomaticEvents;

@end


@interface MixpanelOptOutTests : MixpanelBaseTests

Expand Down Expand Up @@ -41,7 +47,7 @@ - (void)testOptInWillAddOptInEvent
XCTAssertEqualObjects(event[@"event"], @"$opt_in", @"When opted in, a track '$opt_in' should have been queued");
}
else {
XCTAssertTrue([[self eventQueue:testMixpanel.apiToken] count] == 1, @"When opted in, event queue should have one even(opt in) being queued");
XCTAssertTrue([[self eventQueue:testMixpanel.apiToken] count] == 0, @"When opted in, event queue should have one even(opt in) being queued");
}
[self removeDBfile:testMixpanel.apiToken];
}
Expand All @@ -67,7 +73,7 @@ - (void)testOptInTrackingForDistinctID
}

- (void)testOptInTrackingForDistinctIDAndWithEventProperties
{
{
Mixpanel *testMixpanel = [[Mixpanel alloc] initWithToken:[self randomTokenId] andFlushInterval:60];
NSDate *now = [NSDate date];
NSDictionary *p = @{ @"string": @"yello",
Expand Down Expand Up @@ -153,6 +159,7 @@ - (void)testHasOptOutTrackingFlagBeingSetProperlyForOptIn
- (void)testOptOutTrackingWillNotGenerateEventQueue
{
Mixpanel *testMixpanel = [Mixpanel sharedInstanceWithToken:[self randomTokenId]];
[testMixpanel handlingAutomaticEventsWith:NO];
[testMixpanel optOutTracking];
for (NSUInteger i = 0, n = 50; i < n; i++) {
[testMixpanel track:[NSString stringWithFormat:@"event %lu", (unsigned long)i]];
Expand All @@ -165,6 +172,7 @@ - (void)testOptOutTrackingWillNotGenerateEventQueue
- (void)testOptOutTrackingWillNotGeneratePeopleQueue
{
Mixpanel *testMixpanel = [Mixpanel sharedInstanceWithToken:[self randomTokenId]];
[testMixpanel handlingAutomaticEventsWith:NO];
[testMixpanel identify:@"d1"];
[testMixpanel optOutTracking];
for (NSUInteger i = 0, n = 50; i < n; i++) {
Expand All @@ -178,6 +186,7 @@ - (void)testOptOutTrackingWillNotGeneratePeopleQueue
- (void)testOptOutTrackingWillSkipIdentify
{
Mixpanel *testMixpanel = [Mixpanel sharedInstanceWithToken:[self randomTokenId]];
[testMixpanel handlingAutomaticEventsWith:NO];
[testMixpanel optOutTracking];
[testMixpanel identify:@"d1"];
//opt in again just to enable people queue
Expand Down
4 changes: 2 additions & 2 deletions HelloMixpanel/HelloMixpanelTests/MixpanelPeopleTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ - (void)testDropUnidentifiedPeopleRecords {
[self waitForMixpanelQueues:testMixpanel];
XCTAssertTrue([self unIdentifiedPeopleQueue:testMixpanel.apiToken].count == 505);

NSDictionary *r = [self unIdentifiedPeopleQueue:testMixpanel.apiToken].firstObject;
NSDictionary *r = [self unIdentifiedPeopleQueue:testMixpanel.apiToken][0];
XCTAssertEqualObjects(r[@"$set"][@"i"], @(0));

r = [self unIdentifiedPeopleQueue:testMixpanel.apiToken].lastObject;
Expand All @@ -43,7 +43,7 @@ - (void)testDropPeopleRecords {
[self waitForMixpanelQueues:testMixpanel];
XCTAssertTrue([self peopleQueue:testMixpanel.apiToken].count == 505);

NSDictionary *r = [self peopleQueue:testMixpanel.apiToken].firstObject;
NSDictionary *r = [self peopleQueue:testMixpanel.apiToken][0];
XCTAssertEqualObjects(r[@"$set"][@"i"], @(0));

r = [self peopleQueue:testMixpanel.apiToken].lastObject;
Expand Down
2 changes: 1 addition & 1 deletion Sources/AutomaticEvents.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
@property (atomic, weak) id<TrackDelegate> delegate;
@property (atomic, assign) UInt64 minimumSessionDuration;
@property (atomic, assign) UInt64 maximumSessionDuration;
- (void)initializeEvents:(MixpanelPeople *)peopleInstance;
- (void)initializeEvents:(MixpanelPeople *)peopleInstance apiToken:(NSString *)apiToken;

@end

19 changes: 12 additions & 7 deletions Sources/AutomaticEvents.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,19 @@ - (instancetype)init
return self;
}

- (void)initializeEvents:(MixpanelPeople *)peopleInstance {
- (void)initializeEvents:(MixpanelPeople *)peopleInstance apiToken:(NSString *)apiToken {
people = peopleInstance;
NSString *firstOpenKey = @"MPFirstOpen";
if (defaults != nil && ![defaults boolForKey:firstOpenKey]) {
[self.delegate track:@"$ae_first_open" properties:nil];
[people setOnce:@{@"$ae_first_app_open_date": [NSDate date]}];
[defaults setBool:TRUE forKey:firstOpenKey];
[defaults synchronize];
NSString *legacyFirstOpenKey = @"MPFirstOpen";
NSString *firstOpenKey = [NSString stringWithFormat:@"MPFirstOpen-%@", apiToken];
// do not track `$ae_first_open` again if the legacy key exist,
// but we will start using the key with the mixpanel token in favour of multiple instances support
if (defaults != nil && ![defaults boolForKey:legacyFirstOpenKey]) {
if (![defaults boolForKey:firstOpenKey]) {
[self.delegate track:@"$ae_first_open" properties:nil];
[people setOnce:@{@"$ae_first_app_open_date": [NSDate date]}];
[defaults setBool:TRUE forKey:firstOpenKey];
[defaults synchronize];
}
}

NSDictionary* infoDict = [NSBundle mainBundle].infoDictionary;
Expand Down
24 changes: 22 additions & 2 deletions Sources/Mixpanel.m
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ - (instancetype)initWithToken:(NSString *)apiToken
#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT
self.automaticEvents = [[AutomaticEvents alloc] init];
self.automaticEvents.delegate = self;
[self.automaticEvents initializeEvents:self.people];
[self.automaticEvents initializeEvents:self.people apiToken:apiToken];
#endif
}
instances[apiToken] = self;
Expand Down Expand Up @@ -262,6 +262,11 @@ - (void)setUseIPAddressForGeoLocation:(BOOL)useIPAddressForGeoLocation
- (void)setTrackAutomaticEventsEnabled:(BOOL)trackAutomaticEventsEnabled
{
[MixpanelPersistence saveAutomaticEventsEnabledFlag:trackAutomaticEventsEnabled fromDecide:NO apiToken:self.apiToken];
if (!trackAutomaticEventsEnabled) {
dispatch_async(self.serialQueue, ^{
[self.persistence removeAutomaticEvents];
});
}
}

#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT
Expand Down Expand Up @@ -1357,6 +1362,15 @@ - (BOOL)enableLogging

#pragma mark - Decide

- (void)handlingAutomaticEventsWith:(BOOL)decideTrackAutomaticEvents {
[MixpanelPersistence saveAutomaticEventsEnabledFlag:decideTrackAutomaticEvents fromDecide:YES apiToken:self.apiToken];
if (!decideTrackAutomaticEvents) {
dispatch_async(self.serialQueue, ^{
[self.persistence removeAutomaticEvents];
});
}
}

- (void)checkForDecideResponse
{
dispatch_async(self.networkQueue, ^{
Expand Down Expand Up @@ -1408,7 +1422,13 @@ - (void)checkForDecideResponse

id rawAutomaticEvents = object[@"automatic_events"];
if (rawAutomaticEvents != nil && [rawAutomaticEvents isKindOfClass:[NSNumber class]]) {
[MixpanelPersistence saveAutomaticEventsEnabledFlag:[rawAutomaticEvents boolValue] fromDecide:YES apiToken:self.apiToken];
[self handlingAutomaticEventsWith: [rawAutomaticEvents boolValue]];
// [MixpanelPersistence saveAutomaticEventsEnabledFlag:[rawAutomaticEvents boolValue] fromDecide:YES apiToken:self.apiToken];
// if (![rawAutomaticEvents boolValue]) {
// dispatch_async(self.serialQueue, ^{
// [self.persistence removeAutomaticEvents];
// });
// }
}

@synchronized (self) {
Expand Down
1 change: 1 addition & 0 deletions Sources/MixpanelPersistence.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

- (NSArray *)loadEntitiesInBatch:(NSString *)type;
- (NSArray *)loadEntitiesInBatch:(NSString *)type flag:(BOOL)flag;
- (void)removeAutomaticEvents;
- (void)removeEntitiesInBatch:(NSString *)type ids:(NSArray *)ids;
- (void)identifyPeople;
- (void)resetEntities;
Expand Down
11 changes: 11 additions & 0 deletions Sources/MixpanelPersistence.m
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ - (NSArray *)loadEntitiesInBatch:(NSString *)type flag:(BOOL)flag {
return entities;
}

- (void)removeAutomaticEvents {
NSArray *events = [self loadEntitiesInBatch:PersistenceTypeEvents];
NSMutableArray *ids = [NSMutableArray new];
[events enumerateObjectsUsingBlock:^(NSMutableDictionary *event, NSUInteger idx, BOOL * _Nonnull stop) {
if ([event[@"event"] hasPrefix:@"$ae_"]) {
[ids addObject:event[@"id"]];
}
}];
[self removeEntitiesInBatch:PersistenceTypeEvents ids:ids];
}

- (void)removeEntitiesInBatch:(NSString *)type ids:(NSArray *)ids {
[self.mpdb deleteRows:type ids:ids];
}
Expand Down

0 comments on commit 3ec64e1

Please sign in to comment.