Step-by-Step Guide to Creating and Publishing a Native Module in React Native

Nikunj bisht
Technology at Nineleaps
5 min readApr 28, 2023

Sometimes a React Native app needs to access a native platform API that is not available by default in JavaScript, for example, the native APIs to access Apple or Google Pay. Maybe you want to reuse some existing Objective-C, Swift, Java or C++ libraries without having to reimplement them in JavaScript or write some high-performance, multi-threaded code for things like image processing.

The NativeModule system exposes instances of Java/Objective-C/C++ (native) classes to JavaScript (JS) as JS objects, thereby allowing you to execute arbitrary native code from within JS. While we don’t expect this feature to be part of the usual development process, it is essential that it exists. If React Native doesn’t export a native API that your JS app needs you should be able to export it yourself!

NATIVE MODULE SETUP

To get set up with the basic project structure for a native module we will use the community tool called create-react-native-library. You can go ahead further and dive deep into how that library works, but for our needs, we will only execute the basic script:

npx create-react-native-library@latest react-native-rnbiometric

Where react-native-rnbiometric is the name you would like for the new module. After doing this you will navigate into react-native-rnbiometric folder and bootstrap the example project by running:

After successful installation the folder structure will be like this:

Create a Biometric Native Module

To get started, open up the Android project within your React Native application in Android Studio. You can find your Android project here within a React Native app:

The first step is to create the (FirstnativemodModule.java or FirstnativemodModule.kt) Java/Kotlin file inside android/app/src/main/java/com/your-app-name/ folder (the folder is the same for both Kotlin and Java). This Java/Kotlin file will contain your native module Java/Kotlin class.

package com.firstnativemod;
import android.app.KeyguardManager;
import android.app.Service;
import android.content.Context;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;


@ReactModule(name = FirstnativemodModule.NAME)
public class FirstnativemodModule extends ReactContextBaseJavaModule {
public static final String NAME = "FingerPrint";
private static ReactApplicationContext reactApplicationContext;
public FirstnativemodModule(ReactApplicationContext reactContext) {
super(reactContext);
reactApplicationContext = reactContext;

}

@Override
@NonNull
public String getName() {
return NAME;
}
}

getName() method returns a string, which represents the name of the native module. The native module can then be accessed in JavaScript using its name. For example, in the below code snippet, getName() returns "FirstnativemodModule".

The native module can then be accessed in JS like this:

import { NativeModules, Platform } from 'react-native';

const LINKING_ERROR =
`The package 'react-native-firstnativemod' doesn't seem to be linked. Make sure: \n\n` +
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
'- You rebuilt the app after installing the package\n' +
'- You are not using Expo Go\n';

const FingerPrint = NativeModules.FingerPrint
? NativeModules.FingerPrint
: new Proxy(
{},
{
get() {
throw new Error(LINKING_ERROR);
},
}
);

Export a Native Method to JavaScript

Next, you will need to add a method to your native module that will create calendar events and can be invoked in JavaScript. All native module methods meant to be invoked from JavaScript must be annotated with @ReactMethod.

Set up a method showBiometric() for FirstnativemodModule that can be invoked in JS through FirstnativemodModule.showBiometric(). For now, the method will take in a name and location as strings. Argument-type options will be covered shortly.

Callbacks

Native modules also support a unique kind of argument: a callback. Callbacks are used to pass data from Java/Kotlin to JavaScript for asynchronous methods. They can also be used to asynchronously execute JavaScript from the native side.

In order to create a native module method with a callback, first import the Callback interface, and then add a new parameter to your native module method of type Callback. There are a couple of nuances with callback arguments that will soon be lifted with TurboModules. First off, you can only have two callbacks in your function arguments- a successCallback and a failureCallback. In addition, the last argument to a native module method call, if it's a function, is treated as the successCallback, and the second to last argument to a native module method call, if it's a function, is treated as the failure callback.

 @ReactMethod
public void showBiometric(String title, String subTitle , String belowText , Callback callback) {

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
Executor executor = Executors.newSingleThreadExecutor();

BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(getReactApplicationContext())
.setTitle(title)
.setSubtitle(subTitle)
.setNegativeButton(belowText ,executor, new DialogInterface.OnClickListener(){

@Override
public void onClick(DialogInterface dialogInterface, int i) {

}
}).setNegativeButton(belowText, executor, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {

}
})
.build();
biometricPrompt.authenticate(new CancellationSignal(), executor, new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
callback.invoke(true);
}
});

}


}

Now we will access showBiometric in JS

export function showBiometric(
a: string,
b: string,
c: string,
callBack: Function
) {
FingerPrint.showBiometric(a, b, c, callBack);
}

Register the Module for Android

package com.firstnativemod;

import androidx.annotation.NonNull;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class FirstnativemodPackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new FirstnativemodModule(reactContext));
return modules;
}

@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

Publishing Module to NPM

1 — Make sure you have npm account.

2 — In your project directory run npm publish.

3 — After successful publishing your package will get listed in npm registry.

https://www.npmjs.com/package/react-native-rnbiometric

Test What You Have Built

Install your package into your project.

import * as React from 'react';

import { StyleSheet, View, Text, ToastAndroid } from 'react-native';
import { showBiometric, showPatternSheet } from 'react-native-rnbiometric';

export default function App() {
const [result, setResult] = React.useState<number | undefined>();

React.useEffect(() => {
showBiometric("Authentication","del","",()=>{
console.log("authenticated");
});
}, []);

return (
<View style={styles.container}>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: 60,
height: 60,
marginVertical: 20,
},
});

Result

--

--

Nikunj bisht
Technology at Nineleaps

Unleashing creativity through code, turning dreams into apps.