mixpanel-react-native
Version:
Official React Native Tracking Library for Mixpanel Analytics
353 lines (285 loc) โข 9.95 kB
Markdown
# React Native Integration Patterns
## Overview
The mixpanel-react-native library demonstrates sophisticated React Native integration patterns, including native module bridging, autolinking configuration, and cross-platform compatibility strategies.
## ๐ Native Module Bridge Architecture
### Platform Registration
#### iOS (CocoaPods Integration)
```ruby
# MixpanelReactNative.podspec
Pod::Spec.new do |s|
s.name = "MixpanelReactNative"
s.platform = :ios, "11.0"
s.swift_version = '5.0'
s.source_files = "ios/*.{swift,h,m}"
# Key dependencies
s.dependency "React-Core"
s.dependency "Mixpanel-swift", '5.0.0' # Official Mixpanel iOS SDK
end
```
**Key Insights**:
- Minimum iOS 11.0 support
- Swift 5.0 for modern language features
- Direct dependency on official Mixpanel iOS SDK
- Source files include both Swift and Objective-C bridge files
#### Android (Gradle Integration)
```gradle
// android/build.gradle
android {
compileSdkVersion 34
minSdkVersion 21 // Android 5.0+
targetSdkVersion 34
namespace "com.mixpanel.reactnative" // AGP 7.0+ namespace
}
dependencies {
implementation 'com.facebook.react:react-native:+'
implementation 'com.mixpanel.android:mixpanel-android:8.1.0' // Official Android SDK
}
```
**Key Insights**:
- Modern Android API levels (21-34)
- Namespace declaration for AGP 7.0+ compatibility
- Direct integration with official Mixpanel Android SDK
- Dynamic React Native version resolution
### Autolinking Configuration
```javascript
// react-native.config.js
module.exports = {
dependencies: {
'mixpanel-react-native': {
platforms: {
android: {
"sourceDir": "./node_modules/mixpanel-react-native/android",
"packageImportPath": "import com.mixpanel.reactnative.MixpanelReactNativePackage;",
"packageInstance": "new MixpanelReactNativePackage()"
},
ios: {
project: './node_modules/mixpanel-react-native/ios/MixpanelReactNative.xcodeproj',
}
}
}
}
};
```
**Autolinking Strategy**:
- Explicit configuration for both platforms
- Custom package registration for Android
- Xcode project specification for iOS
- Supports React Native 0.60+ autolinking
## ๐๏ธ Native Module Implementation Patterns
### iOS Bridge Pattern (Swift + Objective-C)
```swift
// ios/MixpanelReactNative.swift
open class MixpanelReactNative: NSObject {
static func requiresMainQueueSetup() -> Bool {
return false // Background initialization OK
}
func initialize(_ token: String,
trackAutomaticEvents: Bool,
optOutTrackingByDefault: Bool = false,
properties: [String: Any],
serverURL: String,
useGzipCompression: Bool = false,
resolver resolve: RCTPromiseResolveBlock,
rejecter reject: RCTPromiseRejectBlock) -> Void {
// Native implementation using Mixpanel-swift SDK
Mixpanel.initialize(token: token, ...)
resolve(true)
}
}
```
**Bridge Patterns**:
- `` decoration for React Native exposure
- Promise-based async operations
- Background-safe initialization
- Direct parameter mapping to native SDK
### Android Bridge Pattern (Java)
```java
// android/.../MixpanelReactNativeModule.java
public class MixpanelReactNativeModule extends ReactContextBaseJavaModule {
public void initialize(String token,
boolean trackAutomaticEvents,
boolean optOutTrackingDefault,
ReadableMap metadata,
String serverURL,
boolean useGzipCompression,
Promise promise) throws JSONException {
// Convert React Native types to native
JSONObject mixpanelProperties = ReactNativeHelper.reactToJSON(metadata);
// Initialize native SDK
MixpanelAPI instance = MixpanelAPI.getInstance(this.mReactContext,
token,
optOutTrackingDefault,
mixpanelProperties,
null,
trackAutomaticEvents);
promise.resolve(null);
}
}
```
**Bridge Patterns**:
- `` annotation for method exposure
- Type conversion utilities (`ReactNativeHelper`)
- Thread-safe instance management
- Consistent Promise-based API
## ๐ Fallback Strategy Implementation
### Implementation Router Pattern
```javascript
// index.js
export class Mixpanel {
constructor(token, trackAutomaticEvents, useNative = true, storage) {
this.token = token;
this.trackAutomaticEvents = trackAutomaticEvents;
// Try native implementation first
if (useNative && MixpanelReactNative) {
this.mixpanelImpl = MixpanelReactNative;
return;
} else if (useNative) {
console.warn(
"MixpanelReactNative is not available; using JavaScript mode..."
);
}
// Fallback to JavaScript implementation
this.mixpanelImpl = new MixpanelMain(token, trackAutomaticEvents, storage);
}
}
```
**Fallback Benefits**:
- Graceful degradation when native modules unavailable
- Expo compatibility (no native modules allowed)
- Web support through React Native Web
- Development environment flexibility
## ๐ฑ Platform-Specific Optimizations
### iOS Optimizations
```swift
// ios/MixpanelReactNative.swift
func setFlushOnBackground(_ token: String,
flushOnBackground: Bool,
resolver resolve: RCTPromiseResolveBlock,
rejecter reject: RCTPromiseRejectBlock) -> Void {
let instance = MixpanelReactNative.getMixpanelInstance(token)
instance?.flushOnBackground = flushOnBackground // iOS-specific feature
resolve(nil)
}
```
**iOS-Specific Features**:
- Background flushing support
- App lifecycle integration
- Memory management optimization
- Native SDK performance benefits
### Android Optimizations
```java
// android/.../MixpanelReactNativeModule.java
public void setFlushBatchSize(final String token, Integer flushBatchSize, Promise promise) {
MixpanelAPI instance = MixpanelAPI.getInstance(this.mReactContext, token, true);
if (instance == null) {
promise.reject("Instance Error", "Failed to get Mixpanel instance");
return;
}
synchronized (instance) { // Thread-safe operations
instance.setFlushBatchSize(flushBatchSize);
promise.resolve(null);
}
}
```
**Android-Specific Features**:
- Thread synchronization for safety
- Context-aware initialization
- Lifecycle-aware processing
- Memory-efficient batching
## ๐ ๏ธ Development & Build Configuration
### Metro Configuration (Standard)
```javascript
// Samples/SimpleMixpanel/metro.config.js
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
const config = {}; // Use defaults
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
```
**Build Strategy**:
- Standard React Native Metro bundler
- No custom transformations needed
- Works with standard RN build pipeline
### Babel Configuration (Standard)
```javascript
// Samples/SimpleMixpanel/babel.config.js
module.exports = {
presets: ['module:@react-native/babel-preset'],
};
```
**Transpilation Strategy**:
- Standard React Native Babel preset
- No custom plugins required
- ES6+ feature support
## ๐งช Testing Integration
### Jest Configuration for React Native
```javascript
// package.json
"jest": {
"modulePathIgnorePatterns": ["<rootDir>/Samples/"],
"testMatch": ["<rootDir>/__tests__/*.test.js"],
"setupFiles": ["<rootDir>/__tests__/jest_setup.js"],
"verbose": true,
"preset": "react-native"
}
```
**Testing Strategy**:
- React Native Jest preset
- Custom setup for mocking
- Isolated test environment
- Sample apps excluded from tests
### Native Module Mocking
```javascript
// __tests__/jest_setup.js
jest.doMock("react-native", () => {
return Object.setPrototypeOf({
NativeModules: {
MixpanelReactNative: {
initialize: jest.fn(),
track: jest.fn(),
// ... complete API mock
},
},
}, ReactNative);
});
```
**Mocking Strategy**:
- Complete native module API mocking
- Consistent with actual native interface
- Enables testing without native dependencies
## ๐ Performance Considerations
### Native vs JavaScript Mode
| Aspect | Native Mode | JavaScript Mode |
|--------|-------------|-----------------|
| **Initialization** | Native SDK optimized | Custom implementation |
| **Queue Management** | Native (60s flush) | JavaScript (10s flush) |
| **Storage** | Platform-optimized | AsyncStorage |
| **Network** | Native HTTP stack | Fetch API |
| **Memory** | SDK-managed | Manual management |
### Bundle Size Impact
```javascript
// The library is designed for minimal bundle impact:
// - Core native bridge code only
// - JavaScript fallback loaded conditionally
// - No heavy dependencies in main bundle
```
## ๐ Best Practices Discovered
### 1. Graceful Degradation
Always provide JavaScript fallback for native functionality
### 2. Promise-Based APIs
Consistent async patterns across platforms
### 3. Type Safety
Comprehensive input validation before native calls
### 4. Error Handling
Native errors properly propagated to JavaScript
### 5. Platform Detection
Runtime detection of capabilities rather than build-time
### 6. Memory Management
Proper cleanup and instance management
### 7. Thread Safety
Synchronization for shared resources
### 8. Configuration Flexibility
Runtime configuration over build-time constants