Building an App with React Native

I’ve done a small amount of app development before — although nothing native: mostly Titanium and PhoneGap. I wanted to give it another shot, and had a project in mind: a music player designed for musicians that lets you adjust the pitch and tempo of the song. The actual APIs available to do that are built into iOS 8, so it’s an easy project to get started with.

My first attempt was to go fully native with Swift. A large amount of it was very straight forward. It was very easy to get music playing and adjusting the pitch and tempo. Laying out the UI was also quite straight forward: I actually liked Auto Layout. And drawing custom elements was also simple, it very almost identical to the web canvas API.

However, syncing data between views was not straightforward. I was not using a framework at the time, which would have almost certainly helped, and I was almost certainly not following best practises. However, I would constantly be getting bugs from data being consistent between views. This is something that React and React Native were built to solve, so at least I wasn’t alone in this problem.

Just as an experiment, I wanted to see how hard it would be to get some of the stuff that would have to be native to work in React Native: mainly the audio playing. All I had to do was call some React macros with my functions on the native side, and the functions immediately exposed, with type checking too! I was still able to use Swift, although there is a small amount of Objective-C boilerplate code you still have to write. Long story short, I continued the project in React Native.

What Worked Well

Like I said, implementing native functionality was so simple. Without that factor, this project would almost certainly not have worked. In total, I had to write native code for playing music, getting music metadata, letting a user pick songs from their library, setting the lock screen music controls, and a React view to display album art. Sounds like a lot, but this was about 500 lines of Swift code, and around 75 lines of Objective-C macros to expose my functions.

I was able to use my favourite JavaScript libraries: React and Redux, which immediately eliminated the problem of data being out of sync (see You Aren’t Using Redux Middleware Enough for how I kept the music player state in sync). Styling components was almost identical to the web, so there was next to no learning needed. Animations were also very straight forward. And lastly, the performance was great.

There was at least one feature I needed that wasn’t provided by React Native: tabular numbers. While the React Native codebase is all Objective-C, which I am not familiar with, it was very straight forward to extend styling to include this. And what’s more, Facebook are very good at merging pull requests.

What Worked Less Well

Having the styling api be almost 1–1 with the CSS has its disadvantages. Setting an aspect ratio on a view is something that happens on apps a lot, is very easy using Auto Layout, but there is no CSS property to do this — and no proposals either!

Further to this, all properties that have numeric values have to be absolute pixel values: no percentages, no ems. In addition, there’s no built-in way to do media queries. While using the flex model does help, it cannot handle every case, and the cases that fall outside are hard to build in a responsive way.

Both these things are possible to work around: a view has an onLayout callback that will tell you the size of the view before it’s rendered. Essentially, you have to do the layout manually.

Conclusion

React Native was significantly easier than native. I had less bugs, and the code was easier to extend. While I only built my app for iOS, I know that to support Android, it’ll be a matter of a few small updates a few components and writing a very small amount of native code.

I had no problems getting my app accepted either: it was accepted after the first submission. It can be viewed here!