Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions MixpanelDemo/MixpanelDemoTests/MixpanelAutomaticEventsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,150 @@ class MixpanelAutomaticEventsTests: MixpanelBaseTests {
removeDBfile(mp.apiToken)
removeDBfile(mp2.apiToken)
}

// MARK: - Tests for Deferred Automatic Event Initialization (SwiftUI Early Init Fix)

func testEnableAutomaticEventsAfterInitializationWithFalse() {
let testToken = randomId()
// Initialize with trackAutomaticEvents: false (simulating SwiftUI early init scenario)
let testMixpanel = Mixpanel.initialize(
token: testToken, trackAutomaticEvents: false, flushInterval: 60)
testMixpanel.minimumSessionDuration = 0

// At this point, no automatic events should be tracked
waitForTrackingQueue(testMixpanel)
XCTAssertTrue(
eventQueue(token: testMixpanel.apiToken).count == 0,
"No automatic events should be tracked when initialized with trackAutomaticEvents: false")

// Now enable automatic events (simulating the app UI being ready)
testMixpanel.trackAutomaticEventsEnabled = true
waitForTrackingQueue(testMixpanel)

// After enabling, first open event should be tracked
XCTAssertTrue(
eventQueue(token: testMixpanel.apiToken).count >= 1,
"First open event should be tracked after enabling automatic events")
let firstOpenEvent = eventQueue(token: testMixpanel.apiToken).first
XCTAssertEqual(
firstOpenEvent?["event"] as? String, "$ae_first_open",
"Should have first open event after re-initialization")

removeDBfile(testMixpanel.apiToken)
}

func testEnableAutomaticEventsMethodAfterInitializationWithFalse() {
let testToken = randomId()
let testMixpanel = Mixpanel.initialize(
token: testToken, trackAutomaticEvents: false, flushInterval: 60)
testMixpanel.minimumSessionDuration = 0

waitForTrackingQueue(testMixpanel)
XCTAssertTrue(
eventQueue(token: testMixpanel.apiToken).count == 0,
"No automatic events should be tracked initially")

// Use the explicit enableAutomaticEvents() method
testMixpanel.enableAutomaticEvents()
waitForTrackingQueue(testMixpanel)

// Verify automatic events are now enabled
XCTAssertTrue(
testMixpanel.trackAutomaticEventsEnabled,
"trackAutomaticEventsEnabled should be true after calling enableAutomaticEvents()")
XCTAssertTrue(
eventQueue(token: testMixpanel.apiToken).count >= 1,
"First open event should be tracked after calling enableAutomaticEvents()")

removeDBfile(testMixpanel.apiToken)
}

func testSessionTrackingAfterDeferredAutomaticEventsEnable() {
let testToken = randomId()
let testMixpanel = Mixpanel.initialize(
token: testToken, trackAutomaticEvents: false, flushInterval: 60)
testMixpanel.minimumSessionDuration = 0

// Enable automatic events after initial setup
testMixpanel.enableAutomaticEvents()
waitForTrackingQueue(testMixpanel)

// Clear previous events (first open)
let countBefore = eventQueue(token: testMixpanel.apiToken).count

// Simulate app resignation
testMixpanel.automaticEvents.perform(
#selector(AutomaticEvents.appWillResignActive(_:)),
with: Notification(name: Notification.Name(rawValue: "test")))
waitForTrackingQueue(testMixpanel)

// Session event should be tracked
let countAfter = eventQueue(token: testMixpanel.apiToken).count
XCTAssertTrue(
countAfter > countBefore,
"Session event should be tracked after enabling deferred automatic events")

let sessionEvent = eventQueue(token: testMixpanel.apiToken).last
XCTAssertEqual(
sessionEvent?["event"] as? String, "$ae_session",
"Should track session event after deferred enable")

removeDBfile(testMixpanel.apiToken)
}

func testMultipleDeferredEnablesAreIdempotent() {
let testToken = randomId()
let testMixpanel = Mixpanel.initialize(
token: testToken, trackAutomaticEvents: false, flushInterval: 60)
testMixpanel.minimumSessionDuration = 0

// Enable multiple times
testMixpanel.trackAutomaticEventsEnabled = true
waitForTrackingQueue(testMixpanel)
let countAfterFirstEnable = eventQueue(token: testMixpanel.apiToken).count

testMixpanel.trackAutomaticEventsEnabled = true
waitForTrackingQueue(testMixpanel)
let countAfterSecondEnable = eventQueue(token: testMixpanel.apiToken).count

testMixpanel.enableAutomaticEvents()
waitForTrackingQueue(testMixpanel)
let countAfterThirdEnable = eventQueue(token: testMixpanel.apiToken).count

// The first event count should remain the same despite multiple enables
XCTAssertEqual(
countAfterFirstEnable, countAfterSecondEnable,
"Second enable should not duplicate first open event")
XCTAssertEqual(
countAfterSecondEnable, countAfterThirdEnable,
"Third enable should not duplicate first open event")

removeDBfile(testMixpanel.apiToken)
}

func testDisablingAndReEnablingAutomaticEvents() {
let testToken = randomId()
let testMixpanel = Mixpanel.initialize(
token: testToken, trackAutomaticEvents: true, flushInterval: 60)
testMixpanel.minimumSessionDuration = 0

waitForTrackingQueue(testMixpanel)
let initialEventCount = eventQueue(token: testMixpanel.apiToken).count
XCTAssertTrue(
initialEventCount > 0, "Should have first open event initially")

// Disable automatic events
testMixpanel.trackAutomaticEventsEnabled = false
XCTAssertFalse(
testMixpanel.trackAutomaticEventsEnabled,
"Should be disabled after setting to false")

// Re-enable automatic events
testMixpanel.trackAutomaticEventsEnabled = true
XCTAssertTrue(
testMixpanel.trackAutomaticEventsEnabled,
"Should be re-enabled after setting to true")

removeDBfile(testMixpanel.apiToken)
}
}
27 changes: 24 additions & 3 deletions Sources/MixpanelInstance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,30 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele
/// Controls whether to show spinning network activity indicator when flushing
/// data to the Mixpanel servers. Defaults to true.
open var showNetworkActivityIndicator = true

private var _trackAutomaticEventsEnabled: Bool
/// This allows enabling or disabling collecting common mobile events,
open var trackAutomaticEventsEnabled: Bool
open var trackAutomaticEventsEnabled: Bool {
get {
return _trackAutomaticEventsEnabled
}
set {
_trackAutomaticEventsEnabled = newValue
#if os(iOS) || os(tvOS) || os(visionOS)
if newValue && !MixpanelInstance.isiOSAppExtension() {
// Re-initialize automatic event tracking if being enabled
automaticEvents.delegate = self
automaticEvents.initializeEvents(instanceName: self.name)
}
#endif
}
}

#if os(iOS) || os(tvOS) || os(visionOS)
/// Enables automatic event tracking for the Mixpanel instance.
public func enableAutomaticEvents() {
self.trackAutomaticEventsEnabled = true
}
#endif

/// Flush timer's interval.
/// Setting a flush interval of 0 will turn off the flush timer and you need to call the flush() API manually
Expand Down Expand Up @@ -366,7 +387,7 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele
if let apiToken = apiToken, !apiToken.isEmpty {
self.apiToken = apiToken
}
trackAutomaticEventsEnabled = trackAutomaticEvents
_trackAutomaticEventsEnabled = trackAutomaticEvents
if let serverURL = serverURL {
self.serverURL = serverURL
}
Expand Down