Skip to content
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c9e1231
Add comprehensive Feature Flags support to Mixpanel React Native SDK
jaredmixpanel Oct 22, 2025
8626278
Fix CI failures for Feature Flags implementation
jaredmixpanel Oct 22, 2025
3797aa8
Fix static init method to pass all required parameters
jaredmixpanel Oct 22, 2025
dfcdf1c
Address GitHub Copilot review feedback
jaredmixpanel Oct 22, 2025
b7c4612
Fix remaining CI failures for native Feature Flags implementation
jaredmixpanel Oct 22, 2025
5e97ff6
Fix Android getInstance signature and add missing WritableArray import
jaredmixpanel Oct 22, 2025
edea4c3
Fix iOS MixpanelOptions to use full constructor with all parameters
jaredmixpanel Oct 22, 2025
ff4f5b9
update test ios ci workflow
jaredmixpanel Oct 23, 2025
ee2118d
update test ios ci workflow again
jaredmixpanel Oct 23, 2025
7592848
Add comprehensive Feature Flags test suite and fix iOS initialization
jaredmixpanel Oct 23, 2025
473122b
Fix Feature Flags test suite - all 48 tests passing
jaredmixpanel Oct 23, 2025
75553c3
jest setup and packag-lock
jaredmixpanel Oct 23, 2025
a6327e1
Address GitHub Copilot review feedback
jaredmixpanel Oct 23, 2025
f831462
Fix Android loadFlags to use 3-parameter getInstance
jaredmixpanel Oct 23, 2025
6be34fc
Merge remote-tracking branch 'origin/master' into feature/add-feature…
jaredmixpanel Nov 5, 2025
ea604e0
add MixpanelStarter sample app, refactor and remove JS imp
jaredmixpanel Nov 6, 2025
679c3a2
add MixpanelStarter sample app, refactor and remove JS imp
jaredmixpanel Nov 6, 2025
e9c5af1
js flags ref
jaredmixpanel Nov 6, 2025
d704d64
put JS code back, but block it from non-native mode
jaredmixpanel Nov 6, 2025
310471c
add flags files
jaredmixpanel Nov 6, 2025
07f3d34
use macos-15-intel runner for test_android
jaredmixpanel Nov 6, 2025
ad38cb1
only call loadFlags in native mode and tweak Swift logs
jaredmixpanel Nov 6, 2025
fea7ed3
Apply suggestions from code review
jaredmixpanel Nov 6, 2025
4a99491
demo token
jaredmixpanel Nov 6, 2025
0d114d6
Update __tests__/flags.test.js
jaredmixpanel Nov 6, 2025
5235d6b
Fix flags endpoint to match mixpanel-js implementation
jaredmixpanel Nov 6, 2025
038c298
3.2.0-beta.0
jaredmixpanel Nov 7, 2025
dc85b34
Update .npmignore to reduce package size from 41MB to 316KB
jaredmixpanel Nov 7, 2025
1e66d73
3.2.0-beta.1
jaredmixpanel Nov 7, 2025
c762c68
add and generate docs
jaredmixpanel Nov 7, 2025
a3a815e
3.2.0-beta.2
jaredmixpanel Nov 7, 2025
39b5a73
update FF quick guide
jaredmixpanel Nov 7, 2025
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
32 changes: 24 additions & 8 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,54 +27,47 @@
- run: npm test

test_android:
runs-on: ubuntu-latest
runs-on: macos-15-intel
strategy:
matrix:
node-version: [18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- uses: actions/setup-java@v4
with:
distribution: "temurin" # See 'Supported distributions' for available options
java-version: "17"
- run: npm install
- run: npm install -g react-native-cli
- run: npm i react-native-gradle-plugin
- name: Test Integration - Install dependencies
working-directory: ./Samples/SimpleMixpanel
run: yarn install
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Setup Android
uses: reactivecircus/android-emulator-runner@v2.32.0
with:
api-level: 34
target: google_apis
profile: Nexus 5X
arch: x86_64
working-directory: ./Samples/SimpleMixpanel/android
script: ./gradlew
- name: Test Android
uses: reactivecircus/android-emulator-runner@v2.32.0
with:
api-level: 34
target: google_apis
profile: Nexus 5X
arch: x86_64
working-directory: ./Samples/SimpleMixpanel
script: react-native run-android

test_ios:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
runs-on: macos-latest
strategy:
matrix:
Expand All @@ -95,6 +88,29 @@
- name: Setup iOS
working-directory: ./Samples/SimpleMixpanel/ios
run: pod install --repo-update
- name: List available simulators
run: xcrun simctl list devices available
- name: Boot iOS Simulator
run: |
# Get list of available iPhone simulators
# Note: jq is pre-installed on macOS GitHub Actions runners
SIMULATOR_ID=$(xcrun simctl list devices available -j | jq -r '.devices | to_entries[] | .value[] | select(.name | contains("iPhone")) | .udid' | head -n 1)
if [ -z "$SIMULATOR_ID" ]; then
echo "Error: No iPhone simulator found"
exit 1
fi
echo "Found simulator: $SIMULATOR_ID"
# Check if simulator is already booted
DEVICE_LIST=$(xcrun simctl list devices)
if echo "$DEVICE_LIST" | grep -q "$SIMULATOR_ID.*Booted"; then
echo "Simulator already booted"
else
echo "Booting simulator..."
xcrun simctl boot "$SIMULATOR_ID"
fi
# Verify simulator is booted
echo "Booted simulators:"
xcrun simctl list devices | grep Booted
- name: Test iOS
working-directory: ./Samples/SimpleMixpanel
run: react-native run-ios
2 changes: 1 addition & 1 deletion MixpanelReactNative.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ Pod::Spec.new do |s|
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }

