A React Native Engineer Builds A SwiftUI App: The Good, The Bad, And The 🤯

Daniel Merrill
Dec 22, 2021 · 6 min read

I have been working almost exclusively with React Native for just about my entire software career, so if anyone is in danger of becoming entrenched in the framework, it’s me. And while I believe that React Native is often the best tool for building mobile apps, I want to be sure that I’m choosing it from a place of experience, not just familiarity. So, I decided to challenge myself by building an iOS app with SwiftUI to broaden my horizons and see how native iOS development compares to React Native.

The finished ComputersAndJazz app.

The app I built was very minimal—a tab navigator with three screens. One screen displays a list of jazz musicians fetched from last.fm. Another displays a list of vintage computers. For the third screen, I built a simple “hello world” gesture-based animation—a ball that you can drag around on the screen that springs back to the middle of the screen when you release your finger.

I found a lot to like about working with SwiftUI (and several things that could use improvement). Will I choose SwiftUI for my next mobile app? Is it “better” than React Native? Read on to find out! (spoiler: the answer is “it depends”)

The Good:

  • Once you get over the syntax differences, SwiftUI feels a lot like React: It allows developers to create declarative user interfaces, meaning the UI that is rendered is a direct result of app state. This is important because the views that are rendered remain in sync with the data that drives them. I found myself able to mentally map several React concepts onto SwiftUI—React components become SwiftUI “views”, props become view parameters, component state remains view state, and React context roughly maps to SwiftUI environment objects.
  • Animations are built in: I’ve spent several hours learning how to create performant animations in React Native using Reanimated. While Reanimated 2 is a huge leap forward in developer friendliness, it still doesn’t really hold a candle to SwiftUI. All of the animation primitives are built right into SwiftUI and require minimal configuration to create slick animations. Imagine if performant animations could be created in React Native using nothing but setState and you’ll get the idea (for anyone wondering, calling setState in order to update your component 60 times per second would likely grind your app to a halt, or at the very least cause some noticeable chug). I only dipped my toes into SwiftUI animations, but the “hello world” that I created was enough to demonstrate how clean and intuitive creating animations could be.
This gesture-based animation was conceptually simple and quick to create. See the file: https://github.com/computerjazz/computers-and-jazz/blob/main/ComputersAndJazz/Views/GestureView.swift
  • Navigation is built in: Stack and Tab navigation in SwiftUI is achieved by wrapping your views in the corresponding NavigationView and TabView wrappers. There are also some interesting navigation patterns that aren’t possible in React Native (at least with react-navigation)—there’s no concept of “navigation configuration” the way that react-navigation requires that you declare your entire app navigator at the root of the app. Everything happens “on the fly”—you can navigate to any arbitrary view, even a view declared inline in a parent screen. It feels more flexible, but I also found myself missing the explicitness of declaring my nav structure ahead of time.
  • Previews are fast: I was under the impression that creating a native app would require a lot of waiting around while the app builds. I found that SwiftUI previews were almost as good as the React Native “fast refresh” experience (although the previews did suffer from becoming stale and crashing for unknown reasons). XCode builds were also much faster than React Native XCode builds, although my sample app has no external dependencies, so it’s probably not a useful benchmark.

The Bad

  • Obscure error messages and behavior: When things go south, it can be a pain to figure out what needs fixing. I experienced a lot of infinite spinners and cryptic messages in XCode. Sometimes hitting “Try Again” would fix the issue, sometimes not. The following is a preview crash resulting from attempting to read an out-of-bounds array item:
I don’t want to have to parse a thousand lines of memory dump to figure out that my array index was out of bounds.
  • So many curly braces: It starts to look like the JavaScript “pyramid of doom” pretty quickly, and Inserting new views in the right spot becomes tricky. I found myself missing closing JSX tags—I can still get lost in JSX (especially if I have a lot of nested View tags), but at least JSX has a little more information to orient myself with.
This is a good reason to keep it simple, and make sub-components early and often.
  • It doesn’t always “just work”: There were a few things that I thought should be easy but gave up on before I figured them out: a tab navigator that allows the user to swipe between tabs, an animation that only animates some parameters but not others. The official docs weren’t always thorough, and I waded through lots of StackOverflow workarounds that felt weird and hacky. It was a red flag that I was already fighting the framework, even though I was building just about the simplest app possible.
  • Framework magic: I’m used to being able to see where imports come from, and I was often confused about when I could “just magically use” an api and when I could not. I’m sure this would become more clear with experience.
  • Brittle JSON parsing (compared to JS): This is more of a gripe with Swift than SwiftUI, but I found dealing with JSON data in Swift cumbersome and error-prone. If the JSON you’re parsing doesn’t exactly match the struct you’ve created, a runtime error is thrown. Prepare to spend a long time fine tuning your data types. I guess this is the tradeoff of working in a strongly typed language.

The Mind-Blowing 🤯

  • The shorthand gets ridiculous: Again, this is more of an observation about Swift and not SwiftUI, but the shorthand that the language allows can border on the absurd. Go read the section on sorting methods in the Swift docs for a taste of it. The example sort function in the docs begins as a sensible-looking function that returns true if the first element is greater than the second, or false otherwise:
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}

This function is then refactored to be shorter and shorter, each step taking advantage of different swift shorthand features, each with its own specific syntax to remember. In the end, this function gets reduced down to a single character: > . This would probably start to feel second-nature in time but it seems like the kind of cleverness that obscures the meaning behind code and could cause new developers to quickly feel confused or lost.

So, will I choose SwiftUI for my next project?

The answer is a resounding…”maybe!”. More specifically, the things that I thought I would dread about building a native iOS app were largely debunked (slow feedback loops, cumbersome view/controller logic and boilerplate). And I’m confident that given a bit more time, I’d get used to some of the language features that feel unnatural to me now.

I still think that the requirements of the app in question would have to be pretty specific for me to reach for native first—it’s hard to beat the development speed and cross-platform support of React Native. Those requirements might include:

  • An app that needs to be more performant than React Native can deliver.
  • An app that is designed to have a 100% pixel-perfect native iOS look and feel.

The apps I’ve worked on up to this point haven’t fallen into those categories. But if I found myself planning a new app that did, I’d be happy to jump back into SwiftUI.

Check out my sample SwiftUI app here:
https://github.com/computerjazz/computers-and-jazz

Async

Articles by the Async team