A first look at SwiftUI: Apple’s declarative new UI framework
Declarative UI programming patterns have become incredibly popular over the last few years. On the web with frameworks like React and Vue.js, in cross-platform development environments like React Native and Flutter, and through native tools like RxSwift and RxCocoa — declarative UIs and the reactive principles behind them have truly taken many development communities by storm.
It’s therefore especially exciting to see Apple step into this scene, with their own, completely native declarative UI framework — called SwiftUI. While there’s a ton of functionality to be discovered and experimented with in this new UI framework for all of Apple’s platforms — let’s start by taking a first look at some of the fundamentals of SwiftUI, and how it lets us drastically reduce the amount of code needed to produce common forms of UIs and interactions.
It all starts with a view
One key aspect of most declarative UI frameworks is that they aim to boil any kind of UI down into a series of components — which can then be combined and composed in order to form different sets of views, animations, and interactions.
In SwiftUI, a component is simply called a view, and is defined through the View
protocol — which only has a single property requirement, a body
— which interestingly is also, in turn, a View
. That’s really important in terms of the API’s design, since it makes the whole system completely recursive — as views can easily be embedded inside of other views.
In their own code samples, Apple makes heavy use of several new Swift 5.1
features, for example the new “DSL-like” syntax — which allows return
statements to be omitted — and the new some
keyword, which enables a protocol with associated types to be used as a return type. While I’ll cover all of those new features in great detail in upcoming articles on Swift by Sundell — let’s take a look at how they let us express View
implementations in a very nice and concise way:
struct WelcomeView: View {
var body: some View {
Text("Welcome to my fantastic app!")
}
}
Without any syntactic sugar, the above is basically equivalent to this:
struct WelcomeView: View {
var body: Text {
return Text("Welcome to my fantastic app!")
}
}
Regardless of which syntax we’ll end up using, what we’ve done above is to define a new view (which importantly is not a UIView
or an NSView
, but a new kind of SwiftUI view) — that in turn displays a text when rendered. To display our view inside of an app, we can use the new UIHostingController
view controller type to embed it into any standard UIKit app:
let vc = UIHostingController(rootView: WelcomeView())
The beauty of that design, is that it enables us to quite easily plug code written using SwiftUI into an app written with good old fashioned UIKit (never thought I’d call UIKit “old fashioned”, but here we are), since a UIHostingController
instance acts just like any other view controller — it can be pushed onto a navigation controller, presented modally, and so on.
Rendering a list of items
Now let’s take a look at how we can use SwiftUI to perform a really common task when building apps for any Apple platform — rendering a list of content. Let’s say we wanted to render a list of articles, and enable the user to tap one article in that list to go to some sort of detail view.
We’ll start by making our Article
model conform to the Identifiable
protocol — which is not a requirement, but enables SwiftUI to uniquely identify each model instance without any additional code on our part. The Identifiable
protocol only requires our model to have some form of Hashable
identifier, such as a UUID
:
struct Article: Identifiable {
let id: UUID
let title: String
let preview: String
}
Next, let’s create our actual list view, which will again be a type conforming to the View
protocol — this time returning a List
as part of its body
implementation, which will take each element within our list view’s array of articles, and convert it into a Text
based on that article’s title — like this:
struct ArticleListView: View {
let articles: [Article]
var body: some View {
List(articles) { article in
Text(article.title)
}
}
}
The above code is all we need in order to render a UITableView
-like interface where each row displays the title of an article — pretty impressive!
One really nice aspect of Swift UI’s API is that it’s very much designed to be scalable in terms of how much we’d like to customize any given aspect of the resulting UI. So while our first, simple implementation already gives us a fully functioning list — with a few small steps we can start tweaking it into something that looks much nicer.
For example, in order to also display each article’s preview text below its title, we can create another Text
view, and put both of those texts into a VStack
that’ll align them vertically in a stack-like fashion. We’ll set our VStack
to align all of its members according to its leading edge, and we’ll also customize the fonts used to render each text as well:
List(articles) { article in
VStack(alignment: .leading) {
Text(article.title).font(.headline)
Text(article.preview).font(.subheadline)
}
}
With our row rendering code complete — let’s now implement navigation. To do that, we’ll start by wrapping our List
view in a NavigationView
(which is the SwiftUI equivalent of a UINavigationController
), and we’ll also give our list a navigation bar title — which will automatically be displayed in our UI’s navigation bar when the list is the currently active view:
NavigationView {
List(articles) { article in
VStack(alignment: .leading) {
Text(article.title).font(.headline)
Text(article.preview).font(.subheadline)
}
}.navigationBarTitle(Text("Articles"))
}
And just like that, we’ve enabled our UI to be navigated. Now all that remains is to actually perform some form of navigation when a row is tapped — which we’ll do by wrapping each row’s VStack
in a NavigationButton
that has an ArticleDetailView
as its destination
view.
Here’s what our complete implementation of ArticleListView
will now look like:
struct ArticleListView: View {
let articles: [Article]
var body: some View {
NavigationView {
List(articles) { article in
NavigationButton(
destination: ArticleDetailView(article: article),
label: {
VStack(alignment: .leading) {
Text(article.title).font(.headline)
Text(article.preview).font(.subheadline)
}
}
)
}.navigationBarTitle(Text("Articles"))
}
}
}
With the above in place, we can now move on to implementing ArticleDetailView
and the other top-level components that our app needs — each completely self-contained and isolated, and each automatically reusable in many different kinds of situations.
Conclusion
I don’t think it’s hyperbolic to describe SwiftUI as a game changer for anyone building apps on Apple’s platforms. While we’ll dive a lot deeper into SwiftUI and all of its features in future articles — I hope that this first look as given you a glimpse of what kind of programming model that this new framework is using, and how it’ll most likely require all Swift developers to start rethinking how we build and compose our UIs.
Thanks for reading! 🚀