s.dependency "React-Core"
s.dependency "Mixpanel-swift", '5.1.0'
s.dependency "Mixpanel-swift", '5.1.3'
end
58 changes: 53 additions & 5 deletions Samples/MixpanelStarter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This sample app bridges the gap between basic "Hello World" examples and complex

## ✨ Features Demonstrated

This app showcases 8 core Mixpanel SDK capabilities:
This app showcases 9 core Mixpanel SDK capabilities:

| Feature | Description | Screen |
|---------|-------------|--------|
Expand All @@ -24,17 +24,19 @@ This app showcases 8 core Mixpanel SDK capabilities:
| 📊 **Event Tracking** | `track()` with custom properties and metadata | Home |
| ⏱️ **Timed Events** | `timeEvent()` for automatic duration tracking | Home |
| 🌐 **Super Properties** | `registerSuperProperties()` for global context | Home |
| 🚩 **Feature Flags** | `flags.loadFlags()`, `isEnabled()`, dynamic feature control - **FULL INTEGRATION TEST SUITE** | Feature Flags |
| 🔒 **Privacy Controls** | `optIn/OutTracking()` for GDPR compliance | Settings |
| 🗑️ **Data Management** | `reset()` for logout/data deletion | Settings |
| 🚀 **Manual Flush** | `flush()` to force send queued events | Settings |

## 📱 App Structure

The app has 3 tabs:
The app has 4 tabs:

1. **Onboarding (User ID Tab)**: Demonstrates user identification lifecycle
2. **Home (Events Tab)**: Shows event tracking and super properties
3. **Settings**: Privacy controls and data management
3. **Feature Flags**: **Comprehensive integration test suite** - exercises all 8 public API methods, Promise/Callback patterns, edge cases, and type coercion
4. **Settings**: Privacy controls and data management

## 🚀 Quick Start

