diff --git a/HelloMixpanel/HelloMixpanelTests/AutomaticEventsTests.m b/HelloMixpanel/HelloMixpanelTests/AutomaticEventsTests.m index 45cccda8..ade42f36 100644 --- a/HelloMixpanel/HelloMixpanelTests/AutomaticEventsTests.m +++ b/HelloMixpanel/HelloMixpanelTests/AutomaticEventsTests.m @@ -12,6 +12,13 @@ #import #pragma clang diagnostic ignored "-Warc-performSelector-leaks" +@interface Mixpanel() + +- (void)handlingAutomaticEventsWith:(BOOL)decideTrackAutomaticEvents; + +@end + + @interface AutomaticEventsTests : MixpanelBaseTests //@property (nonatomic, strong) AutomaticEvents *automaticEvents; @@ -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; diff --git a/HelloMixpanel/HelloMixpanelTests/HelloMixpanelTests.m b/HelloMixpanel/HelloMixpanelTests/HelloMixpanelTests.m index 1992ae16..fb1bb10f 100644 --- a/HelloMixpanel/HelloMixpanelTests/HelloMixpanelTests.m +++ b/HelloMixpanel/HelloMixpanelTests/HelloMixpanelTests.m @@ -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]; @@ -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 delegate; @property (atomic, assign) UInt64 minimumSessionDuration; @property (atomic, assign) UInt64 maximumSessionDuration; -- (void)initializeEvents:(MixpanelPeople *)peopleInstance; +- (void)initializeEvents:(MixpanelPeople *)peopleInstance apiToken:(NSString *)apiToken; @end diff --git a/Sources/AutomaticEvents.m b/Sources/AutomaticEvents.m index 6e0f6d4a..99a523ef 100644 --- a/Sources/AutomaticEvents.m +++ b/Sources/AutomaticEvents.m @@ -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; diff --git a/Sources/Mixpanel.m b/Sources/Mixpanel.m index d0a7ada9..c3c445a9 100755 --- a/Sources/Mixpanel.m +++ b/Sources/Mixpanel.m @@ -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; @@ -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 @@ -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, ^{ @@ -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) { diff --git a/Sources/MixpanelPersistence.h b/Sources/MixpanelPersistence.h index 9a40ab96..55722949 100644 --- a/Sources/MixpanelPersistence.h +++ b/Sources/MixpanelPersistence.h @@ -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; diff --git a/Sources/MixpanelPersistence.m b/Sources/MixpanelPersistence.m index 71bca316..a856e203 100644 --- a/Sources/MixpanelPersistence.m +++ b/Sources/MixpanelPersistence.m @@ -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]; }