The development of a mobile application for a digital product is a two-lane (or even three-lane) street. Product owners need to take care of users with both iOS and Android on their devices. At the same time, modern technologies provide the capability to build a single app for both platforms. React Native is one of the tools hoping to replace native app development in the future. How likely is it to succeed?
React Native
In 2015, Facebook announced the release of this superb solution, which allows you to use the same technology for building apps for different mobile platforms – on a shared codebase. React Native is based on principles that had been introduced four years earlier with the release of React.js. This JavaScript library paved the way to creating a dynamic and powerful user interface in web development. If you want to read about the differences between both Facebook brainchildren, you can do it in our React vs. React Native blog post.
With the framework launch, the number of naysayers began to decrease. At the time, developers thought that writing an iPhone app with JS would be terrific but highly improbable. Androiders’ were less skeptic because they’ve been waiting until Facebook make React Native available for the most popular mobile operating system in the world so far. Nevertheless, the demand for multi-platform projects spurred a chain reaction, which consolidated React Native’s position in the industry.
How React Native builds a mobile app
The way you build a mobile app with React Native resembles web app creation. However, web components are replaced with native ones. First, you need to write a single codebase using React. That’s the core of your app. After that, you need to enhance the core with native components to tailor a suitable cross-platform user interface.
To keep up traditions, let’s see how the classic app, “Hello, world!”, looks.
import React, { Component } from 'react' import { Text, AppRegistry } from 'react-native' class HelloWorld extends Component { render() { return ( <View style={{ flexDirection: 'row', height: 100, padding: 20, }} > <View style={{ backgroundColor: 'blue', flex: 0.3 }} /> <View style={{ backgroundColor: 'red', flex: 0.5 }} /> <Text>Hello, world!</Text> </View> ) } }
Components like Text and View take after their web app building counterpart. At the same time, there are plenty of platform-specific options to implement some native UI behavior. React Native engages relevant APIs to render either iOS or Android components. There are several ways to differentiate between them depending on the platform. The first one is by using the Platform module, which can detect the OS:
import {Platform, StyleSheet} from 'react-native'; const styles = StyleSheet.create({ height: Platform.OS === 'ios' ? 200 : 100, });
However, this option is not applicable when big parts of a component are platform-specific. Besides, the module does not implement the platform-specific code, as an app developer has to fill each branch in. So, another way is to use a wrapping platform-independent component. react-native-datepicker component has the same properties (public interface) on both platforms as it handles internally all the potential differences between platform-specific components. DatePickerAndroid is specific for Android, so whenever you have a date picker in your app, you have to handle this one on Android and another one, DatePickerIOS on iOS. If you look on mode in both – they are different. So. in some cases, it might not be enough to do just
const DatePicker = Platform.OS === 'ios' ? DatePickerIOS : DatePickerAndroid
since they have different interfaces.
Another option is to split the code out into separate files marked with a corresponding extension: .ios. or .android., for example DatePicker.android.js. Such filename would make it easier to understand.
In this regard, threads play a significant role in the building process, because they execute separate tasks in parallel:
- Main or UI thread – renders platform-specific UI
- JS thread – runs the basic JS logic code
- Native modules thread – accesses APIs of different platforms
- Render thread – generate OpenGL commands to draw UI
React Native UI components are independent and do not require any wrapper like in hybrid apps.
React Native pros and cons
The framework is no longer a new technology but a promising solution to build cross-platform apps. However, it can be both attractive and inappropriate depending on what your goals are. Below, you will find the benefits and flaws that characterize React Native.
Pros
- Single codebase
This is the main privilege of all cross-platform frameworks including React Native. Instead of building two separate apps, developers can reuse most of the JS code for both Android and iOS.
- Open source
This attribute provides almost limitless capabilities for the framework improvement with additional features.
- Web to Mobile
Web technologies underlie the basic principles of React Native which facilitates an engineer’s transition to mobile app development. Thanks to JavaScript, creating mobile applications is similar to web development.
- Components
You can reuse plenty of accessible components in other projects which is a substantial bonus to your endeavors.
- Hot reloading
The development time is cut, and productivity goes up due to hot reloading. With this feature, the app is kept running, and new versions of files that you edit at runtime are updated. Now, tweaking the UI is not accompanied by any loss of your state. An engineer immediately sees changes in the app.
- Declarative programming
React Native leverages declarative programming for creating a mobile UI. That approach facilitates the coding process and makes the code readable and flexible, which is extremely important when new engineers join the project.
- Smaller time expenses
With React Native, both the development time and time to market are significantly lessened. Instead of coding separately for Android and iOS, you save valuable time due to the code’s reusability. Although you can’t share all of the code, your time efficiency will be extremely high. We’ll dive into that later on.
- Development costs reduction
This benefit follows from the previous one in the sense that the less time you spend on development, the smaller the budget you can make do with.
Cons
- Native API support
Although React Native supports the most used APIs, some specific ones or functionality are not available. However, native modules can be a solution to this issue.
- Native Modules
The native-specified modules like camera access, push notifications, memory storage, device sensors, and others are the key to the above-described problem. At the same time, to take advantage of these parts of the code implemented in the native language, engineers have to possess expertise in this language. If you need to make something that is not available at React Native, you’ll have to learn Java/Kotlin or Objective-C/Swift to build the module.
- Design
Each platform has a specific design language and navigation patterns – iOS has been stricter for a very long time, and Android is catching up with material design. React Native provides the automatic transformation of the graphical elements according to the required platform. However, there might be some placement issues. As a result, you have to dive deep into the code and figure out how to adjust to the design guidelines.
- Native app interaction
Unlike native frameworks that ensure access to other native applications, React Native requires the use of third-party libraries or native modules to implement any integration no matter what technology you’re using.
How React Native differs from native app development
We can’t claim that React Native is better or worse than native development. It’s absolutely different. And the way it differs can be crucial for your decision. Let’s take a look at some essential points in React vs. Native face-off.
General architecture
The answer to the question “How does React Native work” reveals the main distinction from native mobile development. The framework uses platform-specific UI controls for native rendering. However, it’s orchestrated by a single-threaded JS code. Also, JS bridge executes the JS runtime and connects the app with its native parts. No hybrid’s WebView implementation makes the app’s functionality and view akin to a native product.
The most common architectural pattern to build a native app is MVC. It put the app’s structure into three layers:
- M – Model: a layer that manipulates data
- V – View: a presentation layer
- C – Controller: a binding layer between M and V
Schematically, MVC works as follows:
The MVC derivatives, MVP and MVVM, have similar architecture but replace the C-layer with P (Presenter) and VM (ViewModel) respectively.
These patterns are mostly used for making Android apps. iOS developers usually opt for another option that does not come from the MV family. It is called VIPER and consists of five layers: View, Interactor, Presenter, Entity, and Router.
At the same time, it is a usual practice to encounter a mix of architectures in one native app. For example, MVVM works best when used with a binding framework ike ReactiveCocoa – a pure representative of the functional reactive programming paradigm.
Platform-specific components
For a native app, the user interface and experience is to be coined according to the requirements of one platform, be it iOS or Android. The essence of cross-platform development is to focus on design conventions of both platforms in one go. It is important not to affect the UX of different categories of users. React Native allows you to handle the separation of platform-specific components via:
- Platform module
It detects the platform to implement platform-specific code. Platform.OS will differ as follows:
iOS
import {Platform, StyleSheet} from 'react-native'; const styles = StyleSheet.create({ height: Platform.OS === 'ios' ? 200 : 100, });
Android
import {Platform, StyleSheet} from 'react-native'; const styles = StyleSheet.create({ height: Platform.OS === 'android' ? 200 : 100, });
With Platform.select method, you can return the value for the running platform.
import { Platform, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ controlContainer: Platform.select({ android: { backgroundColor: 'green', }, ios: { backgroundColor: 'yellow’', }, }), });
- Platform-specific file extensions
This way is suitable for complex and big parts of a platform-specific component. Therefore, the code is split into separate files, which will be loaded in accordance with the detected platform. For example, you can have two separate files for the SelectListControl component:
- SelectListControl.android.js
- SelectListControl.ios.js
When requiring the component via import SelectListControl from ‘./SelectListControl;, the proper file will be loaded automatically.
Navigation in mobile app development is a tricky thing. It covers not only the ability to go back, skip, undo and other functional actions but also the consolidation of ceremonies required to render a new screen. Each platform has its peculiarities and as well as approaches to navigation UX. Examples of differences in navigation paradigms include a physical home button on iOS vs. navigation of three buttons on Android; or a swipe-back gesture to navigate back on iOS vs. a navigation drawer sliding from the left side on Android. You can opt for numerous solutions to implement navigation in React Native, but the framework itself provides two default modules:
- NavigatorIOS
It is the pioneer navigation stack meant for iOS since it was the first platform React Native started on. NavigatorIOS is a simpler solution, which rests on the native iOS navigation stack.
- Navigator
Navigator is an attempt to make a match-all navigation codebase for both platforms. It is a React component to deal with mapping a route and rendering function. Navigator has a preset piece of UI, ToolbarAndroid (strongly tied to the view) and Navigator.NavigationBar (strongly tied to routes), that functions as the top navigation bar.
If the default solutions are not what you need, there are plenty of JS-based and native implementation modules as well:
- JavaScript-based modules:
- React Navigation
- React Native Router Flux
- React Native simple Router
- Native modules:
- Re-route-Native
- Native-navigation
- React-native-navigation
- React Router Navigation
Bundle size
Native apps are devoid of any additional runtime and other adjoined elements. Therefore their bundle size is much smaller. React Native comes with a compiled WebKit engine (JS runtime + native components), which boosts app size by a few MB.
Ecosystem
Syntax
React Native uses JSX, an XML-like syntax extension to ECMAScript. You may have noticed it in our “Hello, world!” code sample:
Hello, world!
Some frameworks have a special templating language to embed code inside markup language, and here is the reverse case: JSX allows you to embed XML within JS. However, it is not obligatory to use this syntax sugar, and you can do with plain JavaScript. Let’s take a look at some basics.
Variables
You can define variables using let, const and var. The latter is the old way of defining variables, which now is considered unsafe to use. Let is used to define a mutable variable.
let myVariable = 123
You cannot change a variable declared with const.
const myVariable = 123
Arrays
To declare array in JS, you can use new Array or simple square brackets (array literal).
new Array(1, 2, 3) [ 1, 2, 3 ]
Objects
Objects are collections of key/value pairs. “Object” type can be used in different ways compared to those in statically-typed languages. To create a new object, you can use:
const obj = new Object();
or the shorthand way
const obj = {};
You can also initialize it with key/value pairs as follows:
const obj = { “key1”: 1, 2: “value" };
Functions
JS treats functions as first-class citizens. To create a function declaration, you need to use a function keyword or a shorter arrow syntax
const foo = () => 44
The body of the function is contained in curly braces. Names created in the function definition are parameters. They define a function. The value the function receives from each parameter is an argument.
function log(arg1, arg2) { };
Every function in JS returns implicitly unless stated otherwise explicitly with a return statement.
And here is a small bonus – a short syntax comparison between JavaScript and two languages used in native app development.
JavaScript (ES6) | Kotlin | Swift | |
---|---|---|---|
Hello World | print("Hello, world!") | ||
Variables | let var (old way of defining variables) | var | var |
Constants | const | val | let |
Functions | function | fun | func |
Arrays | New Array(1, 2, 3) [1, 2, 3] | arrayOf(1, 2, 3) [1, 2, 3] | [1, 2, 3] |
Null | null | null | nil |
Handling of optionals The essence of the Optional type is that there is a value, and it equals x, or there is no value at all. Let’s find out how JS, Kotlin, and Swift handle them. | |||
JavaScript Optionals are not supported in ES6. However, there is a Babel plugin that supports optional chaining const name = Math.random() > 0.5 ? "Lucky guy" : null TypeScript TypeScript is a common flavor these days and it helps with null safety: const name: string | null = Math.random() > 0.5 ? "Lucky guy" : null; | |||
Kotlinval name : String? = if (Math.random() > 0.5) "Lucky guy" else null | |||
Swiftimport Foundation |
Knowledge
To build a native app, you could confine yourself to expertise in specific language and integrated development environment according to the OS for the application. With React Native, your domain knowledge should include JS or its syntax sugar JSX, as well as supporting tools for testing, debugging, text editing, etc. A background of React and web development technologies will be a huge advantage for learners of React Native. Check out our picks of books to master the framework.
Development time and cost
If you’re making one mobile app for iOS or Android, the native approach is more productive despite all React Native’s advantages. However, we are talking about cross-platform app development, which requires additional resources in terms of money and time. In that case, the framework is a super booster thank to code reusability. At F8 2018, we’ve learned the following code reuse rates in some famous applications – 93% for Facebook, 85% for Skype, and approximately 86% for TaskRabbit. In practice, this means that development time, as well as costs, will be much lower. Meanwhile, debugging React Native might be a problem since errors go through many layers and different stacks – JS, bridge, native components. And that can tear off much of your development time.
Documentation
Comparing Swift vs. React Native or another technology in terms of documentation is a tough task. Every software producer be it Apple, Google or Facebook apply huge efforts to deliver exhaustive documentation. Along with regular docs, each tech stack is provided with helpful prompts and code samples. Moreover, they are not novice but mature technologies. So, we can state a draw here.
Sophistication
React Native loses to native SDKs from the tech perspective. In other words, you opt for a cross-platform solution if some existing product needs to be improved or upgraded. Complex stuff including augmented/virtual reality, Big Data Mining algorithms and other sophisticated things require the power and technical abilities of native technologies.
React Native performance compared to native app development
React Native has an ace in the hole – it can use React.js for mobile apps performance increase. As a result, all the best of React like the increased app performance, simplified programming methods, and so on are at developers’ fingertips. Moreover, there is no need to compile the app every time you tweak anything. You can just refresh it using Live Reload or enjoy Hot Reloading that automatically updates the changes. The native approach cannot provide that and lags behind in terms of productivity, time to deployment, and flexibility of app development.
It would be awesome to take a simple app having authentication, list view, and other basic features and triplicate it using three different tech stacks: React Native, Swift/Objective C, and Kotlin/Java. Actually, John Calderaio, a self-driven engineer, has put this idea into action. He used Swift and React Native to build the same app with four tabs: Profile, To-Do list, Pageview controller, and Maps (here is more about how to embed a geolocation search feature into your React Native app). Measurements of usage of memory, CPU, and GPU of both apps are introduced below.
Memory usage in MiB
CPU usage in %
GPU usage in frames per second
This particular test allowed us to realize that React Native can be on a par with native technology in performance.
Why should I choose React Native to build an app
Now, we come to the question “Is React Native the right tool for my next project?” Of course, a lot of details and criteria should be weighed to get a proper answer. However, you will hardly find a better solution for a cross-platform application. And here are some reasons why:
Native applications entail the burden of defining separate app logic depending on the OS. With React Native, you can forget about swinging from Android to iOS and vice versa. You write code for one operating system and share it on another one with some subtle adjustment. In other words, you accelerate your productivity almost twofold.
No WebView
Hybrid apps rely mostly on WebView, which allows you to define a custom JavaScript-to-native bridge. React Native does the same but uses a bridge between JS and native modules. That means faster loading and reduced memory usage. Also, UI rendering uses native controls but HTML in the browser. Read more about this in our comparison of Ionic vs. React Native feat. Flutter.
Better UX requires great UI
By leveraging the best of React.js, mobile framework puts an emphasis on refining the user interface to make it seamlessly integrated into the entire app environment. Hybrid apps fall behind React Native in responsiveness which means approaching a better user experience.
Save money
Another attractive feature of React Native is the adequacy of human resources on the market. All your project will need is good JS developers with some React expertise – and you can start. Even a small team of engineers can handle a tough project with scarce resources.
Railsware’s cross-platform development experience
Railsware is known for its affection for Ruby and its framework Rails. Still, React Native holds an important place in our technology fleet. For example, at the Ruby Meditation #17 conference, one of our engineers introduced his ideas and vision of this cross-platform technology from a Rubyist’s perspective. We go with the tides which means leveraging the best technologies for production. However, do not think that it is a matter of the comparison of React Native vs. Swift/Java. We count on efficiency and productivity. And that is definitely the focus area of React Native. It prevents the differentiation of iOS or Android developers in the company; it is easy in employment and cost-efficient in support.
Wrapping up
In short, React Native is an astonishing tool for mobile app building. It can be a perfect choice for enthusiasts who are aimed at multi-platform app without much complexity. If you have a small budget or a web development team, those are also the appropriate conditions to leverage the framework for your project. You won’t have to learn to love React Native since it immediately impresses you with its ability to accelerate app development by reusing code for different operating systems without sacrificing user experience.