diff --git a/MixpanelDemo/MixpanelDemoTests/MixpanelAutomaticEventsTests.swift b/MixpanelDemo/MixpanelDemoTests/MixpanelAutomaticEventsTests.swift index ae6608cf..06b5e290 100644 --- a/MixpanelDemo/MixpanelDemoTests/MixpanelAutomaticEventsTests.swift +++ b/MixpanelDemo/MixpanelDemoTests/MixpanelAutomaticEventsTests.swift @@ -69,7 +69,7 @@ class MixpanelAutomaticEventsTests: MixpanelBaseTests { removeDBfile(testMixpanel.apiToken) } - func testFlushAutomaticEventsIftrackAutomaticEventsEnabledIsNotSet() { + func testHoldAutomaticEventsIftrackAutomaticEventsEnabledIsNotSet() { let testMixpanel = Mixpanel.initialize(token: randomId(), flushInterval: 60) testMixpanel.minimumSessionDuration = 0; testMixpanel.automaticEvents.perform(#selector(AutomaticEvents.appWillResignActive(_:)), @@ -78,7 +78,8 @@ class MixpanelAutomaticEventsTests: MixpanelBaseTests { XCTAssertTrue(eventQueue(token: testMixpanel.apiToken).count == 2, "automatic events should be tracked") flushAndWaitForTrackingQueue(testMixpanel) - XCTAssertTrue(eventQueue(token: testMixpanel.apiToken).count == 0, "automatic events should be flushed") + let appOpenEvent = eventQueue(token: testMixpanel.apiToken).last + XCTAssertEqual(appOpenEvent?["event"] as? String, "$ae_session", "automatic events should NOT be flushed") removeDBfile(testMixpanel.apiToken) } @@ -86,7 +87,7 @@ class MixpanelAutomaticEventsTests: MixpanelBaseTests { func testDiscardAutomaticEventsIfDecideIsFalse() { let testMixpanel = Mixpanel.initialize(token: randomId(), flushInterval: 60) testMixpanel.minimumSessionDuration = 0; - MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: false, fromDecide: true, apiToken: testMixpanel.apiToken) + MixpanelPersistence.saveAutomaticEventsEnabledFlag(value: false, fromDecide: true, apiToken: testMixpanel.apiToken) testMixpanel.automaticEvents.perform(#selector(AutomaticEvents.appWillResignActive(_:)), with: Notification(name: Notification.Name(rawValue: "test"))) waitForTrackingQueue(testMixpanel) @@ -99,7 +100,7 @@ class MixpanelAutomaticEventsTests: MixpanelBaseTests { func testFlushAutomaticEventsIfDecideIsTrue() { let testMixpanel = Mixpanel.initialize(token: randomId(), flushInterval: 60) testMixpanel.minimumSessionDuration = 0; - MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: true, fromDecide: true, apiToken: testMixpanel.apiToken) + MixpanelPersistence.saveAutomaticEventsEnabledFlag(value: true, fromDecide: true, apiToken: testMixpanel.apiToken) testMixpanel.automaticEvents.perform(#selector(AutomaticEvents.appWillResignActive(_:)), with: Notification(name: Notification.Name(rawValue: "test"))) waitForTrackingQueue(testMixpanel) @@ -115,7 +116,7 @@ class MixpanelAutomaticEventsTests: MixpanelBaseTests { func testDiscardAutomaticEventsIfDecideIsTrueSettingIsFalse() { let testMixpanel = Mixpanel.initialize(token: randomId(), flushInterval: 60, trackAutomaticEvents: false) testMixpanel.minimumSessionDuration = 0; - MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: true, fromDecide: true, apiToken: testMixpanel.apiToken) + MixpanelPersistence.saveAutomaticEventsEnabledFlag(value: true, fromDecide: true, apiToken: testMixpanel.apiToken) testMixpanel.automaticEvents.perform(#selector(AutomaticEvents.appWillResignActive(_:)), with: Notification(name: Notification.Name(rawValue: "test"))) waitForTrackingQueue(testMixpanel) @@ -126,7 +127,7 @@ class MixpanelAutomaticEventsTests: MixpanelBaseTests { func testFlushAutomaticEventsIfDecideIsFalseSettingIsTrue() { let testMixpanel = Mixpanel.initialize(token: randomId(), flushInterval: 60, trackAutomaticEvents: true) testMixpanel.minimumSessionDuration = 0; - MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: false, fromDecide: true, apiToken: testMixpanel.apiToken) + MixpanelPersistence.saveAutomaticEventsEnabledFlag(value: false, fromDecide: true, apiToken: testMixpanel.apiToken) testMixpanel.automaticEvents.perform(#selector(AutomaticEvents.appWillResignActive(_:)), with: Notification(name: Notification.Name(rawValue: "test"))) waitForTrackingQueue(testMixpanel) @@ -144,13 +145,13 @@ class MixpanelAutomaticEventsTests: MixpanelBaseTests { func testFirstAppShouldOnlyBeTrackedOnce() { let testToken = randomId() - let mp = Mixpanel.initialize(token: testToken) + let mp = Mixpanel.initialize(token: testToken, trackAutomaticEvents: true) mp.minimumSessionDuration = 0; waitForTrackingQueue(mp) XCTAssertEqual(eventQueue(token: mp.apiToken).count, 1, "First app open should be tracked again") flushAndWaitForTrackingQueue(mp) - let mp2 = Mixpanel.initialize(token: testToken) + let mp2 = Mixpanel.initialize(token: testToken, trackAutomaticEvents: true) mp2.minimumSessionDuration = 0; waitForTrackingQueue(mp2) XCTAssertEqual(eventQueue(token: mp2.apiToken).count, 0, "First app open should not be tracked again") diff --git a/MixpanelDemo/MixpanelDemoTests/MixpanelDemoTests.swift b/MixpanelDemo/MixpanelDemoTests/MixpanelDemoTests.swift index c4a5fda4..e18e5bb6 100644 --- a/MixpanelDemo/MixpanelDemoTests/MixpanelDemoTests.swift +++ b/MixpanelDemo/MixpanelDemoTests/MixpanelDemoTests.swift @@ -32,7 +32,7 @@ class MixpanelDemoTests: MixpanelBaseTests { } func testFlushEvents() { - let testMixpanel = Mixpanel.initialize(token: randomId(), flushInterval: 60) + let testMixpanel = Mixpanel.initialize(token: randomId(), flushInterval: 60, trackAutomaticEvents: true) testMixpanel.identify(distinctId: "d1") for i in 0..<50 { testMixpanel.track(event: "event \(i)") @@ -100,7 +100,7 @@ class MixpanelDemoTests: MixpanelBaseTests { } func testFlushQueueContainsCorruptedEvent() { - let testMixpanel = Mixpanel.initialize(token: randomId(), flushInterval: 60) + let testMixpanel = Mixpanel.initialize(token: randomId(), flushInterval: 60, trackAutomaticEvents: true) testMixpanel.trackingQueue.async { testMixpanel.mixpanelPersistence.saveEntity(["event": "bad event1", "properties": ["BadProp": Double.nan]], type: .events) testMixpanel.mixpanelPersistence.saveEntity(["event": "bad event2", "properties": ["BadProp": Float.nan]], type: .events) @@ -149,7 +149,7 @@ class MixpanelDemoTests: MixpanelBaseTests { } func testAddingEventsAfterFlush() { - let testMixpanel = Mixpanel.initialize(token: randomId(), flushInterval: 60) + let testMixpanel = Mixpanel.initialize(token: randomId(), flushInterval: 60, trackAutomaticEvents: true) for i in 0..<10 { testMixpanel.track(event: "event \(UInt(i))") } @@ -1026,7 +1026,7 @@ class MixpanelDemoTests: MixpanelBaseTests { let setProperties2 = group[1]["$set"] as! InternalProperties XCTAssertEqual(setProperties2["a"] as? Int, 1) XCTAssertTrue(MixpanelPersistence.loadOptOutStatusFlag(apiToken: token)!) - XCTAssertTrue(MixpanelPersistence.loadAutomacticEventsEnabledFlag(apiToken: token)) + XCTAssertTrue(MixpanelPersistence.loadAutomaticEventsEnabledFlag(apiToken: token)) //timedEvents let testTimedEvents = MixpanelPersistence.loadTimedEvents(apiToken: token) diff --git a/Sources/Decide.swift b/Sources/Decide.swift index 39702af2..6c699086 100644 --- a/Sources/Decide.swift +++ b/Sources/Decide.swift @@ -37,7 +37,7 @@ class Decide { } if let automaticEventsEnabled = result["automatic_events"] as? Bool { - MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: automaticEventsEnabled, fromDecide: true, apiToken: token) + MixpanelPersistence.saveAutomaticEventsEnabledFlag(value: automaticEventsEnabled, fromDecide: true, apiToken: token) } self.decideFetched = true diff --git a/Sources/MixpanelInstance.swift b/Sources/MixpanelInstance.swift index cd0f7f8a..fe5ddd17 100644 --- a/Sources/MixpanelInstance.swift +++ b/Sources/MixpanelInstance.swift @@ -84,7 +84,7 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele /// If this is not set, it will query the Autotrack settings from the Mixpanel server open var trackAutomaticEventsEnabled: Bool? { didSet { - MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: trackAutomaticEventsEnabled ?? false, + MixpanelPersistence.saveAutomaticEventsEnabledFlag(value: trackAutomaticEventsEnabled ?? false, fromDecide: false, apiToken: apiToken) } @@ -284,7 +284,7 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele } if let trackAutomaticEvents = trackAutomaticEvents { - MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: trackAutomaticEvents, + MixpanelPersistence.saveAutomaticEventsEnabledFlag(value: trackAutomaticEvents, fromDecide: false, apiToken: self.apiToken) } @@ -851,8 +851,11 @@ extension MixpanelInstance { return } - - let eventQueue = self.mixpanelPersistence.loadEntitiesInBatch(type: self.persistenceTypeFromFlushType(.events)) + // automatic events will NOT be flushed until one of the flags is non-nil + let eventQueue = self.mixpanelPersistence.loadEntitiesInBatch( + type: self.persistenceTypeFromFlushType(.events), + excludeAutomaticEvents: !MixpanelPersistence.automaticEventsFlagIsSet(apiToken: self.apiToken) + ) let peopleQueue = self.mixpanelPersistence.loadEntitiesInBatch(type: self.persistenceTypeFromFlushType(.people)) let groupsQueue = self.mixpanelPersistence.loadEntitiesInBatch(type: self.persistenceTypeFromFlushType(.groups)) @@ -1444,7 +1447,7 @@ extension MixpanelInstance { token: self.apiToken) self.trackingQueue.async { [weak self] in guard let self = self else { return } - if !MixpanelPersistence.loadAutomacticEventsEnabledFlag(apiToken: self.apiToken) { + if !MixpanelPersistence.loadAutomaticEventsEnabledFlag(apiToken: self.apiToken) { self.mixpanelPersistence.removeAutomaticEvents() } } diff --git a/Sources/MixpanelPersistence.swift b/Sources/MixpanelPersistence.swift index 634ea57c..7ec25f8a 100644 --- a/Sources/MixpanelPersistence.swift +++ b/Sources/MixpanelPersistence.swift @@ -81,8 +81,11 @@ class MixpanelPersistence { } } - func loadEntitiesInBatch(type: PersistenceType, batchSize: Int = Int.max, flag: Bool = false) -> [InternalProperties] { - let entities = mpdb.readRows(type, numRows: batchSize, flag: flag) + func loadEntitiesInBatch(type: PersistenceType, batchSize: Int = Int.max, flag: Bool = false, excludeAutomaticEvents: Bool = false) -> [InternalProperties] { + var entities = mpdb.readRows(type, numRows: batchSize, flag: flag) + if excludeAutomaticEvents && type == .events { + entities = entities.filter { !($0["event"] as! String).hasPrefix("$ae_") } + } let distinctId = MixpanelPersistence.loadIdentity(apiToken: apiToken).distinctID return entities.map { entityWithDistinctId($0, distinctId: distinctId) } @@ -134,7 +137,7 @@ class MixpanelPersistence { return defaults.object(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.optOutStatus)") as? Bool } - static func saveAutomacticEventsEnabledFlag(value: Bool, fromDecide: Bool, apiToken: String) { + static func saveAutomaticEventsEnabledFlag(value: Bool, fromDecide: Bool, apiToken: String) { guard let defaults = UserDefaults(suiteName: MixpanelUserDefaultsKeys.suiteName) else { return } @@ -147,7 +150,7 @@ class MixpanelPersistence { defaults.synchronize() } - static func loadAutomacticEventsEnabledFlag(apiToken: String) -> Bool { + static func loadAutomaticEventsEnabledFlag(apiToken: String) -> Bool { #if TV_AUTO_EVENTS return true #else @@ -167,6 +170,22 @@ class MixpanelPersistence { #endif } + static func automaticEventsFlagIsSet(apiToken: String) -> Bool { + #if TV_AUTO_EVENTS + return true + #else + let prefix = "\(MixpanelUserDefaultsKeys.prefix)-\(apiToken)-" + guard let defaults = UserDefaults(suiteName: MixpanelUserDefaultsKeys.suiteName) else { + return false // no user defaults at all + } + if defaults.object(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.automaticEventEnabled)") == nil && + defaults.object(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.automaticEventEnabledFromDecide)") == nil { + return false // neither flag is set + } + return true // at least one of the flags is set + #endif + } + static func saveTimedEvents(timedEvents: InternalProperties, apiToken: String) { guard let defaults = UserDefaults(suiteName: MixpanelUserDefaultsKeys.suiteName) else { return @@ -297,7 +316,7 @@ class MixpanelPersistence { MixpanelPersistence.saveOptOutStatusFlag(value: optOutFlag, apiToken: apiToken) } if let automaticEventsFlag = automaticEventsEnabled { - MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: automaticEventsFlag, fromDecide: false, apiToken: apiToken) + MixpanelPersistence.saveAutomaticEventsEnabledFlag(value: automaticEventsFlag, fromDecide: false, apiToken: apiToken) } return } diff --git a/Sources/Track.swift b/Sources/Track.swift index e622e294..1450e4a8 100644 --- a/Sources/Track.swift +++ b/Sources/Track.swift @@ -22,7 +22,7 @@ class Track { weak var mixpanelInstance: MixpanelInstance? var isAutomaticEventEnabled: Bool { - return MixpanelPersistence.loadAutomacticEventsEnabledFlag(apiToken: apiToken) + return MixpanelPersistence.loadAutomaticEventsEnabledFlag(apiToken: apiToken) } init(apiToken: String, lock: ReadWriteLock, metadata: SessionMetadata,