Moving Network Requests to Native Modules: A Guide

Moving Network Requests to Native Modules: A Guide

Improving Your App's Performance

Hello, fellows!

Writing native code is intimidating for some, however moving network requests to native modules seems to increase the performance of the app.

This idea was inspired by Ram Narasimhan's talk about performance in React Native at Chain React 2019. Also, the lack of examples and the need for the open source community motivated me to write this app and article.

Before we start, let's be honest: Who doesn't want to work more comfortably with native modules and have their app run faster? 😄 Certainly not me. So let’s do some magic! 🪄

You can download the app here.


Overview

🚫 What this article is not:

  • It doesn't give a full step-by-step tutorial on how to make network requests in Swift or Java
  • It doesn't show you the best way to make a network request in native

✅ What it is:

  • It shows how to make network requests using native code and then connect them to React Native
  • It gives a complete example for both iOS and Android
  • It helps beginners work with native modules

In a nutshell, this app's network modules will run in native, and when JavaScript is ready, response will be sent to JavaScript, to be handled.

overview.gif Source


For the network request, Google Books API was used as it has a trusted certificate.

https://www.googleapis.com/books/v1/volumes?q=harry+potter

By using this URL, we can get to Harry Potter books ⚡🤓🪄, and this is how the app will look in the end:

ios-simulation.gif

💡 An interesting fact: While I was writing this app, I was traveling to Belgium, and when I ran the app, I noticed books were automatically downloaded in Dutch:

Screenshot 2022-05-10 at 12.19.21.png

So, moving forward, the list and app style were done in React Native using code that was pretty similar to what the React Native docs gave for using a FlatList. Please make sure you'll check this link.


Network Modules 🧱

Native Modules are quite awesome. These modules are built in native and allow us to expose Java and Objective-C/C++ code to JavaScript, so that we can run any native code from within JavaScript.

Having network requests done in native will greatly speed up things. We'll talk about how to do that on each platform, and then we'll use the data we got in React Native.

For bridging, this cheat sheet was a big help. In this case, promises were used on both iOS and Android.

From the MDN docs:

The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

So, if everything works well, the operation is either solved with a value, which means it was finished successfully, or rejected, which means it failed. In our case, if the data containing the books is successfully retrieved, we can use them in our app.

iOS 

As for iOS, I've created a native module called NetworkModule, which is written in Swift.

//  NetworkModule.swift

import Foundation

@objc(NetworkModule)
class NetworkModule: NSObject {

  @objc
  func getBooks(_ resolve: @escaping RCTPromiseResolveBlock,
                rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
                //Here, a network request is made, which is either solved with a value or rejected
}

The @objc modifiers ensure that the class and functions are properly exposed to the Objective-C runtime.

To make the module and getBooks() function available to React Native, an Objective-C file was created for this:

//  NetworkModule.m

#import "React/RCTBridgeModule.h"

@interface RCT_EXTERN_MODULE(NetworkModule, NSObject)

RCT_EXTERN_METHOD(getBooks: (RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)

@end

Since we are mixing Swift and Objective C, we also need an extra bridging file to let Swift access the Objective-C files. But if you add a Swift file through the File>New File menu, Xcode will make this header file for you. Here, you have to import RCTBridgeModule.h:

#import <React/RCTBridgeModule.h>

Finally, in the getBooks function, a simple request is made to make an HTTPS request using the URLSession API.

You can find my code here, where I fetched data and then extracted the necessary information in native, in our case titles, for displaying in the list.

However, you can easily find step-by-step guides for making simple requests in Swift, like this one.

Android 📱

Bridging on Android is a little different.

I’ve created the NetworkModule written in Java inside android/app/src/main/java/com/networkbridge folder. This class will extend the ReactContextBaseJavaModule class. Module contains getName() function that returns the name of the module and our function called getBooks() that we want to export to React native:

// NetworkModule.java

@ReactMethod
public void getBooks(Promise promise) throws IOException {
//Here, a network request is made, which is either solve with a value or rejected
}

All native module methods that can be called from JavaScript must have the @ReactMethod annotation.

In the same folder, we must create a new Java Class called MyAppPackage.java. This class is needed to register the native module with React Native by adding it to ReactPackage. More details you can find in the docs.

package com.networkbridge;

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 MyAppPackage implements ReactPackage {

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

    @Override
    public List<NativeModule> createNativeModules(
            ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();

        modules.add(new NetworkModule(reactContext));

        return modules;
    }

}

Besides that, you must search in your MainApplication.java the method called getPackages() and add MyAppPackage to the list like this:

@Override
protected List<ReactPackage> getPackages() {
  @SuppressWarnings("UnnecessaryLocalVariable")
  List<ReactPackage> packages = new PackageList(this).getPackages();
    packages.add(new MyAppPackage());
  return packages;
}

The request is pretty similar to iOS and the code can be found here using a helper method called getJSONObjectFromURL with HttpURLConnection.

Now that iOS and Android have been set up, let's move on to React Native and figure out how to get these network responses.

React Native ⚛️

First, you must import NativeModules in App.js and then access the module:

import { NativeModules } from 'react-native';
const { NetworkModule } = NativeModules;

Since I’ve already mentioned that we’re using Promises, we will use await inside the async function fetchBooks() to call the module’s getBooks() and wait for it to finish. The async/await syntax is used to avoid having to explicitly set up promise chains.

Furthermore, this function is called inside a useEffect hook, which will only run once because the second parameter is set to an empty array. The useState hook is used to save the books, which can then be used in the FlatList.

let [books, setBooks] = useState([]);

  useEffect( () => { 
    async function fetchBooks() {
      try {
        const fetchedBooks = await NetworkModule.getBooks();
        const mappedBooks = fetchedBooks.map((book) => ({id: uuidv4(), title: book }));
        setBooks(mappedBooks);  
      } catch (e) {
        console.error(e)
      }
    }
    fetchBooks();
}, []);

The titles of the books that were fetched are then mapped to unique ids so that mappedBooks looks like this:

 const mappedBooks = [
   {
     id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
     title: 'Harry Potter: Exploring Hogwarts',
   },
   {
     id: '58694a0f-3da1-471f-bd96-145571e29d72',
     title: 'Harry Potter and Philosophy',
   },
   {
     id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28bc',
     title: 'The Ultimate Harry Potter and Philosophy',
   },
   {
     id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f6d',
     title: "Harry Potter, You're the Best!",
   }
…
 ];

When the books' status has been updated, FlatList will work this way:

<FlatList
        data={books}
        renderItem={renderItem}
        keyExtractor={item => item.id}
      />

And…Voilà! 🪄

We can now run the app on either Android or iOS and see the titles in the list. 🥳

ios-simulation.gif


Final Thoughts 💭

If you didn’t manage to download the code, you can find it here.

As I mentioned at the beginning, my primary goal was to show how to move network requests in native code on both iOS and Android, bridging them to react native, which might have a significant impact on the speed of an app. However, I hope this helped you become more comfortable with native modules as well.

Until next time, cheers! 😊 🥳 🥂