One advantage you often hear about with writing React Native applications is the ability to drop down into the native code whenever necessary.

At first, the thought of having to write native modules intimidated me, but as it turns out, it's pretty simple. This post aims to de-mystify writing native modules and show how easy it can be with React Native.

Can you hear me now?

On a recent client project at Promptworks, we needed to be able to prevent the user from performing an action that would play a sound if their volume was not turned up high enough. In order to do that, we needed to grab the device's volume, but no such method existed in React Native or in any 3rd party packages (at the time).

Thankfully, getting the system volume is not difficult in iOS or Android, so it makes a perfect example of a simple native module. This post will walk through making a native module for iOS, for Android, and the JavaScript code we can use in our app to call our new modules. We'll create a module with one method, getSystemVolume , that returns the system's volume on a scale of 0 to 1.

If you'd like to jump straight to the example code, it's all available on Github.

Note: We're using version 0.40.0 of React Native for this example. Due to the fast moving nature of React Native, your mileage may vary if React Native has moved on!

Creating an iOS module with React Native

We'll begin by adding the functionality to our iOS app.

We'll need to add two files to our project: a header file and its implementation file. In Objective-C, the header file (with a .h extension) will declare the public interface of our class, while the implementation file (with a .m extension) will contain the actual implementation of our class. In our React Native module, we have a bit of a special case: we need only define the module in our header file as each method we want to expose is done so via the RCT_EXPORT_METHOD macro in the implementation file only.

Since we're dealing with volume capabilities, we'll use the imaginative names ios/<ProjectName>/Volume.h and ios/<ProjectName>/Volume.m (see this commit in our example code).

First up, the header file, Volume.h :

#import "RCTBridgeModule.h" @interface Volume : NSObject <RCTBridgeModule> @end

And its implementation file, Volume.m :

#import "Volume.h" @implementation Volume RCT_EXPORT_MODULE(); @end

NOTE: Since we didn't pass any arguments to RCT_EXPORT_MODULE , the native module will take the name of our class, Volume .

You can create these files with your editor of choice, but if you're using something other than XCode, be sure to add the files to the project in XCode ( File > Add Files to "<ProjectName>"... ). This will make some changes to project.pbxproj to include our new module.

Now, we have a bare-bones native module -- it doesn't do anything interesting yet, but it's good to pause at this point. Unlike adding a JS module, the iOS app needs to be re-built before our native module is available (you'll also need to re-build after each change). Now's a good time to do that to ensure your app configuration is up-to-snuff.