React Native - Implementing TypeScript for Type Safety


What Are Native Modules?

Native modules are custom libraries written in languages like Java or Objective-C (and Swift) that extend the functionality of React Native. They allow access to device-specific features, such as sensors or custom hardware interactions, which may not be available through standard React Native APIs. With a native module, you can directly call native code from your JavaScript code, enhancing performance and providing additional capabilities.

When to Use Native Modules

Consider using native modules in the following situations:

  1. Performance Needs: If a specific task significantly slows down your JavaScript app, moving this task to native code can yield better performance. For example, complex graphics rendering that runs at 15 FPS on JavaScript might run at 30 FPS natively.

  2. Device-Specific Functionalities: If you want to leverage features available only at the hardware level, such as the camera or accelerometer, native modules are essential. For instance, accessing the camera directly may improve loading times or image quality compared to using a JavaScript wrapper.

  3. Incorporating Native Libraries: Sometimes a library you need has only a native implementation. For example, integrating Firebase Crashlytics requires native code for better error handling, which can be integrated through native modules.

Setting Up a Basic Native Module

To illustrate how to integrate native modules, we will create a simple native module for iOS that returns the device name as a string.

Step 1: Create Your React Native Project

First, create a new React Native project if you haven’t done so:

npx react-native init NativeModuleExample


Step 2: Navigate to Your Project

Change into the project directory:


cd NativeModuleExample


Step 3: Create the Native Module

For iOS (Objective-C)

Navigate to the iOS directory:

cd ios

Create two new files: `MyDeviceModule.m` and `MyDeviceModule.h` in the `NativeModuleExample` folder.

MyDeviceModule.h


import <React/RCTBridgeModule.h>

@interface MyDeviceModule : NSObject <RCTBridgeModule>

@end



MyDeviceModule.m


import "MyDeviceModule.h"

import <UIKit/UIKit.h>

@implementation MyDeviceModule

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(getDeviceName:(RCTPromiseResolveBlock)resolve

rejecter:(RCTPromiseRejectBlock)reject)

{

NSString *deviceName = [[UIDevice currentDevice] name];

resolve(deviceName);

}

@end


This code creates a native module called `MyDeviceModule` with a method `getDeviceName` that returns the device name.

Step 4: Register the Module

Now, register your newly created module in `NativeModuleExample-Bridging-Header.h`. If this file does not exist, create it. This header file exposes your Swift code to Objective-C.

Step 5: Invoke the Native Module in JavaScript

Next, use your native module in the JavaScript part of your React Native project. Open or create `App.js` and import NativeModules from React Native:


import React, { useEffect } from 'react';

import { NativeModules, Text, View } from 'react-native';

const App = () => {

useEffect(() => {

const getDeviceName = async () => {

try {

const deviceName = await NativeModules.MyDeviceModule.getDeviceName();

console.log(deviceName);

} catch (error) {

console.error(error);

}

};

getDeviceName();

}, []);

return (

<View>

<Text>Check your console for the device name!</Text>

</View>

);

};

export default App;


Step 6: Run Your Project

To see the changes, run your project with the following command:


npx react-native run-ios


Once your app is running, check the console for the displayed device name. This is a simple yet effective way to validate your implementation.

Example for Android

Integrating native modules for Android follows a similar pattern but involves Java code. Here’s how to create a comparable module:

Step 1: Create the Native Module

Navigate to your Android directory at `android/app/src/main/java/com/native_module_example/` and create or update the `MyDeviceModule.java` file:


package com.native_module_example;

import com.facebook.react.bridge.ReactApplicationContext;

import com.facebook.react.bridge.ReactContextBaseJavaModule;

import com.facebook.react.bridge.ReactMethod;

import com.facebook.react.bridge.Promise;

import android.os.Build;

public class MyDeviceModule extends ReactContextBaseJavaModule {

MyDeviceModule(ReactApplicationContext context) {

super(context);

}

@Override

public String getName() {

return "MyDeviceModule";

}

@ReactMethod

public void getDeviceName(Promise promise) {

promise.resolve(Build.MODEL);

}

}


Step 2: Register the Native Module

In the `MainApplication.java` file, register your new module:


@Override

protected List<ReactPackage> getPackages() {

return Arrays.<ReactPackage>asList(

new MainReactPackage(),

new MyDevicePackage() // don’t forget to add your package here

);

}


Step 3: Implement the React Package

Next, create a new Java class named `MyDevicePackage.java`:


package com.native_module_example;

import com.facebook.react.ReactPackage;

import com.facebook.react.bridge.NativeModule;

import com.facebook.react.bridge.JavaScriptModule;

import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

public class MyDevicePackage implements ReactPackage {

@Override

public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

List<NativeModule> modules = new ArrayList<>();

modules.add(new MyDeviceModule(reactContext));

return modules;

}

@Override

public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {

return Collections.emptyList();

}

}


Step 4: Use the Module in JavaScript

You can reuse your `App.js` file from above, using the same logic to invoke the native method for Android.