Virtual Device Wall for Cordova apps

Test Cordova apps on multiple devices at the same time - [link]

One of the biggest benefits of using Cordova for writing mobile apps is the ability to use the same app to target multiple platforms like iOS and Android. To ensure that the apps work well on all the supported platforms, the test matrix needs to have many devices to test the app on.
Most development shops usually have something like a "device wall".

This is usually a range of phones and tablets connected to computers via USB cables, mounted on a wooden board of a wall. Setting this common infrastructure or maintaining it is usually not easy. While they may be great to run automated tests, manual testing basically means that a tester has to interact with each device individually - a process that is time consuming.
Over the weekend, I hacked together a solution that creates a "virtual" device wall. It currently supports Cordova apps and I plan to extend it for use with ReactNative. Here is a video, showing off the capabilities of the virtual device wall.

Using the virtual device wall is simple - simply install the node module called virtual-device-wall in the same directory as your Cordova project. Then run node_modules/.bin/wall from the command line, as described in the project's README.
This command builds the Cordova app, uses the browser-sync capability from the cordova-plugin-browsersync and uploads the app to Once on appetize, the live screens of phones are embedded into a webpage that serves as the device wall.

While this is just an initial prototype with four static devices, the idea can definitely be extended to let the tested add more devices dynamically, or play with other cloud hosted services. You could even hook up your continuous integration system into this system to deliver the "virtual device wall" to your testers for every commit.
The prototype is open source and is hosted on github. You can try it out using npm, or following instructions on the project's home page.

If you think this is a cool idea and would like to try it out, send me a ping and I would love to help you out. There are a bunch of features (like more devices, CI integration, etc) that I am planning to add, and I would love to prioritize what I do first :)

Crash Analytics and Feedback for ReactNative Apps

As I am starting to use ReactNative to write non-trivial, production facing apps, I realized that I needed solutions that would help me share beta versions of my app, get user feedback and analyze crash reports. For native apps, I had used HockeyApp and loved it for its simple API and multi-platform support.
My workflow with HockeyApp for native apps was pretty simple.
  1. Build the native app on a continuous server like Jenkins or Visual Studio Online.
  2. Upload the generated APK or iPA file using curl to the HockeyApp url.
  3. Beta testers use the HockeyApp for iOS or Android to download this beta app, and test it.
  4. HockeyApp SDK was integrated into my app for getting feedback, analyzing crashes, or notifying any changes that I make. 
I wanted similar capabilities for the ReactNative apps that I build. In my last post, I had written about the Cordova Plugin Adapter for React Native, and I was able to use that to integrate the HockeyApp SDK in app. I used this cordova plugin.

Given that the cordova plugin adapter did most of the setup work, I did not have to manually add permissions or modify my activity as specified in the docs. I just had to do  

$ npm install react-native-cordova-plugin 
$ node_modules/.bin/cordova-plugin add cordova-plugin-hockeyapp.

Then I simply require('react-native-cordova-plugin'); and started using the API as below
  1. Initialize the plugins using cordova.hockeyapp.start(success, fail, token) on componentDidMount.
  2. cordova.hockeyapp.checkForUpdate(success, failure); to check if there are new versions. I ran this on startup of the app, and also had a button to refresh the app.
  3., failure); opened the screen for users to provide feedback about the app and was hooked to a button. 
  4. For testing, I also used the cordova.hockeyapp.forceCrash(); API to simulate crashes. There was a bug in the app that made the app crash on a beta tester's phone and I did get the crash reports. 
The entire code showing each of this functions is available in this gist. I also created a demo video showing the APIs in action.

I was only able to get the HockeyApp working on the AndroidSDK since the react-native-plugin-adapter only works on Android for now. If you would like a version for iOS too, or would like to try integrating the HockeyApp into your ReactNative app, please do ping me and I will be happy to help.

Using Cordova Plugins in React Native (Android)

A tool use Cordova plugins with React Native - link
Apache Cordova has a pretty vibrant ecosystem of plugins. These plugins usually comprise of 2 parts - a native component that interacts with the device APIs and a JavaScript layer that makes these APIs simpler. Creating Native Modules with React Native also has a similar model of operation.
Given that more than 1000 Cordova plugins exist today to access device capabilities from JavaScript, it only seems logical to be able to reuse these for any ReactNative projects. 

