Making libraries for React Native

Part I: Android

This is the first part of my articles series.

React Native is getting stronger day by day. But this doesn’t mean that every little piece of functionality you need is already out of the box.

While I was working for a project for Gbox, I ran into several situations that were faced with great libraries contributed by the community: video player, download services, even environment variables. Perhaps, there was one feature that didn’t have an easy workaround.

Chromecast.

There were two options at the moment, but those didn’t have any updates in the last months, and more important, none of them had android support. Waiting for others to fix this problem was not an option.

Wrapping native code it’s pretty straightforward. React Native’s documentation is good enough to accomplish this goal, and there’s tons of articles that cover this topic in a pretty fancy way. 
Although, what if you want to share your trick with others?

This series of articles covers this problem, thinking around the Chromecast issue. We will wrap native code for Android and iOS, create a common API for both platforms, and publish the solution to npm, so others developers can save some time thanks to our solution.

You know, it feels awesome when you contribute back to the community.

(Happy ending of the story, yes, I created the library!)

Wrapping the code

Let’s get the panorama. Chromecast SDK exists for both Android and iOS platforms. What we want to accomplish are the next goals

  1. Use the native SDKs.
  2. Choose the main tasks we want to use. Chromecast can achieve a lot of functionality, and cover each of them could be a extremely exhausted.
  3. Generate a common API to use in React Native.
  4. Publish our library in NPM.

Some shortcuts ahead

You know, when I started my library, I researched like crazy other examples. Fortunately, since then, a guy already have created a generator for us.

React Native generates libraries in its CLI too, but so far, it is for iOS only.

Let’s install the generator.

npm install -g react-native-create-library

And now, let’s create our library!

react-native-create-library -—platforms ios,android ChromecastLibrary

So far, we’ve created a new library that will support iOS and Android.
We could add Windows support too, but without any Chromecast SDK, it is pointless.

Keep it in mind tho!

Adding the Android SDK

Before starting coding, let’s take a look to the one file inside the android folder.

There’s a difference between this build.gradle and the one you have in a common React Native project.

apply plugin: 'com.android.library'

As you guessed, this line help us to use this project as a library, and, when others use your project, it will compile correctly.

Let’s stop for a minute. What do we need before coding our wrapper?

Yup: the Chromecast SDK.

For this library, we will use the Cast Companion Library. It uses the SDK and enhances it with a lot of syntax sugar. Thinking beyond this article, if we want to support more functions, or event controls, this will be extremely helpful.

To add it to our library, go to the build.gradle file. Add the next changes:

dependencies {
compile 'com.facebook.react:react-native:0.20.+'
compile 'com.google.android.libraries.cast.companionlibrary:ccl:2.8.4'
}

Now, we’re ready to code a little bit more. Let’s wrap it!

Wrapping the SDK

react-native-create-library already created some files for us to start playing. One is RNChromecastLibraryModule, and the other one is RNChromecastLibraryPackage.

RNChromecastLibraryPackage is the one that exposes our native code to React Native. And RNChromecastLibraryModule is where our implementation begins. Let’s take a look on it.

So far, it is pretty sample. We have a getName method, where we can change the way we’re gonna invoke this library. But, apart from that, it’s pretty useless. Let’s add some value here.

@ReactMethod
public void startScan() {
Log.d(REACT_CLASS, "start scan Chromecast ");
}

Hey, we have just created our first method! It is still useless, but a good step forward.

@ReactMethod mark the method as public for React Native. These are the ones that can be use in your javascript project.

Now, let’s go further. According to the documentation, we need to add some configuration options to the Chromecast. Let’s create a function for this situation.

public CastConfiguration getCastConfig(){
CastConfiguration options =
new CastConfiguration.Builder(
CastMediaControlIntent
.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID)
.enableAutoReconnect()
.enableNotification()
return options;
}

Don’t hesitate too much in the details. Probably, the more important thing is DEFAULT_MEDIA_RECEIVER_APPLICATION_ID constant.

Normally, Chromecast uses an app id to load custom assets and more. But for this case, it is ok to use the default value.

Did you notice we are not using @ReactMethod? This is not a method we want to use in our library, it is just a helper. So we want to keep it as a private method.

Time for some sugar!

@ReactMethod
public void startScan() {
final CastConfiguration options = getCastConfig();
VideoCastManager.initialize(getCurrentActivity(), options);
mCastManager = VideoCastManager.getInstance();
mCastConsumer = new VideoCastConsumerImpl() {
};
mCastManager.addVideoCastConsumer(mCastConsumer);
Log.d(REACT_CLASS, "start scan Chromecast ");
}

Let’s keep the explanation simple, as we want to focus in the general idea of how to create React Native libraries.

Chromecast SDK requires a VideoCastManager, which is a singleton. To be initialized, it requires an Android activity, and the options we already generated.

mCastManager is the one that get the singleton from VideoCastManager, and mCastConsumer has the implementation of the Chromecast events.

Now we need a Chromecast event. Inside new VideoCastConsumerImpl(){}

@Override
public void onCastAvailabilityChanged(boolean castPresent) {
WritableMap deviceAvailableParams = Arguments.createMap();
deviceAvailableParams
.putBoolean("device_available", castPresent);
emitMessageToRN(
getReactApplicationContext(),
"GoogleCast:DeviceAvailable",
deviceAvailableParams
);
}

We’re adding code related to React Native! WritableMap let us create a data that can be read by React Native. It is helpful to send information from the native side to the javascript side.

When the SDK detects some Chromecast around, it will send us a notification, and we send this information to our app. This can be used to show or hide the Chromecast icon, for example.

We have a pending task tho. Let’s implement emitMessageToRN method.

private void emitMessageToRN(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}

As you can see, this simple method is just a helper to send data to React Native.

There’s two ways to send data to React Native: emitters and promises. Both were created around the nature of the RN’s async world.

Javascript and native sides work in async threads to gain a boost in performance. And native side is slower than Javascript, so, if you send data from one side to the other, you’ll run into a race condition.

In this particular case, we’re broadcasting events. Our wrapper communicates with Chromecast, waits for a response, and then sends the data to our app.

The little details

If we test our code, it will fail. We missed a big detail!

Remember that we talked about the async nature of React Native? Well, most of the Chromecast’s events need to run in the main thread.

Fortunately, there’s a workaround.

@ReactMethod
public void startScan() {
final CastConfiguration options = getCastConfig();
UiThreadUtil.runOnUiThread(new Runnable() {
VideoCastManager.initialize(getCurrentActivity(), options);
mCastManager = VideoCastManager.getInstance();
mCastConsumer = new VideoCastConsumerImpl() {
...
...
};
mCastManager.addVideoCastConsumer(mCastConsumer);
Log.d(REACT_CLASS, "start scan Chromecast ");
});
}

UiThreadUtil.runOnUiThread to the rescue! It let us run code on the main thread. Problem solved!

iOS, NPM and beyond

We haven’t finished yet!

Next chapter will cover the case for iOS, the creation of a common API for both SDKs, and publishing the library in npm.

There are a lot more events to cover for Android, but I will let you continue with them.

Want to take a look into my Chromecast library for React Native? Feel free to open a pull request! Or even ask me a question. We’re just getting started!

Show your support

Clapping shows how much you appreciated Carlos López’s story.