Building Custom React Native Components From Scratch


April 3, 2015

Building Custom React Native Components From Scratch

As we discover the rapidly growing world of React Native, we learn that it’s entirely possible to build an awesome application that leverages only the React Native code.  While the Facebook team has done a fantastic job of implementing a lot of UIKit out of the gate, it’s conceivable that there will be times when you need to go beyond the boundaries of what React Native out of the box (and soon to be Android Java).  

Perhaps you want access to the recently released Apple ResearchKit, or you want to be able to leverage custom networking utilities like AFNetworking. What we’ve learned from our time with React Native is that the possibilities are endless for the developers wanting to take advantage of this capability.

From what we’ve gathered in our research, there are two types of custom components that you can implement for React Native applications; user interface and utilities. For this article, we’re going to focus on the utility type, as it has less moving parts and is a great way to get your feet wet with building custom component code.

Developing a custom React Native component

The application we’ll develop is rather simple.  It uses a custom React Native-enabled Objective-C class to write a random number to a file in your iOS application’s document directory and is a modified version of the custom PhoneGap plugin for iOS that I wrote in 2013.  This class writes data in a non-encrypted format, so please don’t use it in production.

In a nutshell, this demo application will take a filename as input from a React Native TextInput component. The user will be able to load data from the Phone’s filesystem based the file name.  

Upon pressing Save, a random number string will be generated via a simple  Math.random().toString() method calls. The Load button performs the inverse, retrieving the contents of the file described in the TextInput and displaying it below.  We do all of this as well as some basic exception handling as demonstrated in the animated gif below.

image011

Instead of going through the rigmarole of the creating a React Native Project, we’ve decided to assemble an example and placed it in for you to easily checkout, execute and investigate for us to walk through together.

This github repo we created has two subdirectories, Phase1/ and Phase2/, both designed for you to review the implementation of each major phase of this article, as detailed above.

We’re going to kick this off with looking at the project structure itself.

A brief look at the project

Our example project is a generic React Native project with a small twist: instead of placing all of our React JS code at the root of the project, we place it inside of a folder known as jsx/.

The process for doing this is quite simple, as demonstrated below.

image012

We perform this change for cleanliness of the project and suggest that anyone developing React Native applications consider something similar for it allows you to point your JSX editor to a directory that only contains the related JSX code, giving you the opportunity to visually locate files without having all of that XCode and Objective C mess cluttering your JSX editor’s UI.  Proactive organization will only benefit our app development efforts.

Because we changed project structure slightly, we had to inform React Native.  We do this by updating AppDelegate.m.  The process for doing this can be observed below.

image013

Before we can write any code, we’re going to need to inject our custom classes into the XCode project.  There are three great reasons to do this via XCode and not via some external editor.

  • When creating a Cocoa Touch class via this tool, the associated header file (.h), and program file (.m) are created with one process.  

  • XCode will generate the class where we can define our class’s superclass, NSObject.

  • XCode will include our newly added class program to the list of files to be compiled (and linked).

Once the file is created, we will have to require the RCTBridge.h header file and leverage RCTBridgeModule as a protocol.

We will perform this action using XCode’s “New File” menu system as illustrated below.

image014

The Project is now structured as follows.

image015

Now that we’ve got the basic scaffolding stuff out the way, we can actually start writing code.  That’s why you’re here, right?

Phase one: Objective-C to JavaScript Bridge

The first thing we must do is fill in the MCFileWriterUtil.h header file.

The header file consists of a very simple class definition, without the method signatures to do the work required. This is because we’re going to leverage special macros to define and expose our methods to the React Native bridge.

With the header written, we can go on to write the meat of the Objective C class scaffolding.

As you can see, the contents of the write and read methods are nearly identical at this stage.  They are so because they are only setup for this learning phase.

