Native Modules Expert
Specialized in React Native native module integration, including custom native module development, third-party native library integration, and troubleshooting native code issues.
What I Know
Native Module Fundamentals
What Are Native Modules?
-
Bridge between JavaScript and native platform code
-
Access platform-specific APIs (Bluetooth, NFC, etc.)
-
Performance-critical operations
-
Integration with existing native SDKs
Modern Architecture
-
Old Architecture: Bridge-based (React Native < 0.68)
-
New Architecture (React Native 0.68+):
-
JSI (JavaScript Interface): Direct JS ↔ Native communication
-
Turbo Modules: Lazy-loaded native modules
-
Fabric: New rendering engine
Using Third-Party Native Modules
Installation with Autolinking
Install module
npm install react-native-camera
iOS: Install pods (autolinking handles most configuration)
cd ios && pod install && cd ..
Rebuild the app
npm run ios npm run android
Manual Linking (Legacy)
React Native < 0.60 (rarely needed now)
react-native link react-native-camera
Expo Integration
For Expo managed workflow, use config plugins
npx expo install react-native-camera
Add plugin to app.json
{ "expo": { "plugins": [ [ "react-native-camera", { "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera" } ] ] } }
Rebuild dev client
eas build --profile development --platform all
Creating Custom Native Modules
iOS Native Module (Swift)
// RCTCalendarModule.swift import Foundation
@objc(CalendarModule) class CalendarModule: NSObject {
@objc static func requiresMainQueueSetup() -> Bool { return false }
@objc func createEvent(_ name: String, location: String, date: NSNumber) { // Native implementation print("Creating event: (name) at (location)") }
@objc func getEvents(_ callback: @escaping RCTResponseSenderBlock) { let events = ["Event 1", "Event 2", "Event 3"] callback([NSNull(), events]) }
@objc func findEvents(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) { // Async with Promise DispatchQueue.global().async { let events = self.fetchEventsFromNativeAPI() resolve(events) } } }
// RCTCalendarModule.m (Bridge file) #import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(CalendarModule, NSObject)
RCT_EXTERN_METHOD(createEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)date)
RCT_EXTERN_METHOD(getEvents:(RCTResponseSenderBlock)callback)
RCT_EXTERN_METHOD(findEvents:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
@end
Android Native Module (Kotlin)
// CalendarModule.kt package com.myapp
import com.facebook.react.bridge.*
class CalendarModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String {
return "CalendarModule"
}
@ReactMethod
fun createEvent(name: String, location: String, date: Double) {
// Native implementation
println("Creating event: $name at $location")
}
@ReactMethod
fun getEvents(callback: Callback) {
val events = WritableNativeArray().apply {
pushString("Event 1")
pushString("Event 2")
pushString("Event 3")
}
callback.invoke(null, events)
}
@ReactMethod
fun findEvents(promise: Promise) {
try {
val events = fetchEventsFromNativeAPI()
promise.resolve(events)
} catch (e: Exception) {
promise.reject("ERROR", e.message, e)
}
}
}
// CalendarPackage.kt package com.myapp
import com.facebook.react.ReactPackage import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.uimanager.ViewManager
class CalendarPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> { return listOf(CalendarModule(reactContext)) }
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
}
JavaScript Usage
// CalendarModule.js import { NativeModules } from 'react-native';
const { CalendarModule } = NativeModules;
export default { createEvent: (name, location, date) => { CalendarModule.createEvent(name, location, date); },
getEvents: (callback) => { CalendarModule.getEvents((error, events) => { if (error) { console.error(error); } else { callback(events); } }); },
findEvents: async () => { try { const events = await CalendarModule.findEvents(); return events; } catch (error) { console.error(error); throw error; } } };
// Usage in components import CalendarModule from './CalendarModule';
function MyComponent() { const handleCreateEvent = () => { CalendarModule.createEvent('Meeting', 'Office', Date.now()); };
const handleGetEvents = async () => { const events = await CalendarModule.findEvents(); console.log('Events:', events); };
return ( <View> <Button title="Create Event" onPress={handleCreateEvent} /> <Button title="Get Events" onPress={handleGetEvents} /> </View> ); }
Turbo Modules (New Architecture)
Creating a Turbo Module
// NativeCalendarModule.ts (Codegen spec) import type { TurboModule } from 'react-native'; import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule { createEvent(name: string, location: string, date: number): void; findEvents(): Promise<string[]>; }
export default TurboModuleRegistry.getEnforcing<Spec>('CalendarModule');
Benefits of Turbo Modules
-
Lazy loading: Loaded only when used
-
Type safety with TypeScript
-
Faster initialization
-
Better performance via JSI
Native UI Components
Custom Native View (iOS - Swift)
// RCTCustomViewManager.swift import UIKit
@objc(CustomViewManager) class CustomViewManager: RCTViewManager {
override static func requiresMainQueueSetup() -> Bool { return true }
override func view() -> UIView! { return CustomView() }
@objc func setColor(_ view: CustomView, color: NSNumber) { view.backgroundColor = RCTConvert.uiColor(color) } }
class CustomView: UIView { override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = .blue }
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
Custom Native View (Android - Kotlin)
// CustomViewManager.kt class CustomViewManager : SimpleViewManager<View>() {
override fun getName(): String {
return "CustomView"
}
override fun createViewInstance(reactContext: ThemedReactContext): View {
return View(reactContext).apply {
setBackgroundColor(Color.BLUE)
}
}
@ReactProp(name = "color")
fun setColor(view: View, color: Int) {
view.setBackgroundColor(color)
}
}
JavaScript Usage
import { requireNativeComponent } from 'react-native';
const CustomView = requireNativeComponent('CustomView');
function MyComponent() { return ( <CustomView style={{ width: 200, height: 200 }} color="red" /> ); }
Common Native Module Issues
Module Not Found
iOS: Clear build and reinstall pods
cd ios && rm -rf build Pods && pod install && cd .. npm run ios
Android: Clean and rebuild
cd android && ./gradlew clean && cd .. npm run android
Clear Metro cache
npx react-native start --reset-cache
Autolinking Not Working
Verify module in package.json
npm list react-native-camera
Re-run pod install
cd ios && pod install && cd ..
Check react-native.config.js for custom linking config
Native Crashes
iOS: Check Xcode console for crash logs
Look for:
- Unrecognized selector sent to instance
- Null pointer exceptions
- Memory issues
Android: Check logcat
adb logcat *:E
Look for:
- Java exceptions
- JNI errors
- Null pointer exceptions
When to Use This Skill
Ask me when you need help with:
-
Integrating third-party native modules
-
Creating custom native modules
-
Troubleshooting native module installation
-
Writing iOS native code (Swift/Objective-C)
-
Writing Android native code (Kotlin/Java)
-
Debugging native crashes
-
Understanding Turbo Modules and JSI
-
Migrating to New Architecture
-
Creating custom native UI components
-
Handling platform-specific APIs
-
Resolving autolinking issues
Essential Commands
Module Development
Create module template
npx create-react-native-module my-module
Build iOS module
cd ios && xcodebuild
Build Android module
cd android && ./gradlew assembleRelease
Test module locally
npm link cd ../MyApp && npm link my-module
Debugging Native Code
iOS: Run with Xcode debugger
open ios/MyApp.xcworkspace
Android: Run with Android Studio debugger
Open android/ folder in Android Studio
Print native logs
iOS
tail -f ~/Library/Logs/DiagnosticReports/*.crash
Android
adb logcat | grep "CalendarModule"
Pro Tips & Tricks
- Type-Safe Native Modules with Codegen
Use Codegen (New Architecture) for type safety:
// NativeMyModule.ts import type { TurboModule } from 'react-native'; import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule { getString(key: string): Promise<string>; setString(key: string, value: string): void; }
export default TurboModuleRegistry.getEnforcing<Spec>('MyModule');
- Event Emitters for Native → JS Communication
// iOS - Emit events to JavaScript import Foundation
@objc(DeviceOrientationModule) class DeviceOrientationModule: RCTEventEmitter {
override func supportedEvents() -> [String]! { return ["OrientationChanged"] }
@objc override static func requiresMainQueueSetup() -> Bool { return true }
@objc func startObserving() { NotificationCenter.default.addObserver( self, selector: #selector(orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil ) }
@objc func stopObserving() { NotificationCenter.default.removeObserver(self) }
@objc func orientationChanged() { let orientation = UIDevice.current.orientation sendEvent(withName: "OrientationChanged", body: ["orientation": orientation.rawValue]) } }
// JavaScript - Listen to native events import { NativeEventEmitter, NativeModules } from 'react-native';
const { DeviceOrientationModule } = NativeModules; const eventEmitter = new NativeEventEmitter(DeviceOrientationModule);
function MyComponent() { useEffect(() => { const subscription = eventEmitter.addListener('OrientationChanged', (data) => { console.log('Orientation:', data.orientation); });
return () => subscription.remove();
}, []);
return <View />; }
- Native Module with Callbacks
// Android - Pass callbacks @ReactMethod fun processData(data: String, successCallback: Callback, errorCallback: Callback) { try { val result = heavyProcessing(data) successCallback.invoke(result) } catch (e: Exception) { errorCallback.invoke(e.message) } }
// JavaScript CalendarModule.processData( 'input data', (result) => console.log('Success:', result), (error) => console.error('Error:', error) );
- Synchronous Native Methods (Use Sparingly)
// iOS - Synchronous method (blocks JS thread!) @objc func getDeviceId() -> String { return UIDevice.current.identifierForVendor?.uuidString ?? "unknown" }
// JavaScript - Synchronous call const deviceId = CalendarModule.getDeviceId(); console.log(deviceId); // Returns immediately
Warning: Synchronous methods block the JS thread. Use only for very fast operations (<5ms).
Integration with SpecWeave
Native Module Planning
-
Document native dependencies in spec.md
-
Include native module setup in plan.md
-
Add native code compilation to tasks.md
Testing Strategy
-
Unit test native code separately
-
Integration test JS ↔ Native bridge
-
Test on both iOS and Android
-
Document platform-specific behaviors
Documentation
-
Maintain native module API documentation
-
Document platform-specific quirks
-
Keep runbooks for common native issues