| name | native-modules |
| description | Expert in React Native native modules, bridging JavaScript and native code, writing custom native modules, using Turbo Modules, Fabric, JSI, autolinking, module configuration, iOS Swift/Objective-C modules, Android Kotlin/Java modules. Activates for native module, native code, bridge, turbo module, JSI, fabric, autolinking, custom native module, ios module, android module, swift, kotlin, objective-c, java native code. |
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
1. 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');
2. 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 />;
}
3. 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)
);
4. 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