Challenges in developing platform specific apps

El Niño
8 min readApr 24, 2017

Developing a new application can be quite time consuming if you want it to be available on multiple platforms (iOS, Android, web) as it essentially requires you to build three applications.

Besides having to maintain three separate projects, you will also need to have knowledge of several languages: Java for Android, Swift for iOS and one (or multiple) of many options for the web, as well as knowledge of many tools and frameworks. Often you’ll have to rewrite the same logic in a different language.

To make it easier to develop for all of these platforms, several frameworks have been created (e.g. Cordova), which allow you to write a single app and publish it to all (or some) of these platforms (“write once, publish anywhere”). The drawback of these tools is that they often just build a website which is displayed in a web view on mobile devices. This causes the need for the device to always run a web browser when running your app, resulting in a lot of redundant overhead which negatively impacts the performance.

Click here for the full code example

Our framework of choice: React

React is a JavaScript framework which took a different approach: “learn once, write anywhere”.

Instead of building a single app which runs in a webview, with React you build different apps for web and native platforms but you write them all in the same language using the same framework.

React reduces the need for knowledge of the different languages, tools and frameworks but doesn’t reduce the development time and code duplication as much as we’d like it to. However, we’d like to structure our React projects in such a way that they share their application state structure and logic, but have platform specific views. By doing this, we get the best of both worlds: build native and web apps at once, but limit code duplication and performance overhead.

For our application we will need to use React and React Native. As the name implies, React Native is the toolkit for building native (iOS and Android) apps. The main difference between the two is the components you can use to build your UI. In React you can use regular HTML elements to define a UI, but in React Native you have access to elements that map to native elements for each specific platform. So instead of using a <span> tag with some text, you will have to use a <text> element.

Click here for more on React

Extending React with Redux

The way we shared our code between React and React Native projects is focused around Redux. Redux is a so-called predictable state container for JavaScript.

So what’s state then?

The state of an application is basically all information at a given point in time to which the application has access. You can compare it to a lamp for example. A (simple) lamp has two possible states: on and off. For applications this state can be a bit more complex, often containing wether the user is signed in and content retrieved from an API.

Back to Redux

It acts like a backend’s database, but on the client-side in a non-persistent way. It stores information in a JavaScript object and you can add, change, remove or retrieve information.

Redux works especially well with React as it lets you describe a UI as a function of state. Meaning you can easily draw your UI depending on a certain part of the state and rerender it when the state changes. This allows for the UI to change when an action is fired without having to implement specific logic to make this change happen. An example would be a profile picture which is only shown when the user is signed in and otherwise shows a sign in button.

Using React’s conditional rendering syntax (see code snippet below) we define when the profile picture should be shown and when the sign in button should be shown. As soon as the state changes, the UI will be rerendered and changed based on the new Redux state. Simple right?

Now look at the example below:

What did this do?

The example shows a custom React component (in this case for web). When the component is loaded into the application, it is connected to the Redux state via the connect call and the relevant information is extracted from the state and passed to the components properties using the mapStateToProps function. This gives you the control to extract just the information that the component needs. In the render function we can check these properties and render elements based on the property’s value. Connecting your component to Redux also gives you access to the dispatch function which is used to emit actions.

Emitting actions in Redux

Redux doesn’t allow you to directly change the state from anywhere. Rather, it requires you to emit actions when some part of the state needs to change. These actions, which are nothing more than a simple javascript object, tell Redux what to do, containing a uniquely identifiable flag and any other information Redux needs to change the state accordingly. This allows us to separate the views from the logic. Actions are emitted by passing the object into the dispatch function. The snippet below shows a basic example of an action telling Redux the user is trying to sign in.

Actions can also be a bit more complex. To continue on the sign in example, we could have 3 basic actions:

1. Request

Tell Redux we are trying to sign in and set an isFetching flag allowing us to show some kind of indicator to the user.

2. Succes

We got a successful response from the API containing some kind of auth token.

3. Error

An error occurred.

Using these three actions, we can build a single login function which fires the requestLogin, then makes the API call and, depending on the response, fires either the success or error action. This gives us a single login entry point which can be used on both platforms. For a complete example of the login actions, check out our Github.

Reducers

So far, we have discussed what Redux is and how to tell it when and what information to change. However, we haven’t seen how to actually change the state yet. This is where reducers come in. Reducers are functions which receive a state and an action object and, based on the type of action, create a new state and return it. It’s the only thing they do. Reducers should never have side-effects, surprises, mutations or api calls. They are the only place in the application where the state should be changed. In the code snippet below, a simple example of a reducer for the loginRequest action is shown.

When an action is emitted, Redux calls the corresponding reducer and passes the action object to it along with the current state. The reducer then checks the action type to see what has to be done. In our case, the loginRequest action tells the system that we are fetching a token from an API endpoint, so the reducer copies the state and sets isFetching to true. This in turn triggers our view to be rerendered allowing us to show a loading indicator. Your application can contain multiple reducers, each controlling a specific part of the state. This results in modular, clear and easy to maintain code. To see a complete example of our login reducer, check out our Github.

Persisting the state

Having a shared state structure and logic which both React and React Native can use is nice and all, however sometimes you need to persist information for the next session (e.g. an authentication token to remember the user). This is where the differences between both platforms can cause some problems.

Synchronous vs asynchronous

On the web, storage is often done in local storage, which can be accessed synchronously. However, in React Native, asynchronous storage is used. This makes it a bit more difficult to write a single action for both platforms since you will have to change the way you handle storing and retrieving the data depending on your platform.

On the web, we could easily store the token of the user on a successful response from the API by setting a property on the localStorage object and then emitting the receiveLogin action. However, on native this cannot be done as the storage system is asynchronous. Here we’d have to tell the system to store the token, wait for it to return and then fire the receiveLogin action. Or we could skip the waiting and just assume all goes well (which is often not the best strategy). Retrieving data presents similar issues. When the application is loaded, we have to check if a token is stored and, if there is, retrieve it and set it in the initial state. Since the asynchronous storage doesn’t return our token immediately, we have to wait for it. This causes our application to be in a signed out state at first, until the token is returned by the storage and set to the state by emitting a separate action. In many applications this would result in the user being redirected to the sign in page at first before being redirected again to the content, which is not what we want.

Writing this logic is not impossible, but difficult to get right and we’d rather avoid it if we can, don’t we? This is where redux-persist comes in. Redux-persist is an npm package which allows you to store (parts of) your redux state into a number of different storage engines (among which local storage and async storage). By doing this, we can retrieve the stored state as soon a new session is started and from thereon it is as simple as just working with Redux.

In conclusion

Using the tools described above, we can build applications for both web and native platforms without having to write 3 (or more) separate projects. Although it still requires some platform specific code, namely the views, we can share the majority of our business logic between them. Compared to “write once, publish anywhere” platforms (like Cordova), React is a great choice as it allows you to build apps without webviews and the overhead caused by them.

By Rick van Gemert. Rick is webdeveloper at El Niño

--

--

El Niño

http://www.elnino.tech. Digital Development Agency building tailor made solutions, ensuring success by making it measurable.