There are three key takeaways from this phase of defining our custom Objective C class

  • The first key to look at is line #6, where the RCT_EXPORT_MODULE() macro is called. This macro is required to expose our custom module to the React Native bridge. We’ll see later on where we’ll require this module via JavaScript.

  • Next is the call to the RCT_EXPORT_METHOD() macro.  This macro is where the magic glue whose purpose is to expose our Objective C method signature to our future JavaScript code, hence the name “Objective C to JavaScript bridge”.  Technically, iOS doesn’t (as of iOS 8)  have an out of the box Objective C to JavaScript bridge (OS X does, though), so developers have been left to create their own bridge.  When comparing this to developing custom Phonegap iOS plugins (as detailed in this article), the Facebook team hit a home run with this implementation!

  • Last, the successCallback execution is something that we’ll also focus on. It’s worth noting that when calling the success or failure callbacks, you’ll need to pass an array of arguments to be executed on the JavaScript side.  Here, we’re just passing an array with one element, which happens to contain a string literal.  Again, this is just for demonstrating the bridging capabilities of the React Native Objective C to JavaScript bridge.

To implement this Objective C code we created, we’ll need to write some JavaScript code. We will add the block below to the project’s jsx/index.ios.js file.

The first item to focus on is the requiring of our module, MCFileWriterUtil from the React Native NativeModules module. We did not write a JavaScript class with that name.  The React Native Objective C to JavaScript bridge is what is responsible for making this available to our JavaScript code.

From there, we can execute the method MCFileWriterUtil.writeFile(), where we pass in four arguments: file name, file contents, error and success handlers, both of which  simply display a UIKit alert dialog with the string that is returned from the Objective C MCFileWriterUtil  writeFile method.

If you train your eyes to the Objective C code we have our write method for the MCFileWriterUtil class, you can see that there exactly four arguments, two strings and two functions.  When we execute a bridged method, we absolutely must call it using the defined arguments, or the bridge will throw an exception at you.

image016

From here, we can run the project and see that the bridge is doing its job and we can execute Objective C methods easily from JavaScript.

image017

OK, cool. We got this bridge running.  Our next job is to fill in the full contents of MCFileWriterUtil.m and write the code for the React JS implementation of our Objective C utility.

Phase two: Doing the real work

For Phase two, we are writing the actual Objective C code that does the actual work; writing and reading a file.

Here is the full implementation of MCFileWriterUtil.m.  It’s heavily commented, so I will not dive into the details of how things are working.

Before we move on to look at the JavaScript implementation, I would like to again point out that the React Native Objective-C to JavaScript bridge is absolutely brilliant.  If you look at the final if/else condition, you can see how easy it is to communicate with the JavaScript world.  

For both conditions, we generate an NSDictionary literal (think JavaScript object, for you JS devs) to be used as an argument for the success and failure callbacks.  What you’ll see is that those dictionaries will automatically get transformed as JavaScript objects on the JavaScript side.

Speaking of which, let’s dive into the JavaScript implementation of our newly expanded Objective-C class. The code below is a long read, but I’ve commented it very well and the flow is easy to recognize.

There you have it! Developing Objective-C called by JavaScript has never been easier than with React Native.

Access this project in it’s entirety from our GitHub repo.

The future of developing plugins will change

It’s worth noting that one of the clear advantages of Facebook’s approach to releasing React Native is its Open Source licensing.  This has allowed near-biblical flood of community discussions, and a quite a lot of pull requests from enthusiasts who are willing to contribute their time and energies to better the project.

One such project is aptly known as “Pull Request #405”, where Joe Stanton has contributed a scaffold generating utility for custom React Native iOS code. We’re hoping that this pull request is merged soon and once it is, we’ll update our example (and this blog post) accordingly.

Resources to learn more

The Facebook React Native team has done a fantastic job with documentation for the framework. We’re thankful that they’ve taken the time to document the patterns for developing custom React Native components, which can be found here.  We highly suggest reading through it as it covers all of the patterns for developing custom React Native components for iOS.

In closing here’s a list of places people are discussing React Native:


