Bridging the Gap: Native Modules and React Native

Randall Furino, Senior Software Engineer

Cross-platform development languages such as React Native are truly changing the game. From developers and designers to scripters, folks in QA and product owners, everyone wants a one-to-many relationship from codebase to platform. Who wouldn’t want to write the source once and release on both iOS and Android? It sounds great, right? Well, it is, and it’s only getting better. Sadly, therein lies the rub. It seems that a lot of people, possibly you or your team, have been burned by one of React Native’s predecessors and let that experience form an opinion of all cross-platform systems. There have been plenty of promises and ‘game-changers’ over the years — new frameworks, languages and wrappers that all claimed to fix everything the ones before them didn’t. Inevitably, issues would arise that were either genuinely prohibitive or, more commonly, would scare off potential developers and clients.

In response to all the uncertainty and doubt that comes along with a fundamental shift in thinking, there’s an inherent desire to justify those feelings. Anyone can see this by doing a quick search for the pros and cons of React Native. I promise if you do that, you’ll spend your time repeatedly reading through the same bullet points. This post is going to deal with one of those points — one of the largest misrepresentations, and creators of unnecessary worry, associated with React Native.

<Music style={styles.Dramatic}>  Native Modules…  </Music>

It’s unfortunate. This is often used as a barrier to entry, a hindrance to starting a React Native project. Put simply: it shouldn’t be. Native modules are not scary. 

Yes, iOS is iOS and Android is Android. They are their own platforms with their own unique internal structure. Sure, they may mimic functionality, but each is inherently different from the other. This means that, regardless of chosen development language, an app may need to access features that are specific to the platform. If a mobile app is being developed in a native language, unique features are available through Swift or Java, but if it is being cross-developed, those features need to be accessed through a native interface. Blocks of code exist only to allow other blocks of code to talk to each other. The catch is that these blocks have to be written in both languages.

Honestly, that’s it. That’s the big ‘gotcha’ for a lot of people. Some cross-platform projects will never get the chance to be successful because this gap keeps them from launching. As much as 80% of code has the potential to be reused between mobile systems, but the perceived difficulty of building and maintaining a native interface layer can drop that number to zero. 

If a project requires native Bluetooth or ARKit, notification systems or something else entirely, it can be done through React Native, you just need to bridge the gap between the RN layer and the native layer. If you’re thinking ‘no, thank you, I’d prefer to write the whole app twice,’ that’s certainly your choice, however, if it’s because of native modules, I encourage you to reconsider.

OK, I will concede that mixing and maintaining native code in a multiplatform project certainly seems like an area for concern, but the truth is, it’s not. Or rather, it’s not anymore.

Fortunately, Cardinal Peak has been in this ecosystem for quite a while and has learned a lot during that time. Over the last three years, developers have been looking into emerging tech and markets that, by design, improve efficiency and save money; React Native does both. Even with platform dependencies, compared to developing two native apps in tandem, we’ve seen productivity gains and cost savings from 20% to 50%, making it our go-to system for mobile development. Native module integration doesn’t scare us because we’ve done it before. We’ve worked through various implementations, some successful, some less so, and doing so has allowed our team to grow with the system. 

Let’s briefly walk through what it actually takes to implement native code in React Native (RN). Everyone loves  the internet of things (IoT), so let’s use Bluetooth as an example. Part of an IoT app is going to involve communication with an external device of some sort. For this case, we’ll say it’s a simple Bluetooth peripheral. The first part of any Bluetooth or Bluetooth Low Energy  process is scanning, so let’s look at how that’s handled natively. For both platforms, the first thing to do is access the native Bluetooth objects.

iOS:

self.centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)

Android:

final BluetoothManager manager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter adapter = manager.getAdapter();

Even in those couple lines of code, we can see why native support is required. iOS wants to access and cache the CBCentralManager (Core Bluetooth Central Manager) object while Android wants its own BluetoothManager. A cross-platform system needs to instantiate the proper object. Android has no idea what a CBCentralManager is and couldn’t care less. Likewise, iOS has no concept of any Android Bluetooth management and React Native doesn’t know about either of them. From the start, there is no structure to directly access bluetooth on either platform. Still, let’s finish the current path. Now that we have the object, let’s look at scanning.

