About a year ago we faced an interesting question at SoundCloud: can we build SoundCloud Pulse — our app for creators — with React Native? Is a five-month-old technology mature enough to become part of SoundCloud’s tech stack?
A couple of months prior to this question, we started to design our second set of native apps for Android and iOS. We focused on iOS first because that is where the majority of the creators were. Finding iOS engineers proved harder than we thought, which is a situation likely familiar to you. For Android, we partnered with Novoda. We did not want to have many months between the iOS and Android releases. Meanwhile and independently, the design team was running user-testing sessions with React Native-based prototypes and we also had capacity from very eager web engineers.
We had two main goals at the start. Including all teams whose input was valuable for this project and convincing ourselves of React Native. Input from our iOS team with many years of mobile app development experience was very valuable and reassuring. At the same time we were able to give them context as to why we were investigating React Native. Openly talking about the project in the company and including people in the decision making process helped us a lot in learning what React Native could and couldn’t do:
- Which elements can and should be written natively?
- Can it compete with native apps in a way that users won’t notice it is “different”?
- Does React Native support the native share dialogue?
- Does it support deep linking?
- Does it support securely storing access tokens?
- Does it support Facebook & Google sign-in SDKs?
We also had questions about how well it would integrate with our existing native libraries:
- Can we run acceptance tests with our existing iOS tooling?
- How can we implement internationalization?
- Can we integrate it into our continuous-integration pipeline?
- What does the performance look like for long lists? In general?
- Does it have a negative effect on battery life?
During a two-week spike, we built a prototype of the SoundCloud Pulse app to have a better understanding of React Native in practice. After the first week, we had already fleshed out most screens of the app and were surprised how quickly we were progressing. During the second week, we wanted to focus on the unanswered questions and write some native code. It was easy to bridge our existing iOS libraries with our React Native prototype. We completed the prototype with support from our iOS engineers and concluded that we had enough confidence in React Native to go ahead with the project full time.
In contrast to the spike sprint we focused more on achieving high test coverage. Coming from a mostly web background we were used to be able to deploy hot fixes within a couple of minutes in case of a runtime error. On mobile this is not possible and so we had to test more thoroughly before shipping a new version of our app. We thought about using services like CodePush that allow you to push an updated version of your app without needing to wait for the app store review process. However, all those services were not stable enough in the beginning and we decided not to use them to reduce the amount of sources of errors.
Development of the app started mid-October 2015 and we shipped the first version mid-February. It took us four months to write the app with three developers. Four months from a spike code base to a unit-tested, component-tested, and integration-tested application. We estimated that it would take us six months to finish the app but we underestimated how fast React Native allowed us to be.
Things we’ve learned along the way
For many popular native libraries / APIs / SDKs someone probably has written a bridge to React Native already. Of course the Facebook SDK is one of them, so is Google Signin but also things like Appboy, access to the iOS Keychain and many more.
A perfect example of this was our event-logging library. It was battle tested in our listener app, it had support for batch sending events, so it would be lighter on the network traffic, and it could handle offline / bad connectivity. We were able to extract it from the listener application without too much effort. The rest was as simple as bringing a few methods like
Another great help was knowledge around how to distribute an application to the app store and the process that revolves around that. Since we were mostly web developers and did not have any experience with that process, setting it up on the CI saved us a considerable amount of time running builds manually and uploading them to the app store by hand.
We could reuse a lot of our internal web modules with little to no modification. One good example is the way we handle internationalization. We are using the same library that we are using on all of our web-based applications, so we only needed to adapt our string parsing slightly.
Another example is a library for defining API endpoints in a centralized way. We were able to use it straight up, no changes necessary, by passing React Native’s
fetch function as the transport layer. These were just two examples but there were many more small utilities / helpers that we were able to just drop in.
React Native Gotchas
Of course no technology is perfect and so React Native comes with its own set of small gotchas. None of them are deal breakers for us, but you should still know about them before starting a React Native project.
The biggest one by far is the fact that React Native is evolving very quickly, which of course is great, but it does incur a cost. There is a new version every two weeks (along with a release candidate of the next version) and the “Breaking Changes” section is often extensive. If you update on a regular basis you’ll be mostly fine; the bigger potential issues come from the third party libraries you use. As an example, now you import
react instead of from
react-native which is a problem if the third party libraries have not been updated to do the same. On top of that, Facebook definitely values pushing forward more than safely updating which was most apparent in an update to Babel 6 that broke many libraries and took weeks for everyone to catch up. All of this basically means you need to put a little more time aside for keeping up-to-date than with more mature frameworks.
The other little pain point is documentation. It is not as extensive as we wish and there are still some completely undocumented features / components, but this is getting better and you can see a big investment happening in this area.
Why React Native worked well for us
It is quite obvious that a better developer experience was a top priority for Facebook when creating React Native and it really shows. Automatically live reloading your app when your code changes massively shortens the feedback loop and using Chrome to debug your application feels very much at home to any web developer. If you also happen to use Redux and a logger middleware, knowing what is going on in your app at any point is very straightforward. It is a very comforting feeling to know the tools are there for you when you need them and its effect on developer happiness, development speed and confidence can not be understated. If you need one reason to give it a try, this is it. Happy developers and a faster turnaround are hard to beat. Product managers, decision makers & designers listen up, I’m talking to you here :)
But there’s more: the open source community provides many ready-to-use libraries and components and is very responsive to React Native’s changes, questions and improvement suggestions. It feels good to be part of this positive community.
Another huge benefit that we only recently started to explore is the cross platform nature of React Native. Facebook mentions about 85 percent code reuse between their iOS and Android implementation of Ads Manager, and with the right app structure that number is very realistic to expect. This means that once you have the structure there, building a new feature for your app on both platforms will only have an additional cost of 10 - 20 percent instead of 100 percent. That’s huge.
The ability to update your application on the fly, as mentioned earlier, also allows immediate bug fixes, a/b tests and the warm, cozy feeling that you can push an update whenever you want: a dream come true in the mobile development space (as long as Google and Apple allow it, which is always a little hard to predict).
Based on the great experience we had with React Native, we plan on using it in more places at SoundCloud. Right now there is a team of designers working on a replacement for our prototyping / user-testing application written in React Native. Our designers found it much simpler to work on a React-based application than on a native application and are capable of building the application by themselves without frequent developer input. They are also working on bringing this application to Android to increase the pool of users that we can test with and to test Android-specific features.
Speaking of Android, we have spent some time to prepare the app for running on Android and got a stripped-down version of the app working in just a couple of hacker time sessions. We are currently investigating further steps in that direction, such as replacing the current Android Pulse app with the React Native version. Another area we are looking into is integrating React Native views inside our native applications. We’re looking forward to these experiments and hope they go as well as this one.
Get in touch with us