This blog is about the internals on how Cordova plugins can be used with ReactNative. The scenario only works on Android for now, as shown in the video below. 

[Link to Video]
Update: Note that the demo adds plugins using node_modules/rncp/cli/cli.js. Now that the module is on npm, you could simply use node_modules/.bin/cordova-plugin add pluginname.

You can use this sample application to test the APIs.

Step 0: Installing CordovaPlugin Adapter

Using native modules in a ReactNative project usually means adding the dependencies in build.gradle, adding the sub project, etc. Instead of doing this for every single cordova plugin, I was able to leverage Cordova's node based plugin manager (called plugman). Hence, you just add the dependencies once to set up the "Cordova Adapter ReactNative" (lets call it CARN).
CARN is on npm and you can install it in the locally in the ReactNative project.

Step1 : Installing the plugins

With CARN installed, you can use it's command line interface to add plugins.
  1. Add plugins using $ node_modules/.bin/cordova-plugin add cordova-plugin-name. This command simply relies on plugman to download the plugin from npm.
  2. Once the plugin is downloaded, plugman looks at the plugin's plugin.xml file to determine any other plugins that need to be installed as dependencies - all these are stored in a assets/xml/config.xml to be used later. 
  3. Plugman then copies over the javascript, java file and JARs to the appropriate locations. These locations are defined in CARN's build.gradle. 
  4. Plugman also looks at plugin.xml to change config files like AndroidManifest.xml to add any activities or permissions that are needed for the plugin to work well. 
  5. Finally CARN combines all the Javascript files from all plugins using Cordova's module builder system (which is similar to browserify). 
  6. This is later "required" by the ReactNative's to start using the plugins. 

Step 2: Using the Plugin

  1. All Cordova plugins are available when CARN's javascript is "required" in ReactNative JavaScript. For example, the API that cordova-plugin-device exposes is typically called using require('react-native-cordova-plugin').navigator.device.getInfo(successCallback, failCallback).
  2. All the Javascript APIs that Cordova plugins expose eventually call a cordova.exec(service, action, callbackID) method, which in turns calls the exec method exposed on the Java side using the WebView.addJavascript Interface. Unlike Cordova, Reactnative does not have a webview. Hence we hijack this method and use ReactNative's Native Modules to expose a corresponding "exec" method on the Java side.
  3. Once the "exec" method on Java is invoked, it starts Cordova's Java PluginManager. This PluginManager consults the assets/xml/config.xml to look at the ClassName for the service (which is the plugin name) and calls the corresponding action on that classname provided by the plugin. 
  4. All results are asynchronous, so the plugin continues working on the method call. A plugin may have to invoke another activity like in the case of a ContactPicker or Camera using startActivityForResult.
  5. The plugin is finally done with its work. In case another activity (like Contact Picker) was  invoked, a contact is picked and our ReactNative's MainActivity is started again with the result from the previous activity. This result passed to the plugin's onActivityResult callback
  6. Now that we have the result, the plugin manager calls the WebView to deliver the result back. In our case, we instead have a MockWebView for ReactNative that uses the RTCEventEmitter to deliver the result back.
  7. We already have a listener on the JavaScript side that looks at the result that also contains a callbackID. We consult our list of callbackIDs, and call the successCallback or failCallback. 
The code itself is pretty straightforward and mostly reuses Cordova's plugman and PluginManager for most of the work.  The github repository has all this code, and the best place to start looking would be the CordovaPluginAdapter for Java side of things, and index.js for Javascript side of things.

Next Steps

The steps described above are Android specific, and I am assuming that we would have a very similar flow for iOS too. Including almost all of Cordova for Runtime may not be not most efficient, and I am working on trimming down the dependencies to make this even faster.
Would love to hear any feedback you may have and any suggestions or contributions for Android, or the iOS implementation.

[1] Note that the demo is on Windows. ReactNative still has issues to work with Windows, and I had to modify some files in ReactNative to get it to work on Windows. 
[2] If  you are using Windows, I would recommend using this Android Emulator - it works with HyperV on and is super fast !!