---
description: React Native bridge patterns for iOS Swift and Android Java implementations
globs: ["ios/**/*.swift", "android/**/*.java", "**/*Native*.js"]
alwaysApply: false
---

# Native Module Patterns

## iOS Swift Implementation

### Method Declaration Pattern (REQUIRED)
ALL iOS methods MUST use @objc decorator with Promise-based async pattern:

```swift
@objc
func methodName(_ token: String,
                parameter: String,
                resolver resolve: RCTPromiseResolveBlock,
                rejecter reject: RCTPromiseRejectBlock) -> Void {
    let instance = MixpanelReactNative.getMixpanelInstance(token)
    guard let instance = instance else {
        reject("Instance Error", "Failed to get Mixpanel instance", nil)
        return
    }
    
    // Call native SDK method
    instance.nativeMethod(parameter: parameter)
    resolve(nil)
}
```

### ⚠️ Evolution Notice [Updated: 2025-05-30]
Initialize method now supports optional gzip compression:

```swift
@objc
func initialize(_ token: String,
                optOutTrackingDefault: Bool = false,
                trackAutomaticEvents: Bool = true,
                superProperties: NSDictionary?,
                serverURL: String = DEFAULT_SERVER_URL,
                useGzipCompression: Bool = false,  // New parameter
                resolver resolve: RCTPromiseResolveBlock,
                rejecter reject: RCTPromiseRejectBlock) -> Void {
    // Initialize with compression support
    MixpanelReactNative.initializeWithToken(token,
                            trackAutomaticEvents: trackAutomaticEvents,
                            optOutTrackingByDefault: optOutTrackingDefault,
                            superProperties: superProperties,
                            serverURL: serverURL,
                            useGzipCompression: useGzipCompression)
    resolve(nil)
}
```

### Instance Management Pattern
```swift
// ALWAYS get instance by token for multi-tenancy
let instance = MixpanelReactNative.getMixpanelInstance(token)
guard let instance = instance else {
    reject("Instance Error", "Failed to get Mixpanel instance", nil)
    return
}
```

### Type Conversion Pattern
```swift
// Use MixpanelTypeHandler for React Native → Native conversion
let processedValue = MixpanelTypeHandler.processProperty(value: reactNativeValue)

// For dictionary/object parameters
let processedProperties = MixpanelTypeHandler.processProperties(properties: properties)
```

## Android Java Implementation

### Method Declaration Pattern (REQUIRED)
ALL Android methods MUST use @ReactMethod with synchronized access:

```java
@ReactMethod
public void methodName(final String token, 
                      String parameter, 
                      Dynamic value, 
                      Promise promise) throws JSONException {
    MixpanelAPI instance = MixpanelAPI.getInstance(this.mReactContext, token, true);
    if (instance == null) {
        promise.reject("Instance Error", "Failed to get Mixpanel instance");
        return;
    }
    
    synchronized (instance) {
        // Convert React Native Dynamic to appropriate type
        Object processedValue = ReactNativeHelper.dynamicToObject(value);
        
        // Call native SDK method
        instance.nativeMethod(parameter, processedValue);
        promise.resolve(null);
    }
}
```

### Thread Safety Pattern (CRITICAL)
ALWAYS synchronize access to Mixpanel instances:

```java
synchronized (instance) {
    // ALL instance operations must be within synchronized block
    instance.track(eventName, properties);
    instance.flush();
}
```

### Type Conversion Pattern
```java
// Use ReactNativeHelper for type conversion
Object processedValue = ReactNativeHelper.dynamicToObject(dynamicValue);

// For complex objects
JSONObject processedProperties = ReactNativeHelper.dynamicToJSONObject(properties);
```

## Bridge Integration Patterns

### JavaScript Native Module Interface
```javascript
// Import native module with fallback
const { MixpanelReactNative } = NativeModules;

// ALWAYS check availability before calling
if (MixpanelReactNative && MixpanelReactNative.methodName) {
  return MixpanelReactNative.methodName(token, parameter);
} else {
  console.warn("Native method not available, falling back to JS implementation");
  return jsImplementation(token, parameter);
}
```

### Error Handling for Native Calls
```javascript
// ALWAYS wrap native calls with error handling
try {
  await MixpanelReactNative.methodName(token, parameter);
} catch (error) {
  MixpanelLogger.error(token, `Native method failed: ${error.message}`);
  // Optionally fall back to JS implementation
  return jsImplementation(token, parameter);
}
```

## Platform-Specific Considerations

### iOS-Specific Features
```swift
// iOS supports background flushing
@objc
func setFlushOnBackground(_ token: String, flushOnBackground: Bool) -> Void {
    // iOS-only feature implementation
}
```

### Android Limitations
```java
// Android doesn't support certain iOS features
// Document limitations and provide alternatives:

// setFlushOnBackground not supported on Android
// Use alternative approach or skip functionality
```

## Testing Native Modules

### Mock Pattern for Tests
```javascript
// __tests__/jest_setup.js
jest.doMock("react-native", () => ({
  NativeModules: {
    MixpanelReactNative: {
      initialize: jest.fn().mockResolvedValue(undefined),
      track: jest.fn().mockResolvedValue(undefined),
      identify: jest.fn().mockResolvedValue(undefined),
      // Mock ALL native methods
    },
  },
}));
```

### Testing Both Modes
```javascript
describe('Native Mode', () => {
  beforeEach(() => {
    // Ensure native mode is active
    mixpanel = new Mixpanel('test-token', true, true);
  });

  it('should call native implementation', () => {
    mixpanel.track('Event', {prop: 'value'});
    
    expect(MixpanelReactNative.track).toHaveBeenCalledWith(
      'test-token',
      'Event',
      expect.objectContaining({prop: 'value'})
    );
  });
});
```

## Autolinking Configuration

### iOS Podfile Requirements
```ruby
# Ensure MixpanelReactNative is properly linked
pod 'MixpanelReactNative', :path => '../node_modules/mixpanel-react-native'
```

### Android Gradle Requirements
```gradle
// Autolinking should handle this, but verify:
// settings.gradle includes MixpanelReactNative
// build.gradle includes react-native-mixpanel
```

## Performance Considerations

### Minimize Bridge Calls
```javascript
// Good: Batch operations when possible
const superProps = {prop1: 'value1', prop2: 'value2'};
mixpanel.registerSuperProperties(superProps);

// Avoid: Multiple individual calls
mixpanel.registerSuperProperties({prop1: 'value1'});
mixpanel.registerSuperProperties({prop2: 'value2'});
```

### Use Native Optimizations
- Native queue management (60s flush interval)
- Platform-specific background handling
- Efficient serialization/deserialization