Commit graph

245 commits

Author SHA1 Message Date
ketanmixpanel
5aa52257c1
Fix feature flags and track events race condition (#715)
* added fix for the race FF condition

* removed unused initilizers

* passed the distinctId to the recordFirstTimeEvent

* Update Sources/FeatureFlags.swift

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update Sources/FeatureFlags.swift

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: avoid trackingQueue.sync from main thread in async flag paths and fix doc comments

Agent-Logs-Url: https://github.com/mixpanel/mixpanel-swift/sessions/035f0c40-e3d9-4629-a0f5-e52a16a531a6

Co-authored-by: ketanmixpanel <188901560+ketanmixpanel@users.noreply.github.com>

* fix: align getAllVariantsSync doc comment to use 'may block' for consistency

Agent-Logs-Url: https://github.com/mixpanel/mixpanel-swift/sessions/035f0c40-e3d9-4629-a0f5-e52a16a531a6

Co-authored-by: ketanmixpanel <188901560+ketanmixpanel@users.noreply.github.com>

* fixed review feedback

* removed queue scheduling

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ketanmixpanel <188901560+ketanmixpanel@users.noreply.github.com>
2026-04-17 22:07:58 +05:30
Santi Gracia
22cdbed76e
Defer automatic events initialization - Fix SwiftUI global accent color override when initializing Mixpanel with automatic events (#712)
* Defer automatic events initialization

Delay automaticEvents.initializeEvents by dispatching to DispatchQueue.main.async to run on the next run loop iteration. This avoids interfering with SwiftUI's accent color setup when Mixpanel.initialize() is called from a SwiftUI App's init (see #522). Uses a weak self capture to avoid retain cycles.

* Drain main run loop before tracking queue

Call waitForAsyncTasks() at the start of waitForTrackingQueue to drain the main run loop so any deferred initialization (e.g. automatic events) is dispatched to the tracking queue before synchronizing. Adds a clarifying comment and prevents races in tests that rely on tracking/ network queue state.
- Fix for copilot review comment on pull/712

* Fix for tests in PR

drains the main run loop only once per test right after init, ensuring initializeEvents() has dispatched before the test proceeds
2026-04-17 00:09:18 +05:30
ketanmixpanel
3ad0fb8221
Integrate common SDK to use the Event bridge (#709)
* added code for communcation bridge

* removed the excess logging

* removed the MixpanelBridgeEvent class

* updated eventBridge code

* moved notifyListeners to track call

* use AsyncStream

* Merge branch 'master' into feature/communcation-bridge

* moved the bridge code to shared module

* Update Package.swift

* Use mixpanel-swift-common package

Replace mixpanel-swift-shared with mixpanel-swift-common across the project. Updated Package.swift (dependency URL and product name), changed import in Sources/Track.swift, and modified Mixpanel.xcodeproj entries to reference MixpanelSwiftCommon (XCRemoteSwiftPackageReference, XCSwiftPackageProductDependency, framework entries and groups). Also bumped the package requirement to upToNextMajor with minimumVersion 2.0.0.

* Update Package.swift

* Update Package.swift

* Lower mixpanel-swift-common min version and remove duplicate

Reduce the XCRemoteSwiftPackageReference minimumVersion for mixpanel-swift-common from 2.0.0 to 1.0.0 in Mixpanel.xcodeproj to allow older compatible releases. Also remove a duplicated flagManager.checkFirstTimeEvents(...) block in Sources/Track.swift to prevent the same first-time event check from running twice.

* Add MixpanelEventBridgeTests covering bridge notification on track and opt-out behavior

Agent-Logs-Url: https://github.com/mixpanel/mixpanel-swift/sessions/fa45ba33-c2bf-4811-9e91-829afdc858bf

Co-authored-by: ketanmixpanel <188901560+ketanmixpanel@users.noreply.github.com>

* Add MixpanelEventBridgeTests.swift to Mac test directory

Agent-Logs-Url: https://github.com/mixpanel/mixpanel-swift/sessions/fa45ba33-c2bf-4811-9e91-829afdc858bf

Co-authored-by: ketanmixpanel <188901560+ketanmixpanel@users.noreply.github.com>

* Add MixpanelEventBridgeTests.swift to tvOS test target

Agent-Logs-Url: https://github.com/mixpanel/mixpanel-swift/sessions/47b76ca2-86a7-4c28-9265-b72264c0d548

Co-authored-by: ketanmixpanel <188901560+ketanmixpanel@users.noreply.github.com>

* Update MixpanelEventBridgeTests.swift

* added pod dependency

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ketanmixpanel <188901560+ketanmixpanel@users.noreply.github.com>
2026-04-10 20:14:33 +05:30
Mark Siebert
b00c302a44
Swift: Add OpenFeature support to MixpanelFlags protocol (#701)
* Add OpenFeature provider

Implement an OpenFeature provider for the Mixpanel Swift SDK as a
separate SPM target (MixpanelOpenFeature). The provider wraps the
MixpanelFlags protocol and bridges flag evaluations to the OpenFeature
FeatureProvider interface.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix macOS tests to use renamed instanceName parameter

The MixpanelPersistence API was renamed from token/apiToken to
instanceName to support multiple instances under the same token,
but the macOS test files were never updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add missing OpenFeature provider tests for consistency across SDKs

Adds context no-op verification, variant key passthrough, SDK exception
handling, and empty variant key edge case tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Implement OpenFeature context forwarding in initialize and onContextSet

Add setContext method to MixpanelFlags protocol and FeatureFlagManager to
support dynamic context updates with flag re-fetch. Update the OpenFeature
provider to forward evaluation context during initialize() and
onContextSet() using replace mode, awaiting flag fetch completion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add GENERAL error handling and normalize targetingKey

Wrap resolve() in do/catch to convert unexpected errors to
OpenFeatureError.generalError. Rename targeting_key to targetingKey
(camelCase) in context forwarding for cross-SDK consistency per spec.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Simplify OpenFeature provider and remove dead code

Remove unnecessary do/catch wrapper with unreachable dead code in
resolve method. Remove ~50-line duplicate mock class. De-duplicate
completion handler append in _fetchFlagsIfNeeded. Remove unnecessary
weak self captures. Fix redundant nil-coalescing in test base classes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* improved update context variable storage

* Return defaults with error codes instead of throwing exceptions

Evaluators now catch errors from resolve() and return default values
with specific error codes (.providerNotReady, .flagNotFound, .typeMismatch)
instead of throwing exceptions. This provides a more graceful experience
when flags are accessed before they are ready.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use ERROR reason code for failed evaluations to match JS provider

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove OpenFeature provider (moved to mixpanel-swift-openfeature)

The OpenFeature provider has been extracted into its own standalone
package at https://github.com/mixpanel/mixpanel-swift-openfeature.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix flag context not loaded from options on init

The init(serverURL:) initializer sets flagContext to empty, and
MixpanelInstance sets the delegate afterward. The context from
FeatureFlagOptions was never populated into flagContext, causing
the first flag fetch to send no context and receive empty flags.

Fix by populating flagContext from the delegate's options in a
didSet observer on the delegate property.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert platform version requirements raised for OpenFeature provider

The OpenFeature provider has been moved to its own repo
(mixpanel-swift-openfeature), so the platform minimums can return to
their original values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 22:03:16 +05:30
Tyler Roach
2fecbcc4be
feat: OSX Automatic Event Tracking (#706)
* OSX Automatic Event Tracking

* fix tests
2026-04-02 08:49:37 -04:00
Mark Siebert
01df5e3516
Add loadFlags(completion:) callback variant (#700)
* Add loadFlags(completion:) to support async flag loading callbacks

Adds a completion handler variant of loadFlags() to the MixpanelFlags
protocol and FeatureFlagManager implementation. The completion is called
with true on success and false on failure. The existing no-arg loadFlags()
now delegates to loadFlags(completion: nil).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add loadFlags(completion:) to MockMixpanelFlags for protocol conformance

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 10:13:58 -07:00
Mark Siebert
d1fd4318d4
First time event targeting (#685)
* support for first time event detection
2026-03-13 14:32:42 -07:00
efahk
7da924f18e
Add support to get all feature flag assignments (#697)
* Add support to get all feature flag assignments

* Fix test class conformity

* cleanup

---------

Co-authored-by: Kwame Efah <kwameefah@h7d9gc72q4-kwameefah.corp.mixpanel.com>
2026-03-04 12:08:34 -08:00
Mark Siebert
84f5aa84d1
Add FlagOptions struct for granular feature flag configuration (#699)
* Add FlagOptions with loadOnFirstForeground to fix initialize/identify race

When calling Mixpanel.initialize immediately followed by identify, both
can trigger concurrent flag fetches causing inconsistent flag evaluation.

This adds a FlagOptions struct with a loadOnFirstForeground option that
lets customers disable the automatic flag load at init time, call
identify first, then manually trigger loadFlags(). The new flagsOptions
parameter on MixpanelOptions groups flag config together while keeping
full backward compatibility with existing featureFlagsEnabled and
featureFlagsContext parameters.

Usage:
  let options = MixpanelOptions(
      token: "TOKEN",
      flagsOptions: FlagOptions(enabled: true, loadOnFirstForeground: false)
  )
  let mp = Mixpanel.initialize(options: options)
  mp.identify(distinctId: "user123")
  mp.flags.loadFlags()

https://claude.ai/code/session_01234twgpsuvC2SP67zKDNru

* Clarify FlagOptions docs: identify() already triggers loadFlags()

identify() calls loadFlags() internally when the distinctId changes,
so an explicit loadFlags() call is only needed when re-identifying
with the same persisted distinctId.

https://claude.ai/code/session_01234twgpsuvC2SP67zKDNru

* deprecate old options

* test update

* Fix CI test failures: replace timer waits with predicate expectations

Timer-based waits (0.2-0.3s) expire before .utility QoS dispatches
complete on resource-constrained CI runners. Replace with
XCTNSPredicateExpectation that polls observable state and fulfills
immediately when ready, with 10s timeout for CI headroom.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* rename fields

* rename to featureFlagOptions

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-04 11:22:47 -08:00
Jared McFarland
de73100ee3
Add deviceIdProvider closure for custom device ID generation (#692)
* Add deviceIdProvider closure for custom device ID generation

Adds a `deviceIdProvider: (() -> String)?` property to MixpanelOptions
that allows customers to supply their own device ID generation logic.

Key behaviors:
- Provider called on init (if no persisted identity) and after reset()/optOutTracking()
- Return same value = persistent device ID (never resets)
- Return different value = ephemeral device ID (resets each time)
- SDK logs warning when provider would replace existing persisted anonymousId
- Empty string from provider falls back to default UUID/IDFV behavior

This gives customers full control over device ID persistence and reset
semantics without needing separate parameters on reset() and optOutTracking().

Includes:
- 13 unit tests covering all behaviors
- Comprehensive documentation in docs/device-id-provider.md

* Address PR review feedback for deviceIdProvider

- Change log level from warn to error for identity mismatch
- Use defaultDeviceId() in unarchive to avoid calling provider twice
- Add whitespace trimming to provider result
- Update documentation table with "Value Used?" column
- Add testIdentifyWithDeviceIdProvider test case

* Add deviceIdProvider QA helpers to MixpanelDemo

- Add persistent and ephemeral device ID provider examples in AppDelegate
- Enhance Reset button with before/after console logging
- Default to no provider (standard SDK behavior) for production

* Make deviceIdProvider return optional String for graceful error handling

Change type from `(() -> String)?` to `(() -> String?)?` so customers can
return nil to signal "use SDK default" when their device ID source fails.

- Update MixpanelOptions type signature and documentation
- Handle optional return in defaultDeviceId() with nil-coalescing
- Update warning message to mention nil or empty string
- Add failingDeviceIdProvider test option in demo app
- Add testProviderReturningNilFallsBackToDefault test

* Add Original ID Merge compatibility note to deviceIdProvider docs

Document the footgun with persistent device IDs + Original ID Merge
on shared devices, where createAlias() can incorrectly merge
identities of different users.

* Add thread-safety warning to deviceIdProvider documentation

The provider closure is called while holding internal locks, so blocking
operations (Keychain, network, UserDefaults) can cause deadlocks. Update
docs and examples to recommend caching the device ID at app launch and
returning the cached value from the provider.

- Add Thread Safety note to MixpanelOptions.swift doc comment
- Update docs/device-id-provider.md with caching pattern
- Revise Best Practices to emphasize non-blocking providers
- Update MixpanelDemo to demonstrate caching pattern
2026-02-06 11:35:58 +05:30
Kwame Efah
99223d3d80
Plumb additional flags properties through to exposure event (#678)
* Plumb additional flags properties through to exposure event

* Add experiment ID decoding and tests

* Update tracking prop keys

---------

Co-authored-by: Kwame Efah <kwameefah@h7d9gc72q4-kwameefah.corp.mixpanel.com>
2025-09-24 15:19:33 -07:00
Mark Siebert
3bc59ca391
use HTTP GET for feature flags (#677)
* use HTTP GET for feature flags

* use iPhone 16 Pro for CI

---------

Co-authored-by: Mark Siebert <1504059+msiebert@users.noreply.github.com>
2025-09-17 13:50:23 -07:00
Copilot
8833248376
Add device_id as an automatic context value for Feature Flags (#675)
* Initial plan

* Add device_id as automatic context value for Feature Flags

Co-authored-by: msiebert <1504059+msiebert@users.noreply.github.com>

* Add timeLastFetched and fetchLatencyMs tracking properties to $experiment_started events

Co-authored-by: msiebert <1504059+msiebert@users.noreply.github.com>

* Fix type mismatch in timeLastFetched and fetchLatencyMs properties

Co-authored-by: msiebert <1504059+msiebert@users.noreply.github.com>

* Fix private access level for timeLastFetched and fetchLatencyMs properties

Co-authored-by: msiebert <1504059+msiebert@users.noreply.github.com>

* fix concurrency bug

* determine if sync is needed when tracking

* add more tests

* tweak test

* prevent potential deadlock, simplify tracking threading

* add timing properties sanity test

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: msiebert <1504059+msiebert@users.noreply.github.com>
Co-authored-by: Mark Siebert <mark.siebert@mixpanel.com>
Co-authored-by: Jared McFarland <jared@mixpanel.com>
2025-08-06 12:12:43 -07:00
Jared McFarland
e8c8783d94
swift-format (#672) 2025-05-29 17:45:08 -07:00
Jared McFarland
330b471fc5
Initial support for Feature Flags (#670)
* initial feature flag support

* tests and tweaks

* add new sources to podspec

* fix config init

* tests and cleanup

* pass to AnalyticsMessages and add to mixpaneldemo

* fix test

* rename APIs

* more renaming

* fix demo app delegate

* fix tests

* update podspec

* revert demo app changes

* one more config -> options rename

* nope... more config->options renaming

* modify enabled method names and load flags on identify

* Update Sources/MixpanelPersistence.swift

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix response parser function test

* update README with DeepWiki badge

* tweak README

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-28 13:49:38 -07:00
Santi Gracia
a8dd5ffd88
Adding optional gzip compression for /track (#653)
* Adding optional gzip compression for /track

* Logger->MixpanelLogger

* safe unwrap in gzipCompressed

---------

Co-authored-by: Jared McFarland <jared@mixpanel.com>
2025-03-31 16:34:07 -07:00
Jared McFarland
d076b5d91e fix tests 2025-02-10 16:06:19 -08:00
Jared McFarland
028b5eee7b more renaming 2025-01-30 12:52:14 -08:00
Jared McFarland
650df0ba34 rename in MixpanelDemo LoggerTests 2025-01-30 12:49:04 -08:00
Jared McFarland
4b58a6cb7f Revert "rename with MP prefix to avoid namespace clashes"
This reverts commit 348574216b.
2025-01-30 12:38:50 -08:00
Jared McFarland
348574216b rename with MP prefix to avoid namespace clashes 2025-01-30 12:19:36 -08:00
Zihe Jia
471f69cd2e temporarily remove the lint 2024-05-03 16:46:07 -07:00
Jared McFarland
324d59ff7f add privacy manifest 2024-03-14 16:14:38 -07:00
Zihe Jia
618d2456eb fix CI 2024-01-17 14:18:26 -08:00
Zihe Jia
b0fbb6fd10 add test for the max batch size 50 enforcement 2023-11-09 16:56:35 -08:00
Zihe Jia
4f222e4541 add new property flushBatchSize for fine tuning the network request 2023-11-09 16:41:33 -08:00
Zihe Jia
b06b0a8477 fix flaky test 2023-06-15 19:50:29 -07:00
Jared McFarland
d492d887cb fix warnings 2023-05-04 10:30:43 -07:00
deepak neralla
edd030174d identity v3 changes for swift sdk 2023-02-17 13:53:57 -08:00
Jared McFarland
d02177702e add to comment 2022-10-26 16:24:17 -07:00
Jared McFarland
6ae0a0a8cb add a comment to test 2022-10-26 16:20:02 -07:00
Jared McFarland
f0893e0fc9 update testMPDB 2022-10-26 16:13:57 -07:00
Jared McFarland
1d030d1e2e revert appdelegate changes 2022-10-26 14:49:01 -07:00
Jared McFarland
d1220a1f4c use CharacterSet.alphanumerics 2022-10-26 14:48:13 -07:00
Jared McFarland
db92974f8b dont initialize AutomaticEvents if trackAutomaticEvents is false 2022-09-09 10:27:36 -07:00
Jared McFarland
cca52a2f38 fix tests 2022-07-06 12:11:00 -07:00
Jared McFarland
e5225512fc
Merge branch 'master' into jared-deprecate-decide 2022-07-06 12:01:39 -07:00
Zihe Jia
aa7c38efe6 add support for mulitple instances under the same token 2022-07-01 17:44:47 -07:00
Zihe Jia
71c4db0258 add tests 2022-06-27 13:11:13 -07:00
Zihe Jia
7c6d58e1cc add option for 'createAlias' for not calling identify 2022-06-24 17:05:15 -07:00
Jared McFarland
cc18259319 remove trackAutomaticEvents from user defaults 2022-06-22 12:43:36 -07:00
Jared McFarland
4f916a4a8b change default 2022-06-22 12:14:05 -07:00
Jared McFarland
ddebd2de7d fix tests 2022-06-22 12:09:05 -07:00
Jared McFarland
a9600054f5 remove decide and make trackAutomaticEvents required 2022-06-22 11:27:48 -07:00
Zihe Jia
4fe7326df6 add DevX survey to the demo app 2022-04-29 11:56:14 -07:00
Zihe Jia
f581143204 Add SDK internal tracking 2022-04-28 15:49:07 -07:00
Jared McFarland
e3d17d30f4 change test name 2022-03-17 15:15:10 -07:00
Jared McFarland
f57b2824bb fix tests 2022-03-15 14:57:14 -07:00
Jared McFarland
cbfd64cc96 revert app delegate 2022-03-15 13:43:59 -07:00
Jared McFarland
888ff2a4aa small refactor 2022-03-15 13:43:06 -07:00