Create a native module for React Native app - Part 1

Create a native module for React Native app - Part 1

React Native is a popular framework for building cross-platform mobile applications. It allows developers to use the same code base for both iOS and Android platforms. However, there are occasions when developers need to access some native API that is not provided by Javascript by default. In that case, you may want to reuse some existing Java, Objective-C, or Swift code without having to recreate it in Javascript, or you may want to write some high-performance code for complicated things such as augmented reality. Then you can import the module into your React Native app and use it in your Javascript code. In this post, we will build a native module to check the connectivity to the internet.

Create a new React Native app:

We will create our React Native app using the usual process, and we will name it NetworkNativeModule .


npx react-native init NativeNetworkModule
cd NativeNetworkModule

At this point, you may want to run the app to make sure that everything is working fine.


npx react-native run-ios

So you will end up with the app running in your iOS simulator.

App-running-in-your-iOS-simulator

Awesome! The app is running fine; let’s now open the Xcode project by opening NativeNetworkModule.xcworkspace:


cd ios && open NativeNetworkModule.xcworkspace

Create a Swift file:

Now, we will create a new file in the Xcode project that will contain the code of our native module. We will call it NetworkStatus.swift. To do this, right-click on the Xcode project and select New File, or (cmd + N):

Create-a-swift-file

Then select Swift File and click Next Then name the file NetworkStatus. Make sure to select NativeNetworkModule in the Group Section and click Create:

Create-a-NetworkStatus-file

Great! Now that we have our Swift file, you will be prompted to choose if you want to create an Objective-C bridging header. So we create it:

create-an-Objective-C-bridging-header

Now, let’s add the headers that we will need to expose our Swift code that we will create:

// NativeNetworkModule-Bridging-Header.h

#import "React/RCTBridgeModule.h"
#import "React/RCTEventEmitter.h"

Let’s create our class in Swift:

import Foundation
import Network
import React

@objc(NetworkStatus)
class NetworkStatus: RCTEventEmitter {
  let monitor = NWPathMonitor()
  let queue = DispatchQueue(label: "NetworkStatus")
  var previousStatus: NWPath.Status = .unsatisfied

  override init() {
    super.init()
  }

  override func supportedEvents() -> [String]! {
    return ["networkStatusChanged"]
  }

  @objc
  func startMonitoring() {
    let queue = DispatchQueue(label: "NetworkStatus")
            monitor.start(queue: queue)
            monitor.pathUpdateHandler = { [weak self] path in
                guard let self = self else { return }
                let currentStatus = path.status
                if currentStatus != self.previousStatus {
                    self.previousStatus = currentStatus
                    var status = ""
                    switch currentStatus {
                    case .satisfied:
                        status = "connected"
                    default:
                        status = "disconnected"
                    }
                  print(currentStatus)
                  self.sendEvent(withName: "networkStatusChanged", body: ["status": status])
                }
            }
  }
  
  @objc
  func stopMonitoring() {
    monitor.cancel()
  

  override static func requiresMainQueueSetup() -> Bool {
    return true
  }
}

The NetworkStatus class is a subclass of RCTEventEmitter, which allows us to emit events to JavaScript. It uses NWPathMonitor to monitor changes in network status. The startMonitoring method starts the monitoring process, and when a status change occurs, it emits a networkStatusChanged event to JavaScript with the new status, and the stopMonitoring method stops the monitoring process. The requiresMainQueueSetup method is used to let React Native know whether the module should be initialized in the main thread or in the background thread. In our case, we want to initialize our module in the main thread, so we returned true.

Expose the Swift module into React Native:

To expose our module to React Native, we need to create a new Objective-C file in our project:

Expose-our-module-to-React-Native

Press Next, and we name it NetworkStatus:

Expose-our-module-to-React-Native

We make sure again that the file is within the NativeNetworkModule Group Section, and we click Create:

Expose-our-module-to-React-Native

This file will contain the code that will allow React Native to interact with our Swift class. In this file, we need to import the RCTBridgeModule.h header file and declare a new module:

// NetworkStatus.m

#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface RCT_EXTERN_MODULE(NetworkStatus, RCTEventEmitter)

RCT_EXTERN_METHOD(startMonitoring)
RCT_EXTERN_METHOD(stopMonitoring)
@end

The @interface declaration creates a new module named NetworkStatus. The RCT_EXTERN_METHOD declarations expose the methods of our Swift class to React Native.

Use React Native's native module methods.

Although we can use any component or view to call the native module, for the sake of simplicity in this example, we will only use App.tsx. Before calling the native module, React must import it, also, we need to networkStatusChanged event from Javascript / Typescript, our code will end up like the following

// App.tsx

import React, {useEffect, useState} from 'react';
import {NativeEventEmitter, NativeModules, Text, View} from 'react-native';

const {NetworkStatus} = NativeModules;
const networkStatusEmitter = new NativeEventEmitter(NetworkStatus);

const App = () => {
  const [networkStatus, setNetworkStatus] = useState('unknown');
  useEffect(() => {

    networkStatusEmitter.addListener('networkStatusChanged', ({status}) => {
      console.warn('Network status changed:', status);
      setNetworkStatus(status);
    });

    NetworkStatus.startMonitoring();
    return () => {
      networkStatusEmitter.removeAllListeners('networkStatusChanged');
      NetworkStatus.stopMonitoring();
    };
  }, []);

  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Text>{networkStatus}</Text>
    </View>
  );
};

export default App;

So, here is the result on an iOS device when it is connected to the internet:

Connected

and when it is not connected to the internet:

Disconnected

In the next post, we will see how to implement the same native module for Android.

You can find the full project on the following link

Ayoub Aharmim
Ayoub Aharmim
2023-06-26 | 6 min read
Share article

More articles