My experiences of React Native vs Swift for iOS development
Update November 2019: I was recently invited to give a talk based on this article at Bath Digital Festival. You can see the slides from the talk here.
Part of my role at Green13 Solutions involves research and development. As part of this I evaluated using React Native to develop mobile apps. I also started experimenting with React Native outside of work in some of my side projects, too.
Below are some of my thoughts and reflections on using React Native and how it compares to developing native iOS apps and using React for the web. The points below focus on the developer experience of using both approaches.
All opinions are my own.
Summary: Overall I preferred using React Native compared to the experience of developing native iOS apps and would be inclined to chose it again for another project. Also, I had a very easy learning curve moving from React to React Native. React Native provides more components ‘out of the box’ for you to use, which I found really helpful.
Quick background
React is a powerful JavaScript library for creating rich user interfaces. It was originally developed for the web for websites like Facebook and Instagram and has grown in popularity massively.
React Native is a way of using the same React technology to create mobile apps that automatically work on different platforms.
Language
Summary: Despite the fact that I have lots of experience working with React and I am a big fan of using React to develop user interfaces, I actually preferred working with Swift. React Native was very similar to React with only a few things to learn which meant it was easy to transfer my skills.
React Native vs React
React Native is like using React for native apps. I know that might sound obvious, but what I mean here is that you write virtually the same React code that you would use for web projects in almost the same way. The infrastructure provided by React Native works ‘under the surface’ to make your code run on mobile devices.
This is because both React and React Native share the same approach to parsing your code and generating an intermediate representation that can be translated into different elements of the platform they are targeting (e.g., native UI elements on a mobile or the DOM).
I use the words ‘virtually’ and ‘almost’ above as there are a few differences. For instance, there are a few simple differences in syntax for creating UI elements using JSX. For example, React Native uses <Text/> instead of <div/> as a generic container element.
Another difference is that with React Native there are a few useful components that come ‘out of the box’ that don’t come with React that you can use in your application. For example, React Native provides a modal component that can be imported and controlled very easily.
React (for the web) introduced portals as a much easier way of managing components that ‘break out’ of a web page, and while they are really declarative and easier than implementing this yourself I found the React Native modal easier to work with.
JavaScript vs Swift
The application logic of a React Native app is just JavaScript and the code I have used to write logic in native iOS apps is written in Swift.
Swift is a relatively modern programming language designed by Apple. JavaScript has been around for over 20 years and has had a lot more time for community-driven development (both are open source projects).
I have lots of experience using JavaScript and JavaScript frameworks, so I expected to prefer using JavaScript. However, I came to really like developing with Swift and in some respects it feels like Swift is the language JavaScript could have been.
It’s not a fair competition in some respects though, because Swift was conceived and created at a much later point in time with the benefit of learning from languages like JavaScript and with the idea of combining the best features of static and dynamic languages.
Why I like Swift
- It has the safety of compile-time type-checking but still has a clean and uncluttered syntax because it uses type inference. This means that you don’t have to clutter your code with declaring the type of each variable, but the compiler will detect the type of each variable and will throw a compile-time error if you treat the variable as an incorrect type elsewhere.
- Because Swift is compiled to create iOS apps it was easier to debug applications because I could step through my code using Xcode. This was especially useful when I had to debug asynchronous code and could pause the execution with breakpoints.
What I didn’t (initially) like about Swift
Initially, Swift felt very easy to learn and to work with, but after getting through the fundamentals there were a few moments where I had to really pause and think to understand things. One example is optionals which are all to do with accessing data that may or not be there. You can then ‘unwrap’ the optional with exclamation marks. For example, when accessing a property of a response from an external resource there is no guarantee that the property will exist which can cause errors if you don’t take care.
The syntax for optionals looks a little confusing and a bit terse, but I eventually learnt to like it and prefer it to the JavaScript approach.
In fact, there is an argument to be made that having a piece of code stand out with an exclamation mark (such as with optionals) is a good thing because of how exclamation marks are used in English to draw a reader’s attention. They can be seen as flag to a developer that “there is a variable here that you should take care with!!!”.
Development ecosystem and tooling
Summary: I preferred the development ecosystem for React Native. Xcode feels too cluttered and hot reloading a React Native application beats waiting for a Swift app to compile.
Xcode vs WebStorm
Creating React Native apps means spending most of your time writing front-end code that is pretty much identical to creating web-based React projects. I went for WebStorm as my development tool of choice as this is what I use with React and other JavaScript projects.
Developing native iOS projects means using Xcode which I have experience using in C++ projects.
I preferred using WebStorm compared to Xcode. While Xcode is really powerful and has a large suite of tools that provide fine-grained control, I actually found it too cluttered and ‘dense’. I often found it took me a while to find the right checkbox or submenu to achieve relatively simple things.
Hot reloading vs compilation
The development tools that come with a React Native project allow you to take advantage of hot reloading. This means that whenever you save your project the emulator showing your app will automatically reload to reflect your changes.
Compared to re-compiling Swift and waiting for the app to close and re-open again, it made developing (seem) a lot faster, especially when making small tweaks to layouts when I wanted to iterate quickly.
UI Elements, layout and connecting to logic
Summary: While it is powerful, I found the Xcode Interface Builder too cluttered and frustrating. I preferred using FlexBox and CSS to programmatically control layouts in React Native. Also, connecting UI elements to business logic is easier with JSX in React Native projects than with using ‘click and drag’ in the Interface Builder.
Background
Xcode contains a very sophisticated set of tools in its Interface Builder. You can create your UI by dragging and dropping components and editing these with a series of menus and helpers.
React Native layouts are styled programmatically using the same FlexBox and CSS technology that is used on the web. In React Native, UI elements are connected to business logic programatically by combining markup for the UI elements with JavaScript to control them in JSX (in the same way as with React projects).
Layout
It initially felt like the menus in Xcode and the ability to position elements with my mouse gave me lots of control over the layout of my views. However, I started to find that the Interface Builder was too complex and cluttered to use comfortably. I found that controlling the layout of elements became too fiddly and too time consuming. I also found it quite frustrating when a layout didn’t work in different orientations or screen sizes as I would expect.
For example, I spent half a day trying to make a view properly scrollable (I wrote a tutorial here if you are interested), whereas in React Native you can simply import the ScrollView component and wrap a component with it.
With React Native, you control layouts with CSS and FlexBox. I already had lots of experience using these technologies from web-based projects, but even if I hadn’t I realised that you only have to learn a few key things to be able to create sophisticated and rich interfaces in React Native.
Connecting UI elements to business logic
With the Interface Builder in Xcode you can CTRL+Click and drag a link from a UI element to a ‘controller’ source code file where it can be referenced and used as a variable.
While I liked this to begin with, I eventually found it a bit frustrating to make sure that the appropriate controller file was open at the right time and I was zoomed into the appropriate part of the main screen to select the UI element I was interested in. With React Native components, I much prefer how I can programmatically link up a UI element to my logic using JSX all within the same file.
Pre-packaged components
In Xcode you create your UI by dragging and dropping items from a list of pre-made components.
Although the Xcode Interface Builder contains lots of components that can be dragged and dropped into a project, there are lots of React Native components that also come out of the box that make it really easy to work with.
Furthermore, I find it a lot neater to include import statements at the top of my React Native components and have a clear list of all of the components that are included in a section of the UI. This can be contained in a few lines of code, whereas with Xcode I have to visually inspect a representation of a view to see what components are used in a part of an app.
A good example of a really useful React Native component that is pre-packaged with the library is for modal ‘pop-up’ windows. It is a lot easier and much more declarative to control a modal component in React Native compared to using segues in a native app.
In fact, I find that controlling modals in React Native is actually a lot more declarative and a lot neater than in a traditional React project, despite the fact that React portals do a lot of work for you in a React app.
Release and deployment process
Summary: the release processes are almost identical for both native iOS apps and React Native iOS apps. Both native and React Native apps are treated the same by Xcode/App Store Connect.
Deploying React Native to the App Store and native iOS apps involves the same process. You have to open the project in Xcode and follow a standard set of steps. This means that by the time that you open Xcode and start preparing your app for deployment, there is no distinction between a native iOS app and a React Native iOS app.
Although I haven’t released any native Android apps to the Play store, from releasing a React Native Android app to the Play store and from reading online I believe that the processes are very similar. Both processes involve using Gradle as a build tool and uploading an app bundle to the Play store. After this point, there isn’t a distinction from Google’s point of view.
I have read that developing React Native apps with Expo means that you can deliver ‘over the air’ updates to users which means you (and your users) don’t have to go through the App Store/Play Store to deliver some updates. I haven’t explored this in depth and using Expo comes with its own set of pros and cons to consider (see ‘Project Roadmap’ below).
Project Roadmap
Summary: To start any project that builds on the framework and tooling of others, certain important decisions have to be made early on. I found that creating a native iOS app was a lot clearer and more guided than creating a React Native app. This was to be expected, because part of the Apple philosophy is to do things ‘the Apple way’ whereas React Native is in a sub-community of the JavaScript community which has a lot more of a ‘plug and play’ approach.
There are obvious pros and cons and associated tradeoffs with these. Overall I preferred the native iOS route here.
React Native
There are two very similar but different routes to take to create a React Native project: the ‘Expo CLI’ route or the ‘React Native CLI’ (RNC) route. Both of these approaches are explored in the ‘getting started’ section of the documentation. Already at the outset of the project there is a fork in the road.
The main difference between the approaches is that an ‘Expo CLI’ project comes bolted with a tool called Expo which is basically a shell for your app that gives you a really neat and easy way to share your development project with others using QR codes. Expo makes your app feel like more of a hybrid because it hides away native code from you. Also, projects created with Expo allow you to deliver ‘over the air updates’ to users meaning both you and they don’t have to go through the App Store to get updates (similar to deploying updates to a web project).
So choosing an Expo CLI project means that you can’t include any native code in your app … but you may not know if you will or will not need to add any native code at some point in the future.
This happened to me. I wanted to integrate a Firebase client into the front-end for handling authentication. To do this I imported an NPM module into my JavaScript code but to make it work I also had to add a few lines of code in objective-C to a make file. This meant I had to modify the underlying native iOS files. I realised that there wasn’t a way of doing this with an Expo CLI app at all. The only way forward (at least that I could find at the time, perhaps I missed something?) was to ‘eject’ my app. This essentially means removing the Expo shell and having a project structure similar to RNC project … so I would lose the benefits of Expo.
As an aside, at the time, Expo didn’t feel mature enough to me. There were a few missing features and a few big outstanding bugs, but perhaps in the time between it has developed. I really hope so because it’s a fantastic idea and I am sure there are lots of tough challenges they have already had to overcome to bring this to the community.
In summary, it isn’t always clear what is best route is to create a React Native app.
Native apps
In classic Apple style, there is a lot of inflexibility/useful guidance (depending on your perspective and use case!) to create native iOS in the ‘Apple way’.
There are lots of resources that guide you through creating a native iOS app that provide you with what appears to be best practices. For instance, there is a clear linear learning curve in dedicated resources like this book here.
I found it really easy to see and plan the steps I would need to complete and how to achieve them with my native apps.
Bugs and challenges
Summary: When I had bug in my React Native app, I often found it hard to track down the underlying cause of the error. In contrast, this was a lot easier with native iOS apps and Swift and it was often clear whether or not a solution was ‘best practice’.
As with any software project, especially when not too familiar with the technology, I ran into the usual sorts of bugs and challenges. Not being able to run my apps on emulators and not being able to style the objects in the way I wanted etc.
When I turned to the internet for help, I found that I could generally find a solution for my native iOS problems a lot easier. Furthermore, it was often clear that a solution was considered to be best practice because of the guided nature of creating native iOS apps (see ‘Project Roadmap’ above).
More often than not with React Native, I ended up sifting through GitHub issue pages trying to track down exactly what the cause of a problem was. I found there seemed to be lots of changes between small incremental versions of React Native, which is great for progress but frustrating when trying to eliminate reasons for bugs.
React Native seems to have too many moving parts and issues between versions. It seems to me that it is because the flexibility offered by React Native and its tooling means that there are so many different ways of configuring an app and developing it. This means it was often hard to track down the exact source of the problem.
In contrast, there seems to be the way to develop iOS apps with Swift/Xcode and that meant it was a lot easier to track down the exact cause of bugs and find solutions to them.
Overall
I much prefer creating iOS apps with React Native over the Swift/native approach.
The development environment I used for React Native feels a lot less cluttered and easier to work with compared to developing a native app with Xcode.
In terms of creating and controlling UI elements, I found that the catalogue of pre-made and community-built components for React Native was more than good enough and I found it easier to use FlexBox to control the layout as opposed to the fiddly and dense nature of the Interface Builder in Xcode. Also, I preferred linking UI elements to my application logic using JSX as opposed to manually dragging a reference from UI elements to a controller file in Xcode with my cursor.
Despite the fact that I have lots of experience working with React and JavaScript I actually preferred working with Swift. However, the transfer of skills from a web background was very easy and is definitely a factor to consider when evaluating the tools from the point of view of leading a team to create apps.
I do appreciate that React Native may be playing catch-up with Swift/native iOS tools to implement new features and this may cause some developers some real frustration. However, I didn’t run into any of these myself with the projects I have worked on (yet!).
The project roadmap is a bit frustrating with a React Native project, but after going through the process a few times this has become a lot clearer. This wasn’t the case with native iOS apps, where there was a clearly guided/inflexible (depending on your perspective!) approach even the first time.
When I had bug in my React Native app, I often found it hard to track down the underlying cause of the error. In contrast, this was a lot easier with native iOS apps and Swift and it was often clear whether or not a solution was ‘best practice’.
Finally, although this article has been comparing native and React Native approaches to creating iOS apps, I haven’t commented on the ‘cross-platform’ nature of React Native app. This means developing with React Native can (nearly) create two apps for the price of one (for Android and iOS)! In terms of developer experience, this was great!
Thanks for reading. I hope you found this useful. Please let me know if you have any questions or comments.