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

Check if automatic events flag is set before flushing #526

Merged
merged 11 commits into from
Mar 23, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -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(_:)),
Expand All @@ -78,15 +78,16 @@ 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)
}


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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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")
Expand Down
8 changes: 4 additions & 4 deletions MixpanelDemo/MixpanelDemoTests/MixpanelDemoTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)")
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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))")
}
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Sources/Decide.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 8 additions & 5 deletions Sources/MixpanelInstance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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()
}
}
Expand Down
29 changes: 24 additions & 5 deletions Sources/MixpanelPersistence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand Down Expand Up @@ -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
}
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Track.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down