DEV Community

Cover image for SwiftUI: An Introduction
marinbenc🐧 for CometChat

Posted on • Edited on

SwiftUI: An Introduction

Apple dropped a bombshell for developers on WWDC 19. Among a bunch of other things, they released SwiftUI — a whole new way of making iOS apps and the biggest change to iOS development since Swift came out. Say goodbye to storyboards, auto-layout or coding your UI by hand, SwiftUI is here to replace all of that. If this sounds good to be true, you might be right. This introduction is here to show you what SwiftUI is all about, how it fits in the existing ecosystem and what it means for iOS developers.

Whether or not you're brave enough to switch to SwiftUI now, the fact is that the future of iOS development is SwiftUI. Currently, SwiftUI is in the same place Swift was a couple of years ago, starting small with a handful of early adopters. But Apple's messaging is pretty clear: Just like Swift, SwiftUI will become the dominant framework in a couple of years.

That's why learning SwiftUI, sooner rather than later, is a valuable (and marketable!) skill for any iOS developer and this introduction is here to get you started.

I am creating this course in collaboration with CometChat, a modern chat platform to help you add chat to you Swift app. During the next few weeks, we’ll be releasing installments of our free SwiftUI course here on dev.to! In the course, you’ll dive deep into SwiftUI by building a real-world production-scale chat app, learning SwiftUI in a practical way, on a scale larger than a simple example app. Follow me to get notified of future parts of this course! You can also follow @CometChat on Twitter see the course of CometChat’s blog.

How SwiftUI Works

To see just how different SwiftUI is from UIKit, let's look at a SwiftUI login screen.

A plain login screen written in SwiftUI

That screen is coded with the following code:

import SwiftUI

struct ContentView: View {

  @State var email = ""
  @State var password = ""
  @State var error: String?

  var body: some View {
    NavigationView {
      VStack {
        TextField("Email", text: $email, onCommit: validate)
          .padding()
          .textFieldStyle(RoundedBorderTextFieldStyle())
          .keyboardType(.emailAddress)
        SecureField("Password", text: $password, onCommit: validate)
          .padding()
          .textFieldStyle(RoundedBorderTextFieldStyle())

        Button(action: login) {
          Text("Sign in")
        }.disabled(error != nil)

        error.flatMap {
          Text("Error: \($0)").foregroundColor(.red)
        }
      }.navigationBarTitle("Log In")
    }
  }

  func validate() { ... }
  func login() { ... }
}
Enter fullscreen mode Exit fullscreen mode

Right now you might not understand everything that's going on in the code above — that's okay. Focus on how much code there is. This is the whole UI. There are no additional storyboards, constraint setup code or UIView subclasses. Even without knowing SwiftUI, you can kind of figure out what the screen looks like and what it does just by looking at the code.

You can think of SwiftUI views as being split between two workers. The workers are each working on their own thing, and only indirectly impacting each other. The first worker renders the UI based on the current state. The other worker takes input from the user and changes the state.

How SwiftUI works

In a SwiftUI View, the state is a variable marked with @State, while the view is a computed variable called body.

struct ContentView: View {
  @State var count = 0