jay copy
Jay Garcia is co-founder and managing director at Modus Create. He is a U.S. Air Force veteran with 20 plus years of technology and consulting experience in leading RIA development for companies around the world. He is co-organizer of the NoVa.JS and NYC.JS meetups, and is actively involved in the software communities that provide business frameworks and technologies, which enable rich mobile and desktop web experiences.

  • ed

    thanks for this tutorial, the best I have found about bridging.
    It is not clear to me why you inherited from RCTViewManager instead of NSObject as stated in the official doc. Can you please clarify?

  • Alex Kotliarskyi

    Few suggestions: use RCTMakeError for constructing error callback argument, at least it has somewhat standard structure with “message” field. Don’t use stuff like me = this, use ES6 lambdas (fat arrow functions) instead, they are automatically bound to the context. Also would be nice to include link to official docs on this topic.

    • JayGarcia

      @alexkotliarskyi:disqus, RCTMakeError is currently defined in RCTUtils.h, which is not included in RTCBridge.h. It seems that that function is not widely used by the framework.

  • https://artboost.com/ Kim Døfler Sand Laursen

    Awesome tutorial!!! love the humor 😉
    Just tried it myself. Made a module for using the native Facebook and Twitter share:
    https://github.com/doefler/react-native-social-share

    What do you think?

    • JayGarcia

      Absolutely fantastic job =)

      • https://artboost.com/ Kim Døfler Sand Laursen

        Thank you so much Jay :)

  • http://josebalius.com Jose Garcia

    Awesome tutorial, thanks!

  • http://transistorsoft.com Chris Scott

    Useful tutorial, thanks Jay. I got my BackgroundGeolocation plugin de-coupled from Cordova and working with React Native yesterday.

    http://christocracy.github.io/cordova-background-geolocation/

  • Dave

    FYI, Pull Request #405 has been merged in v0.4.4 :) Great article!

  • Kevin DeLeon

    Just as a bit of a proof of concept, I re-created your code in Swift just to see if I could do it. The documentation on the React Native site seems a bit lacking in that area, and I just wanted to see if I could get it going. I’ll probably write a small blog post at some point, and I’ll definitely link back to your original article! Anyway…here’s the code if you want to give it a go. I didn’t try your original code, but mine does bomb if you press either button before ever entering a filename…afterwards…it works as expected (even producing appropriate errors if you leave out the filename after the fact). Gonna see if I can figure that out…could be a problem in my code somewhere. My code can be found here: https://github.com/kevindeleon/react-custom-swift-component

  • lifeleanote

    Thanks!

  • Kevin DeLeon

    Great article Jay! Just as a proof of concept, I re-created this in Swift over on my blog and obviously link back to, and give you all the credit! I prefer coding in Swift and was excited to see that the React Native community has implemented the ability to export Swift components as well. If interested, check it out here http://kevin-deleon.com/2015/05/custom-react-native-components-in-swift/

  • riyuk

    Great article! Do we need to do changes on the `jsCodeLocation` too when pre-bundling?

  • http://alper.nl Alper Cugun

    So if I understand it you can only really bridge class methods? I haven’t seen an example yet that creates an Objective-C/Swift class on the javascript side and calls methods on that.

    • ModusJesus

      Hi Alper,

      Are you saying something like “new MySwiftClass().doSomething();”, which instantiates MySwfitClass and executes the doSomething method on that?

      • http://alper.nl Alper Cugun

        Yeah, is that possible?

  • Raj Vansia

    nice post dude. quick question what do u use to Make those cool gifs

    • https://wasin.io Wasin Thonkaew

      I use LICEcap. Specify the area to capture on screen, set framerate, then it gets counted down 3-2-1 before begin capturing. Works great for me. Try it.


What We Do

We’ll work closely with your team to instill Lean practices for ideation, strategy, design and delivery — practices that are adaptable to every part of your business.

See what Modus can do for you.

LET'S GET STARTED

We're Hiring!

Join our awesome team of dedicated engineers.

Loading...

APPLY NOW