June 04, 2017
By Alexis Campailla
Jxcore,
Nodechakracore,
Nodejsmobile
Over the last few months, Janea Systems has been working on a port of Node.js with ChakraCore to iOS. Now that things appear to be working well, we have created a demo app that showcases this work, and we’d like the feedback of the developer community to help us shape the future of the project.
UPDATE [Oct 3rd, 2017]: The work described in this article has culminated in the release of Node.js for Mobile Apps: a full-fledged Node.js runtime for Android and iOS. Read the announcement.
One of the many strengths that Node.js can claim under its belt is its ubiquity. From server to desktop to IoT, the Node.js runtime is available and generally well-supported on an impressive list of operating systems and architectures.
Two major platforms, however, are conspicuously absent from the list: iOS and Android.
Why is that?
The reasons are both historical and technical. For one, Node.js was originally designed as a server platform, with assumptions that don’t always fit the app model and the unique challenges of mobile operating systems. But this reason alone would not have prevented developers from at least experimenting with Node.js on mobile. In fact, there have been a few reports already of people running Node.js on Android.
One issue, however, has completely prevented any attempts to make Node.js work on iOS: Apple does not allow Just-In-Time compilation on iOS (except for its own JavaScriptCore engine). The JavaScript engine that powers Node - Google’s V8 - relies on JIT compilation to execute JavaScript (even with the addition of the Ignition interpreter, V8 still needs to generate code at runtime, which does not comply with iOS’ restriction).
To be fair, somebody did manage to make Node work on iOS. JXcore, a fork of Node based on version 0.10, added features specifically targeting mobile platforms and circumvented the iOS JIT restriction by adding the ability to use engines other than V8. By using SpiderMonkey in its interpreter mode, JXcore was able to run on iOS. JXcore also added other features and tooling to make Node fit well with mobile environments. Unfortunately in March 2016, Nubisa - the company behind JXcore - announced that they would no longer be working on the project. JXcore has made massive changes since the fork and meanwhile, development of mainline Node.js has continued at a dramatic pace; the two codebases have diverged so much that reconciling them is no longer practical and with JXcore still stuck at v0.10, it is clear that the project has reached a dead end.
Fortunately, other relevant efforts have emerged. Microsoft published Node-ChakraCore, a friendly fork of Node which adds support for optionally using the ChakraCore engine instead of V8. Rather than modifying the Node source code to use a different engine, as JXcore did, they wrote a shim on top of ChakraCore which essentially emulates the V8 API. Thanks to this approach, the fork is much easier to keep in sync with the parent project; the two happily coexist under the Node.js Foundation and will likely be merged at some point.
Node-ChakraCore opens an opportunity for Node and iOS because ChakraCore has a well-optimized, 100% JIT-free interpreter mode. Even though it doesn’t officially support iOS at the moment, it was recently ported to macOS, which is not too far off.
The Janea team has been involved with these technologies for a while. We have been supporting JXcore for the Thali project and we have been working closely with Microsoft on both the Windows port of Node.js and Node-ChakraCore. So a few months ago we decided to capitalize on the lessons learned from all those projects and started porting Node.js with ChakraCore to iOS.
We chose to focus on iOS first because we knew that it would be significantly more challenging than Android. With a little bit of effort, you can pretty much already run Node with V8 on Android (even though a lot more work needs to be done to make it an easy-to-use framework for mobile). It’s Node.js on iOS that truly breaks new grounds.
We have now reached a point where things seem to be working pretty well. Even though it’s still too early to publish a release, we wanted to at least share the news and show a demo.
For the demo, we thought it would be interesting to showcase an integration of Node.js with React Native. React Native allows developers to write mobile apps primarily using JavaScript, while still having to write native iOS/Android code for things that are not covered by the framework. By adding Node.js to it, the hope is that developers will be able to do more with just JavaScript. While there are already solutions for using Node.js modules with React Native, having a real Node.js engine in the app should open up more opportunities and improve support for those modules.
The way we added Node.js to React Native is by starting Node.js on its own thread at application startup. Node and React Native each use their own JavaScript engine instance: ChakraCore and JavaScriptCore, respectively. In this particular implementation, the two JavaScript environments communicate via a combination of a REST API and WebSockets, which is a simple but effective approach that in principle allows to make calls, register callbacks, and pass JSON objects back and forth. While we chose to keep things simple in the demo, it would be easy to add some syntactic sugar on top of the REST API / WebSockets to make the interface between the frontend and backend even slicker.
The application allows the user to create photo albums and synchronize them with other users/devices. Synchronization happens between any number of devices and it works peer-to-peer, with no external server involved. Because of that, it only happens when the devices are on the same Wi-Fi network.
Node’s built-in dgram module is used for local network peer discovery, by means of UDP broadcast packets. The React Native portion of the app handles the UI. When a photo is added, it is stored in an in-memory database running in the Node layer, using PouchDB and MemDOWN. Back on the Node side, PouchDB handles the synchronization between devices. The REST API is provided by the express-pouchdb module, which starts a CouchDB compatible REST API on top of PouchDB, using the popular Express module. WebSockets is provided by the express-ws module, also on top of Express.
Here’s a video showing the app in action.
The demo source code is available on GitHub.
The possibilities for Node.js on iOS are certainly not limited to React Native. For starters, an integration similar to the one shown in the demo could be implemented for Cordova / PhoneGap (similarly to how JXcore-Cordova integrates Cordova with JXcore). Thali, which uses JXcore through JXcore-Cordova to provide rich peer-to-peer synchronization abilities, could certainly benefit from such a thing. Other mobile development frameworks, especially the ones based on JavaScript, could also be considered.
Of course, Node can also be added to any regular iOS application written in Objective-C or Swift. You would simply link the Node.js library with the application and add all the JavaScript code to the bundle. In your app startup code, you would then create a thread to execute the Node event loop and have it run the main script. You now have at your disposal the power of the Node.js built-in modules and the npm ecosystem.
So there are plenty of opportunities for future developments. At Janea, we intend to continue working towards making Node.js a powerful tool for iOS and Android development.
In the meantime, we would love to hear your feedback about all of this. How would you use Node.js on iOS and Android? What scenarios, features, or integrations would be most interesting to you? Please share your thoughts in the comments section.
For any further questions or suggestions, please don’t hesitate to get in touch. Also, don’t forget to subscribe to our social media channels and our newsletter, so that we can keep you posted about future developments.
Ready to discuss your software engineering needs with our team of experts?