  var body: some View {
    VStack {
      Button(action: { self.count += 1 }) {
        Text("Increment")
      }
      Text("Count: \(count)")
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

You can think of body as a function from the state to the view. The @State annotation tells SwiftUI to listen to changes of that variable. Whenever it changes, SwiftUI will call body to re-render the view based on that new state.

The Body

The syntax inside of body might look strange, like a different language embedded into Swift. In reality, it's all just plain old Swift. There's no compiler magic or special exceptions for SwiftUI: You could implement SwiftUI using plain Swift if you had enough time.

The reason the syntax looks so strange is that it uses implicit [return](https://forums.swift.org/t/se-0255-implicit-returns-from-single-expression-functions/22544)s and function builders, two Swift 5.1 features.

Single-expression functions don't need a return keyword, which is why, in the above example, body can return a VStack even if you don’t write return.

Function builders let you define functions that implicitly return an array as a list of expressions. For instance, if you had a builder for an array of integers, you could write a function like the following:

@NumbersBuilder
func build() -> [Int] {
  1
  2 + 2
  3
}
Enter fullscreen mode Exit fullscreen mode

Swift will then create an empty array, take every top-level expression in the function, evaluate then and add their results to the array, returning [1, 4, 3] for the above example.

Similarly, in the SwiftUI example above, VStack is a struct that receives a function builder of Views in its initializer. Because Swift lets us emit the round brackets around closures, the syntax looks like VStack { ... }. Inside the curly braces, every top-level expression gets evaluated to a list of Views (in this case containing Button and Text) and added to a vertical stack view.

If you remove all syntax sugar from the above body, you'll end up with this:

var body: some View {
  let button = Button(
    action: { self.count += 1 },
    label: { return ViewBuilder.buildBlock(Text("Increment")) })

  let countText = Text("Count: \(count)")

  return VStack(content: {
    return ViewBuilder.buildBlock(button, countText)
  })
}
Enter fullscreen mode Exit fullscreen mode

It looks a lot messier, but you might get a better idea of what the code does this way.

The reason I'm telling you this is because SwiftUI will look daunting at first. You'll also receive very opaque error messages that you that will make you scratch your head in confusion. Knowing that underneath it all lies familiar old Swift will illuminate a lot of these errors for you.

The State

The counter screen example works without any additional code. You'll notice there's no "glue" code anywhere telling SwiftUI to update the counter label with the new text. That's because SwiftUI does that automatically.

By annotating count with @State, SwiftUI knows to call body to reload the view whenever count changes.

This still isn't compiler magic, just plain Swift. The @State annotation is made with a new Swift feature called property wrappers. Property wrappers are structs or classes that hold a single property and expose a setter and a getter, but when used they indistinguishable from a plain value.

By incrementing count (count += 1), you're really calling the setter in the State property wrapper. In other words, the following lines:

@State var count = 0
count += 1
Enter fullscreen mode Exit fullscreen mode

Can be de-sugared to:

let countState = State(initialValue: 0)
countState.wrappedValue += 1 
Enter fullscreen mode Exit fullscreen mode

State has a defined setter for wrappedValue that will tell SwiftUI to reload the view. Thankfully, you don't have to write all that. By using property wrappers, SwiftUI lets you use the state as a regular variable, reloading the view automatically on every change.

You might notice an issue here. If we re-render the body every time a tiny change happens, we'll end up rewriting the whole UI every couple of seconds! Thankfully, SwiftUI only updates the views that need updating. The view hierarchy has a tree-like structure, and you can compare the current tree of views with the next one. Only the nodes that are different (and their sub-nodes) need to be updated.

The goal of SwiftUI is to let the system take care of more things, so you can focus on making the app you like.

The Three Tenets of SwiftUI

You'll hear a lot of buzzwords around SwiftUI, both from Apple and the community. To realize what SwiftUI means, it's important to unpack those buzzwords:

Composable

First, SwiftUI is composable. It doesn't separate views from view controllers, instead, your whole UI is made up of Views. This gives you the flexibility to separate your code in any way you see fit. You can have one huge View for your app, or have a huge hierarchy of small Views. This also makes it easier to reuse stuff. Every View can be placed inside every other View, with no need for child view controllers or lifecycle management pains.

A composable View hierarchy in SwiftUI

This makes SwiftUI development incredibly easy for copy-and-paste development. While seasoned developers might scoff at that sentence, the way people build apps these days is by searching for simple things like "how to make a button" and copying the code.

I built a login screen this way in SwiftUI, without any previous knowledge, in 15 minutes. If I wanted to do that in UIKit, I'd have to spend days reading about Interface Builder, auto-layout and outlets before I could even start coding.

Finding a working example and tweaking it to your needs is incredibly productive, and SwiftUI lets you do that.

Declarative

Secondly, SwiftUI is declarative. Instead of creating a storyboard or creating your UI by instantiating and setting up views, SwiftUI lets you use Swift code to describe your UI. You tell SwiftUI, "I want a black view here, and a red view inside of it." SwiftUI then goes and draws those views for you. And it's all just plain Swift code.

What's frustrating about Storyboards is that they're not code. Everything needs to be constant and pre-determined. You can't dynamically calculate a margin or hide a view based on some condition. In other words, there are no **if**s in a storyboard.

However, the code inside SwiftUI's body is plain Swift. That means that all of Swift's tools are available to you: You can use structs, classes, collections, loops and control blocks to write out your UI. If you want to show an empty state in your list, you can check if the list is empty and show that view, and otherwise show the list.

By giving you the flexibility of Swift, your UI code becomes a lot simpler, and the tension between the Storyboard and your code disappears.

Reactive

Finally, SwiftUI is reactive. If you want to change the color of a button in UIKit, you'd grab the button and directly set its color. SwiftUI doesn't offer this direct control. Instead, you'd have a state variable that defines button's color. You'd also have a different function that returns a new button based on this new variable. Whenever you change the state variable, SwiftUI will automatically call the latter function and draw the new button.

This eliminates a whole class of bugs that come up as a result of a difference between your view controller's state and what's shown on the UI. By letting SwiftUI reload your views automatically, you know that your view's state is the single source of truth for the view. This lets you focus on working only on the state, without having to think of what will happen in your view.

This idea of separating working on your state and your view is a long-standing idea in the iOS community. It's the idea behind the most popular architectural patterns like MVP (Model and Presenter separated from the View), MVVM (Model and View Model separated from the View), VIPER and others. With SwiftUI, you get this separation out of the box.

This combination of features in a single framework isn't new by a long shot. React, Vue.js and Angular — three of the most JavaScript UI frameworks already have these features. With frameworks like Electron, React Native, Jetpack Compose and Flutter, this approach to UI is already here on mobile, in huge apps like Instagram, Uber or Pinterest. This is well-trodden territory and has proved itself to be one of the most productive ways to program UIs.

Thinking SwiftUI

By now you noticed that SwiftUI works a lot differently than UIKit. Just like you can't write Swift as you wrote Objective-C, you can't write SwiftUI like you wrote UIKit. SwiftUI requires a change of mindset.

When looking at a login screen, an iOS developer will first think of a view controller containing two text views and a button. In SwiftUI, you'll instead think of three different Views. First, you'll need a View for the text view. Next, you'll need another View for the button. Finally, you'll need a View that will assemble two text views and the button.

When working with SwiftUI, you'll stop thinking in terms of properties and methods, and start thinking in terms of state. A text view has a state variable that holds the text. Instead of changing textView.text, you'll update the state. The state change will then trigger a call of the body variable, where you'll return a new text field containing the new text.

By starting simple and slowly building out a whole app, this type of thinking will become second-nature by the end of our SwiftUI course.

How Hard is SwiftUI?

I won't lie to you, SwiftUI is completely different than UIKit. When I first started building an app in SwiftUI I had a little identity crisis. Here I was, an iOS developer who has worked on tens of apps, wrote and edited books about iOS, spoke at iOS meetups — struggling with centering a text field.

Over time, though, I found my way around the different types of views. When I get stuck, I knew what to try and where to find the solutions. I realized that, even though my UIKit knowledge isn't of much use, I still understand Swift, the iOS ecosystem and programming. Thankfully, SwiftUI is nothing but Swift! Armed with these skills (and our course!), I'm sure you'll quickly get the hang of SwiftUI.

That's where we come in. During the following few months, we'll be releasing a SwiftUI course that will teach you how to make a complete, production-level SwiftUI app. Read to the end of this article to find out more about the course!

What about UIKit?

You can think of UIKit now as Objective-C a couple of years ago. Even though SwiftUI is the new hotness, it still stands on the shoulders of UIKit (and Objective-C, for that matter). That means that UIKit isn't going away soon.

Slowly, over time, Apple will probably put more and more focus on SwiftUI, until eventually, SwiftUI will be the framework receiving new features instead of UIKit. Don't worry, though, you still have plenty of time.

Speaking of which, the pragmatists among you might want to hold out a little bit until you convert your app to SwiftUI. Just like the early days of Swift, SwiftUI is young and not fully molded into shape. Some things that exist in UIKit are still just plain missing. As SwiftUI evolves and matures, expect a lot of changes and View rewriting.

That isn't to say you shouldn't learn SwiftUI. If you're an iOS developer, knowing SwiftUI (and knowing it early) can help you stand out among other, more conservative developers. Eventually, everybody doing iOS development will need people who know SwiftUI. Getting in early can be a competitive advantage for you or your company.

Should I Use SwiftUI?

You should learn SwiftUI as soon as possible. But, should you convert or start making your app in SwiftUI? The answer is, as it is often, an unsatisfying “it depends”.

SwiftUI is very young and the small SwiftUI community is only starting to grow. Some things that are available in UIKit don't yet exist in SwiftUI, and there are some kinks Apple still needs to work out. I expect SwiftUI to change a little bit in a year's time, which might not be the best thing to hear if you want to convert your whole app.

If you're starting to make an app now and only want to support iOS 13, I'd strongly consider using SwiftUI as much as possible.

If you already have an existing app, it might be more practical to build new views in SwiftUI (and convert a few existing smaller ones) and use UIKit and SwiftUI in tandem, slowly increasing the ratio of SwiftUI code over UIKit. John Sundell seems to agree with this thinking and has some good practical advice on his blog post about how to mix and match SwiftUI with UIKit.

If you decide to go full SwiftUI, be ready for some changes down the line. If you decide to not use SwiftUI at all, don't be surprised when Apple pulls the UIKit rug out from under you sooner than you expect. I suggest going right down the middle and gradually adopting SwiftUI as you go.

Let's get started!

To help you master SwiftUI, we're working on a full SwiftUI course that we'll release over the coming few months.

This SwiftUI course is designed for the practical developer. You'll learn SwiftUI by building our a real-world production-level chat app. You'll see the benefits and disadvantages of SwiftUI on a scale larger than a simple example app. You'll also learn how large SwiftUI apps can be structured and architected, as well as how to leverage other new frameworks like Combine to help you build your apps.

While other courses and Apple's website offer great short examples, diving in deep is the only way to fully learn a framework, and that's what we'll do in this course.

Here's how this course is structured:

  • SwiftUI: An Introduction (You are here!)
  • Build your first Swift UI screen
    • What's a View?
    • Using Previews
    • Managing the State
  • Working with Lists in Swift UI
    • Lists and Navigation
    • Building the Chat Screen
  • Build a chat app interface in SwiftUI for iOS
  • State management with Combine
    • State and Binding
    • Environment Objects
    • Introduction to Combine
  • Complete the SwiftUI chat app for iOS
    • Using Combine for Networking
    • Creating Combine Publishers
    • Hooking everything up

If you want to receive the next part of the course as soon as it comes out, follow me here, or check out the posts on CometChat's blog!

Top comments (0)