iOS:

func beginScanningForDevice() {
  self.centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)
  self.centralManager.scanForPeripheralsWithServices(nil, options: nil)
}

func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [NSObject: AnyObject]!, RSSI: NSNumber!) {
  // Peripheral discovered

  // Perform connection or other logic

  // Stop scan
}

Android:

private void beginScanningForDevice() {
  scanner = adapter.getBluetoothLeScanner();
  scanner.startScan(filters, settings, scanCallback);
}

public void onScanResult(int callbackType, ScanResult result) {
  // Peripheral discovered

  // Perform connection or other logic

  // Stop scan
}

Again, we can see that both iOS and Android are doing the same thing in their own unique way. Both are initializing their scan objects, starting a scan and responding to the results of the scan. Granted, it’s a bit of a simplification, but the core difference is that one is iOS and one is Android. So, just like before, React Native needs to replicate that behavior per platform. 

With RN only knowing what you tell it to know, developers need to expose any desired functionality. This is accomplished with RCT bridges (ReaCT), and those are exactly what they sound like: pieces of code that bridge the communication layer from React Native to true-native. Fortunately, Facebook provides developers with the scaffolding for these bridges in two core classes; RCTBridgeModule and ReactContextBaseJavaModule.

iOS:

// MyNativeModule.m

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

@interface RCT_EXTERN_MODULE(MyNativeModule, NSObject)

RCT_EXTERN_METHOD(startBTScanning:(NSString*)param1)

+ (BOOL)requiresMainQueueSetup
{
  return YES;
}

@end

Android:

// MyNativeModule.java

public class MyNativeModule extends ReactContextBaseJavaModule {
  // ...
}

Extending iOS and Android classes from those cores sets up the foundation of the bridge. Once exported, RN will need to know how to talk to the bridged objects, and these class extensions make that possible. Now, continuing the Bluetooth example, let’s connect this to React Native for both iOS and Android. For this, we need an RCTBridge telling it what functionality is available and from whom. Under the covers, this is simply a native class exporting itself and a list of methods that RN can call/call back. We can see that in the code snippets below. 

iOS:

// MyNativeModule.swift

import Foundation

@objc(MyNativeModule)
class MyNativeModule : NSObject {  
  @objc func startBTScanning(_ param1: String?) {
    // Call native functionality for Bluetooth scanning
    self.beginScanningForDevice();
  }

  func beginScanningForDevice() {
    // ...
  }
}

Android:

// MyNatveModule.java

public class MyNativeModule extends ReactContextBaseJavaModule {
  public MyNativeModule(ReactApplicationContext reactContext) {
    super(reactContext);
  }

  // Defines the name of the native module in Javascript code
  @Override
  public String getName() {
    return "MyNativeModule";
  }

  @ReactMethod
  public void startBTScanning(String param1) {
    // Call native functionality for bluetooth scanning
    beginScanningForDevice();
  }

  beginScanningForDevice() {
    // ...
  }
}

And now that the structures exist, we can use simple accessors from React Native to utilize the RCTBridge and call the platform-specific Bluetooth code.

*It’s worth noting that for Android, there are a couple more steps that won’t be detailed here. For quick reference, those steps include registering the custom Android package and updating the main application getPackages function. Below is the Javascript code that imports the native module and calls the startBTScanning function – the same Javascript is used for both iOS and Android:

import { NativeModules } from 'react-native';

NativeModules.MyNativeModule.startBTScanning('string parameter 1');

The examples provided here are stripped down and simple, but they can be — bridging isn’t as complicated as some folks make it out to be. Of course, the more complexity present in the application, the more complexity there will be in the native access, but fortunately, the framework allows for that. Callbacks to JavaScript, promise fulfillment, even custom module generation for preexisting/legacy code are all features available to React Native projects through bridging native modules. 

