How We’re Scaling React Native
Over the past few months, we’ve seen a flurry of blog posts, responses, and rebuttals regarding React Native. Companies like Airbnb and Udacity have said goodbye to the development framework while others like Headway are still moving forward.
After reading up on both sides of the argument, we here at You.i TV are more bullish than ever about React Native being a positive step forward for our customers.
Why? Like many others, we love the usability of the framework, we have witnessed the benefits of the iteration speed, and we are believers in the growing ecosystem and community building out around the technology.
At You.i TV, we solve a cross-platform problem that reaches beyond mobile. Our customers turn to our tools to give them a consistent way to deliver video apps across a dozen platforms spanning mobile, tablet, gaming consoles, streaming devices, Smart TVs, and more.
As evidenced from the posts referenced above, we know that the current React Native architecture cannot properly scale across this volume of platforms; while still delivering a high-quality user experience, and providing a manageable way for development teams to reach into the native layers for customization or integration of existing code when required.
With the RN implementation for iOS, Android and UWP weighing in at about 27,00, 62,000, and 48,000 lines of code, respectively, there are too many abstractions and code paths to keep in sync as both the underlying platforms and overarching RN framework evolve for this approach to scale.
The more platforms to support, the more the React Native APIs will fragment. The more React Native fragments, the more platform-specific code gets written to get the desired user experience. The more platform-specific code, the worse your overall reuse. The worse your reuse, the worse your developer experience in needing to jump between developing and debugging across multiple environments. You get the point.
We have 3 goals for the developer experience when using our React Native solution:
- Bring the awesomeness of the React Native developer experience to all of the platforms we support
- Introduce zero fragmentation into the React Native API on all of our platforms, including ones the framework has never been used on before
- Provide a single reliable mechanism for writing native code that executes across all of the platforms we support
To achieve our goals, a different approach to React Native is needed. Our approach to developing cross-platform using React Native is to replace the underlying implementations for each OS, with a single unified platform called You.i Engine One.
Our C++ application framework maximizes code reuse through the use of platform independent controls and libraries. By binding React Native to a cross-platform application framework, we have created an architecture that can realize our 3 key objectives stated above. Additionally, since we have full control of our platform-agnostic application stack, we can make targeted improvements to performance, application lifecycle, threading, etc..
Also, core to our DNA is the quality and fidelity of the user experience. If you are interested in how we are integrating native design elements with JSX through our tooling, check this out.
A Common Native Foundation
By unifying the React Native platform foundation to a single native framework, our tooling enables the reuse of code across the entire application stack on every platform. This means a wide range of challenges are reduced due to a single React Native codebase for the foundation as well as the app.
After reading the debates in the posts referenced earlier, we wanted to explore how some of the issues surfaced apply or are addressed by our approach to scaling React Native.
Developing and Debugging
React Native requires digging into the native foundation. This means the engineers working on the projects need to be proficient in React Native and iOS or Android. It’s ideal but rare to find someone proficient in all. With a common native foundation, this problem is greatly minimized. Tough memory leaks or performance problems can be debugged on any given platform, and chances are very high that it will fix the issue on all platforms as well.
A team of experts knowing the nuances for debugging deep into each native platform is not needed. All we need is a single, unified team developing on one unified codebase — leveraging JS, JSX and (optionally) our designer tool the same way for all platforms.
Bridging into Native
A Native Bridge is the communication layer between the native platform and React Native. They are often cumbersome to write. For You.i Engine One, the clunky nature of Native Bridges is greatly aided by the fact that the JS-level integration is only done once. When C++ bridging is used, it’s much less error-prone than the existing React Native bridging code. We have written a JS to C++ bridge using modern C++ 11 techniques to provide easy constructs to the developer for bridging. In most cases, issues are found during compile time rather than runtime.
When native platform APIs are required, our C++ engine is capable of reaching into the platform APIs, and is already pre-integrated to many of the common services; networking, keyboards, secure storage, and more. The most common use case for custom bridging through You.i Engine One to an underlying platform is for code that comes from third-party SDKs which may need a module created. However, this is simple to solve. If the vendor, say Mixpanel for analytics, has a way to have a JS module (SDK + HTTP Rest Interface) — that is the preferred route. Otherwise, if the vendor only has platform-specific SDKs, a C++ bridge is first created to that native SDK, then exposed via a JS bridge. We plan on making video-specific integrations available in the near future through npm packages.
One of the debated concerns around React Native is its performance. Initialization time, initial render time, and long lists were all concerns raised where React Native was underperforming the natively built experiences. The common native foundation provided by You.i Engine One uses a very small fraction of the OS, which means that app lifecycle, rendering, lists, etc. are all handled in a platform agnostic way. This opens up the opportunity to optimize the full application stack to tightly integrate with React Native. Without this flexibility, React Native is, and will be, limited by the implementations and APIs of the native OSes and will be forced to continually refactor the implementation code itself over various platform iterations.
JS Engine Support
With the broad range of hardware we support, we found that we needed a new level of flexibility in choosing which JS Engines to leverage. For example, we use an up-to-date version of JSC for several platforms (including Android) but have also added support for Duktape so that we can take advantage of its smaller footprint on low-end platforms. This provides us with the flexibility to use the JS engine with the most performance per heap ratio given particular platform constraints.
In our readings, we found that some hard to find issues arose from debugging in V8 vs running in JSC. These issues will still remain as we don’t unify the JS engines. However, we are under full control of what engines we use and what versions we bundle with our React Native implementation. Through our testing, we are working to ensure parity to help track and eliminate hard to trace inconsistencies.
The You.i Engine One platform binding layer will need to continue to evolve in-line with changes to the React Native Framework. To date, we have not seen any need to fork React Native and don’t plan on it in the future. It is our responsibility to keep our implementation up to date with the fast moving ecosystem so that our customers can bring the latest and greatest of React Native to the platforms we support.
For any updates to iOS, Android or any other environment, we provide a CLI to download and install the latest version of our engine. Underneath the hood, we make heavy usage of industry tools like CMake to unify the build process regardless of the target platform. Anyone using You.i Engine One would need to only have the platform SDKs installed on their device, which is the same requirement for pure native development. From a team perspective, this means that developers need only as much maintenance as they would for native development. Even then, sometimes developers simply have environments for 1-2 of the platforms and rely on builds from a CI server or test team to provide binaries for testing other platforms.
Unified Design Language System (DLS)
DLS is a concept near and dear to us here at You.i TV. We have always embraced an external language to define designs coming directly out of designer tools. We have brought this to React Native by augmenting JSX tags with view references, which allow the definition (look, location and animations) to come from our design language. We are very happy so far with the results and are seeing some very impressive designs being developed quickly.
While animations are compatible with our approach, we embrace and extend this with our UX workflow. Under the hood, we natively support the concept of multi-track timelines. This gives the ability to easily coordinate arbitrary timelines of animating properties, allowing for well-timed and orchestrated transitions. We bring these timelines to JSX with a JSX extension.
We also add tools in the developer’s toolbox for debugging dropped frames; developer-centric diagnostic tools in order to visually profile rendering, frame rates, memory usage and focus chains, directly on the devices.
While we’ve done a lot of work to overcome some of React Native’s challenges in reaching even more platforms in-house, we too are also seeing huge value for our customers by taking advantage of what is available in the community to improve the development process.
We are excited about the possibilities that Redux and other state stores bring to app development and our approach is absolutely compatible with them. We are big fans of Flexbox and have built compatibility into our engine so that it can be used standalone or in concert with our own layout system. We allow for the nesting of these systems, which enables developers and designers to use the best tool for the job. The main benefit of our layout system is that it is capable of using a fixed coordinate space with constraints, providing an extra level of UX design control.
Additionally, the fact that there is a vast array of JS projects that can be leveraged via open source tools has opened the door to an exciting ecosystem that was previously not available to our customers on top of our C++ application framework.
A Flawed Yet Powerful Framework
I totally understand why some major tech companies have chosen to move away from React Native. While it is a powerful framework for cross-platform development, the tools and developer experience as it exists today has its challenges.
Of the many organizational and technical challenges that arise from React Native, many can be solved by bringing a unifying native codebase for all platforms. From raising the quality bar, to writing code only once instead of twice (or 11x for some apps), to improving the overall developer experience.
We are excited to keep working with React Native to see how far we can push its limitations. So far, we’ve been pleasantly surprised with our progress and hope to share more with your in the coming weeks/months.
If you’d like to learn more about the technology featured in this piece, check out our You.i Engine One product overview.