<onWebFocus />

Knowledge is only real when shared.

Write Once, Run Anywhere?

November 8, 2021

When does a single codebase for multiple platforms makes sense?

Write once, run anywhere was a slogan to highlight the benefits of how the same Java code can be run on multiple platforms. At the time this included mainly Windows, Linux and macOS.

Since this approach didn't prove to be very popular React Native has chosen a similar but somewhat different slogan Learn once, write anywhere. The idea is that by knowing React and Browser technologies one is now able to create native apps without having to aquire significant additional knowledge.

React Native and Flutter have been very successful in allowing people to create native apps for iOS and Android without full knowledge of either of the native stacks. The React Native team recently introduced their Many Platform Vision which is a more or less obvious step in the direction of Learn once, write anywhere.

React Native running on various platforms

React Native running on various platforms, taken from Tweet by Matteo Mazzarolo.

Learn once, write anywhere

Apps for Android and iOS are usually quite similar. Screen sizes are predictable and in a range where there isn't a need for something like responsive design or breakpoints. Both platforms mostly share the same sensors and features: Multi-Touch, Gestures, Gyrometer and a Virtual Keyboard to name a few. This big overlap due to the platforms itself makes it a very compelling case to have a framework allowing to create two apps from a single codebase. React Native exposes a few very versatile components which will be mapped to the matching native components for their specific platform at runtime. With just a few of those components like View, Text, Button, Input layed out inside JavaScript using JSX plus styling based on Flexbox some quite impressive apps can be created.

import React from 'react'
import { AppRegistry, StyleSheet, View, Text, Button, Input } from 'react-native'

const styles = StyleSheet.create({
  screen: {
    flex: 1,
  },
  heading: {
    fontSize: 20,
    fontWeight: 'bold'
  }
})

const App = () => (
  <View style={styles.screen}>
    <Text style={styles.heading}>Login</Text>
    <Input placeholder="Username" />
    <Button title="Submit" onPress={() => alert('Logging in...')} />
  </View>
)

AppRegistry.registerComponent('simple-app', () => App)

JSX as an Advantage

React pioneered JSX the approach of writing HTML inside JavaScript code. On the Web this has become the most popular approach to render the HTML for websites. React can be used for all kinds of layouts not just HTML. An example is react-pdf which allows for the dynamic creation of PDF documents in node and also in the browser. React-three-fiber is another library leveraging React and JSX to make it easier to create and structure 3D scenes.

Flutter is using the same approach of sharing a single code base between Android and iOS. Unlike, React Native which is clearly aimed at Web Developers it's aimed at Native Developers looking to avoid code duplication by targeting both native platforms with a single codebase. Instead of JavaScript it's using Dart a new language created at Google; Flutter being the most popular use-case for it. Styling is more like native layouting for Android and not made to mirror CSS. The most stringent difference and possibly drawback is the absence of JSX.

Native Look and Feel

More and more the distinct look and feel of apps between iOS and Android has waned. Apps nowadays come in all styles independent of the platform. Still, most native apps for each platform usually share the design direction promoted by Google or Apple respectively.

React Native can use native UI components for each platform and is therefore able to provide the native look and feel albeit somewhat at a cost. In a few cases where a native look would require UI elements not readily available as React Native packages or the look is very easy so implement using native UI elements a native approach is recommended.

Run Anywhere?

Having a common framework requires that the target platforms have a lot in common. Safari on a Mac and Chrome on a PC have a lot in common, therefore it makes sense to use the shared react-dom package. iOS and Android also share a lot of elements therefore it makes sense to use one codebase and one framework namely react-native. On the Desktop Windows and Mac apps have a lot in common therefore it makes sense to use react-native-windows which is still in development and supports both Windows as well as macOS.

Having a common framework and codebase requires that the target platforms have a lot in common.

This assessment is somewhat different from the approach described in the initially mentioned Many Platform Vision the React Native team has put out. The codebase can only be shared if the target platforms have enough in common. In practice this is rarely an issue since these distinct platforms will usually also require different designs and feature sets. From a user adoption perspective it's not an issue if these different platforms are developed by different teams of developers or lauched at different times.

Pockets of Reusability

Due to platform differences it's often hard to share code. Apart from sharing knowledge like the use of JavaScript, React and JSX sometimes it's also possible to share code across vastly different platforms. This requires a common framework independent of the platform constraints. For example the logic talking to the backend using a GraphQL API could be shared while GraphQL would make sure that on different platforms only the data actually displayed is requested. Generation of PDFs or Canvas elements also will not differ depending on the platform and can therefore be shared. React-native-skia currently only supports iOS and Android but it's possible to imagine animated graphics being shared across platforms in the future.