Cardinal Peak has consistently and successfully utilized React Native for our clients and in ways far more involved than the example above. 

  • Streaming audio and video on both iOS and Android using GStreamer, including OpenGL rendering.
  • Custom BT and BLE logic, including a two-way pub/sub event system allowing updates from the native layer to RN.
  • Specialized network layer to support self-signed HTTPS certificates. 
  • Homegrown third-party support for native UrbanAirship libraries before even they had RN libraries for them.
  • Integration with pre-existing libraries written in Java, Swift and Objective-C, utilizing native UI elements not directly exposed by the framework, and legacy C++ libs interpreted through the JNI or through Objective-C.

That’s it. No uncrossable canyon-size native module gaps to stop you. Just build a bridge from point A to point B and then walk as much data across it as necessary. 

Cardinal Peak’s developers have had great success with React Native for our clients because of native module integration, not in spite of it. 

Cardinal Peak
Learn more about our Audio & Video capabilities.

Dive deeper into our IoT portfolio

Take a look at the clients we have helped.

We’re always looking for top talent, check out our current openings. 

Contact Us

Please fill out the contact form below and our engineering services team will be in touch soon.

We rely on Cardinal Peak for their ability to bolster our patent licensing efforts with in-depth technical guidance. They have deep expertise and they’re easy to work with.
Diego deGarrido Sr. Manager, LSI
Cardinal Peak has a strong technology portfolio that has complemented our own expertise well. They are communicative, drive toward results quickly, and understand the appropriate level of documentation it takes to effectively convey their work. In…
Jason Damori Director of Engineering, Biamp Systems
We asked Cardinal Peak to take ownership for an important subsystem, and they completed a very high quality deliverable on time.
Matt Cowan Chief Scientific Officer, RealD
Cardinal Peak’s personnel worked side-by-side with our own engineers and engineers from other companies on several of our key projects. The Cardinal Peak staff has consistently provided a level of professionalism and technical expertise that we…
Sherisse Hawkins VP Software Development, Time Warner Cable
Cardinal Peak was a natural choice for us. They were able to develop a high-quality product, based in part on open source, and in part on intellectual property they had already developed, all for a very effective price.
Bruce Webber VP Engineering, VBrick
We completely trust Cardinal Peak to advise us on technology strategy, as well as to implement it. They are a dependable partner that ultimately makes us more competitive in the marketplace.
Brian Brown President and CEO, Decatur Electronics
The Cardinal Peak team started quickly and delivered high-quality results, and they worked really well with our own engineering team.
Charles Corbalis VP Engineering, RGB Networks
We found Cardinal Peak’s team to be very knowledgeable about embedded video delivery systems. Their ability to deliver working solutions on time—combined with excellent project management skills—helped bring success not only to the product…
Ralph Schmitt VP, Product Marketing and Engineering, Kustom Signals
Cardinal Peak has provided deep technical insights, and they’ve allowed us to complete some really hard projects quickly. We are big fans of their team.
Scott Garlington VP Engineering, xG Technology
We’ve used Cardinal Peak on several projects. They have a very capable engineering team. They’re a great resource.
Greg Read Senior Program Manager, Symmetricom
Cardinal Peak has proven to be a trusted and flexible partner who has helped Harmonic to deliver reliably on our commitments to our own customers. The team at Cardinal Peak was responsive to our needs and delivered high quality results.
Alex Derecho VP Professional Services, Harmonic
Yonder Music was an excellent collaboration with Cardinal Peak. Combining our experience with the music industry and target music market, with Cardinal Peak’s technical expertise, the product has made the mobile experience of Yonder as powerful as…
Adam Kidron founder and CEO, Yonder Music
The Cardinal Peak team played an invaluable role in helping us get our first Internet of Things product to market quickly. They were up to speed in no time and provided all of the technical expertise we lacked. They interfaced seamlessly with our i…
Kevin Leadford Vice President of Innovation, Acuity Brands Lighting
We asked Cardinal Peak to help us address a number of open items related to programming our systems in production. Their engineers have a wealth of experience in IoT and embedded fields, and they helped us quickly and diligently. I’d definitely…
Ryan Margoles Founder and CTO, notion