I’ve spent this past week diving into SwiftUI, seriously, for the first time. As you know, I’ve been keeping my eye on it since it was released, but I’ve avoided it due to a combination of hesitancy, apprehension, and just being too busy with other projects and work. However, while taking some time off from contracting work, I decided to dive in.

I finally had an app idea for which SwiftUI would be well-suited. It’s a small and simple utility app, essentially just a single view. It’s multi-platform for iOS and macOS. I’m making it mostly for myself, but I do plan on releasing to both App Stores when finished. I thought this would be the perfect opportunity to experiment with SwiftUI. The risks are low. While I do consider this “a real app”, the scope is incredibly narrow.

One of my objectives is to stay within the guard rails of SwiftUI, so to speak — stick with the defaults, do nothing fancy, avoid heavy UI customization, only use SF Symbols, no third-party dependencies, try to avoid UIKit unless absolutely necessary. Just a simple, vanilla app for both platforms. Another restriction is to deploy to the latest OS releases only — just iOS 15 and macOS 12 — to avoid all the potential issues with previous versions of SwiftUI.

I want to see what is possible with all of these constraints. That is, what can I achieve — and how fast — with this no-frills, vanilla development model? Firstly, I want to experience the unadulterated “happy path” of development, which I rarely encounter. Secondly, I want to see how far we’ve come since the earlier days of Xcode, iOS, and macOS. I started making apps in college with iOS 5 — it’s been a decade!

Regarding the app, I will follow up with more posts in the coming weeks.

* * *

Overall, it’s been quite a pleasant experience. In fact, sometimes it feels like fucking magic. This is especially true when I realize I’m building two apps at once for two different platforms. I’m often delighted by how easy it is to build views, and how quickly I can build them — not just the static UI, but actual functionality as well. In UIKit, it’s usually a two step process: (1) build a nice UI that does nothing, (2) hook it up to actually do something. In SwiftUI, this often becomes a single step because of bindings, or at least the distinction between the two is blurred.

Some things in SwiftUI are so well done. Complex UI that used to be tedious to implement is suddenly effortless. I’m not just thinking about UIKit in its current state, which has improved and evolved tremendously over the years. I’m thinking about what it was like in iOS 5 as well. Take, for example, collapsible table view sections. That was not trivial to solve in iOS 5. In iOS 15, it is mostly effortless with UICollectionViewCompositionalLayout, but there is still a good amount of boilerplate. In SwiftUI, you merely need a DisclosureGroup. That really is incredible.

However, SwiftUI comes with caveats — which I’ve covered previously. There are some sharp corners and unexpected behaviors. The moment you wish to break away from the defaults and do any customization, you start to hit walls. (Thus, my constraints for this project.) However, I even face issues when trying to implement simple functionality that I’ve written in UIKit dozens or hundreds of times, only to discover that functionality is simply missing from SwiftUI.

* * *

The other prominent aspect of working with SwiftUI is the learning curve. Sometimes it feels very odd coming from my very experienced background with UIKit. In UIKit, I’m a native speaker. SwiftUI is familiar enough that it’s like learning a new grammar, but not that new. I find myself either surprised at how easy something was, or frustrated that it is simply missing from the API.

Unfortunately, API discoverability and documentation are a nightmare. With UIKit, you can typically type . after any instance, partially type the name of a member you are looking for, and find it quickly in the auto-complete list — or you can just browse what is available. This does not happen reliably with SwiftUI. Auto-complete is frequently populated with members and modifiers that simply don’t work in the given context. Often documentation is sparse, and it is difficult to answer the question “How do I do X from UIKit in SwiftUI instead?” solely using Apple-provided resources. I am grateful for the extensive resources provided by the community.

* * *

Despite these issues I find SwiftUI enjoyable to use overall and often find it easier than UIKit and AppKit — at least once I figure out the right way to do something in SwiftUI. But I think my feelings are heavily influenced by the constraints and simplicity of this specific project, namely, not having to worry about deploying to previous OS releases.

Ultimately, I agree with Steve Troughton-Smith’s advice that SwiftUI is great for auxiliary, non-critical UI layouts but beyond that it does not meet the bar for complex, reliable apps. Even with my small project, I have had to make a few UI/UX compromises that I would not have had to make in UIKit — and I would not have shipped these compromises in a more serious app. I have other app ideas and other apps in-progress for which I will not be using SwiftUI, not only because I want to deploy to earlier OS releases but because I also need more than what SwiftUI currently offers.

Still — after this week of SwiftUI development, it is clear to me that SwiftUI will eventually be The Future™ — but it is not there yet. SwiftUI continues to trail far behind UIKit, but I am looking forward to the day it catches up.