Expand Down Expand Up @@ -83,13 +85,18 @@ MixpanelStarter/
│ ├── screens/
│ │ ├── OnboardingScreen.tsx # User identification demos
│ │ ├── HomeScreen.tsx # Event tracking patterns
│ │ ├── FeatureFlagsScreen.tsx # Feature flags integration tests
│ │ └── SettingsScreen.tsx # Privacy & data management
│ ├── components/
│ │ ├── ActionButton.tsx # Reusable button component
│ │ ├── InfoCard.tsx # Info display card
│ │ ├── FlagCard.tsx # Flag display component
│ │ ├── TestResultDisplay.tsx # Test results visualization
│ │ ├── EventTrackingLog.tsx # Event history component
│ │ └── ErrorBoundary.tsx # Error handling wrapper
│ ├── types/
│ │ └── mixpanel.types.ts # TypeScript definitions
│ │ ├── mixpanel.types.ts # TypeScript definitions
│ │ └── flags.types.ts # Feature flags test types
│ ├── constants/
│ │ └── tracking.ts # Event names & properties
│ └── App.tsx # Navigation setup
Expand Down Expand Up @@ -169,7 +176,48 @@ mixpanel.registerSuperProperties({

**Why?** Eliminates repetitive property passing. Perfect for user preferences and app state.

### 5. GDPR-Compliant Logout
### 5. Feature Flags for Dynamic Control

```typescript
// Enable feature flags during initialization
await mixpanel.init(false, {}, undefined, false, {
enabled: true,
});

// Load flags from Mixpanel
await mixpanel.flags.loadFlags();

// Check if flags are ready
const ready = mixpanel.flags.areFlagsReady();

// Synchronous flag evaluation (fast, uses cached values)
const isEnabled = mixpanel.flags.isEnabledSync('feature-key', false);
const value = mixpanel.flags.getVariantValueSync('feature-key', 'default');

// Asynchronous flag evaluation (ensures latest values)
const enabled = await mixpanel.flags.isEnabled('feature-key', false);
const variant = await mixpanel.flags.getVariant('feature-key', {
key: 'feature-key',
value: null,
});

```

**Why?** Enables remote feature control, A/B testing, and gradual rollouts without app updates. Perfect for experimentation and user segmentation.

#### Feature Flags Testing Screen

The Feature Flags screen is a **full integration test suite** with:

- **4 Test Modes**: Sync, Async (Promise), Edge Cases, Type Coercion
- **12 Pre-configured Flags**: Boolean gates, string experiments, dynamic configs
- **Real-time Results**: Execution time, type detection, fallback tracking
- **Event Monitoring**: Live `$experiment_started` event tracking
- **All API Methods**: getVariantSync, getVariantValueSync, isEnabledSync, getVariant, getVariantValue, isEnabled (both Promise and Callback patterns)

Use this screen during development to verify Feature Flags functionality!

### 6. GDPR-Compliant Logout

```typescript
// Before logout
Expand Down
18 changes: 12 additions & 6 deletions Samples/MixpanelStarter/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ PODS:
- hermes-engine (0.82.1):
- hermes-engine/Pre-built (= 0.82.1)
- hermes-engine/Pre-built (0.82.1)
- Mixpanel-swift (5.1.0):
- Mixpanel-swift/Complete (= 5.1.0)
- Mixpanel-swift/Complete (5.1.0)
- Mixpanel-swift (5.1.3):
- Mixpanel-swift/Complete (= 5.1.3)
- Mixpanel-swift/Complete (5.1.3)
- MixpanelReactNative (3.1.2):
- Mixpanel-swift (= 5.1.0)
- Mixpanel-swift (= 5.1.3)
- React-Core
- RCT-Folly (2024.11.18.00):
- boost
Expand Down Expand Up @@ -1788,6 +1788,8 @@ PODS:
- React-RCTFBReactNativeSpec
- ReactCommon/turbomodule/core
- SocketRocket
- react-native-get-random-values (1.11.0):
- React-Core
- react-native-safe-area-context (5.6.2):
- boost
- DoubleConversion
Expand Down Expand Up @@ -2547,6 +2549,7 @@ DEPENDENCIES:
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-oscompat (from `../node_modules/react-native/ReactCommon/oscompat`)
Expand Down Expand Up @@ -2674,6 +2677,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
React-microtasksnativemodule:
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
react-native-get-random-values:
:path: "../node_modules/react-native-get-random-values"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
React-NativeModulesApple:
Expand Down Expand Up @@ -2755,8 +2760,8 @@ SPEC CHECKSUMS:
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
hermes-engine: 273e30e7fb618279934b0b95ffab60ecedb7acf5
Mixpanel-swift: 7b26468fc0e2e521104e51d65c4bbf7cab8162f8
MixpanelReactNative: b523d54778e31e396c55df6f732173f3819e58ee
Mixpanel-swift: 630484a1b61cb90820aa34492798d542d9f1d0f1
MixpanelReactNative: 30f767dd9aab033e24821454874afaa35c20e293
RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f
RCTDeprecation: f17e2ebc07876ca9ab8eb6e4b0a4e4647497ae3a
RCTRequired: e2c574c1b45231f7efb0834936bd609d75072b63
Expand Down Expand Up @@ -2790,6 +2795,7 @@ SPEC CHECKSUMS:
React-logger: fceaaedb9c715923a1900af68a7534e9b3a601a1
React-Mapbuffer: 7e7ca4c53288117e7e0406e9eaa804bf259b4b30
React-microtasksnativemodule: 885bebe5c5f25035e1fd0920776078840a0e3a76
react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06
react-native-safe-area-context: 54d812805f3c4e08a4580ad086cbde1d8780c2e4
React-NativeModulesApple: c4bee6aa736092cd347456488a4f97a8e7517604
React-oscompat: 95875e81f5d4b3c7b2c888d5bd2c9d83450d8bdb
Expand Down
13 changes: 12 additions & 1 deletion Samples/MixpanelStarter/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import {MixpanelProvider} from './contexts/MixpanelContext';
import {ErrorBoundary} from './components/ErrorBoundary';
import {OnboardingScreen} from './screens/OnboardingScreen';
import {HomeScreen} from './screens/HomeScreen';
import {FeatureFlagsScreen} from './screens/FeatureFlagsScreen';
import {SettingsScreen} from './screens/SettingsScreen';
import {MIXPANEL_TOKEN} from '@env';

const Tab = createBottomTabNavigator();

// Fallback token for demo purposes (use your own from Mixpanel dashboard)
const DEMO_TOKEN = 'YOUR_TOKEN_HERE';
const DEMO_TOKEN = 'metrics-1';
const token = MIXPANEL_TOKEN || DEMO_TOKEN;

function App(): React.JSX.Element {
Expand Down Expand Up @@ -52,6 +53,16 @@ function App(): React.JSX.Element {
),
}}
/>
<Tab.Screen
name="FeatureFlags"
component={FeatureFlagsScreen}
options={{
tabBarLabel: 'Flags',
tabBarIcon: ({color}) => (
<Text style={{fontSize: 20, color}}>🚩</Text>
),
}}
/>
<Tab.Screen
name="Settings"
component={SettingsScreen}
Expand Down
Loading
Loading