🤘 We're hiring: join our team today.Learn more
Apphud
Why Apphud?PricingBlogContactSign inStart for Free
Ren
Ren
June 24, 2019
28 min read

What is SwiftUI? SwiftUI Tutorial

Want to learn how to use Apple SwiftUI? Discover the SwiftUI basics and get answers to FAQ in our blog!

What is SwiftUI? SwiftUI Tutorial

SwiftUI Tutorial

SwiftUI is a powerful and promising framework that may well surpass UIkit in the future in terms of its robustness and reliability. When Apple announced SwiftUI in 2019, it rocked the development world, with some people considering it Apple's most exciting piece of news since the announcement of Swift in 2014. 

The SwiftUI framework plays into Apple's goal of getting everybody coding: it simplifies the basics and saves you time — which you can then invest into developing custom features. 

If you want to learn how to use SwiftUI and determine whether it's a good fit for your project, you've come to the right place. 

Getting started with SwiftUI may seem intimidating, but it doesn't have to be. In this iOS SwiftUI tutorial, we'll break down the framework, examine its key features and benefits, list its challenges, and then give examples to show you how to navigate the latest version. We'll also examine how SwiftUI differs from other UI frameworks like Flutter and UIkit. 

Get a handle on how to use this modern framework, and you'll be able to build dynamic user interfaces for apps that run on iOS platforms:

  • iOS
  • iPadOS
  • tvOS
  • watchOS
  • macOS

Author's Note: Hi! It's Renat from Apphud – the best tool for analyzing iOS subscriptions. As you know, at WWDC 2019, Apple announced their new SwiftUI framework, which is supposed to replace the existing UIKit. SwiftUI lets developers write code in a declarative way. That shortens the amount of code a lot!

There are already plenty of Apple SwiftUI tutorials with code and examples. I, instead, will try to make this article in Question-Answer format. Alright, let's go.

What Is SwiftUI?

First things first, let's start with the SwiftUI basics. SwiftUI offers a completely new approach to building UIs for iOS, macOS, and other Apple platforms. 

With the release of iOS 13, developers gained access to the SwiftUI framework, which allows you to build a UI entirely with Swift code. 

So, why is SwiftUI preferable to other user interface toolkits? Well, its main draw is that it allows you to design apps declaratively. In other words, you tell the framework how you want your UI to look and work — and then it figures out how to accomplish your vision. 

Contrast that with imperative UI (which is what developers had to deal with before iOS 13), in which you would have to specify implementation details and control every tiny detail.  

Let's say you wanted to build a form with UIKit. You'd need to write code for all details, such as: creating a vertical UIStackView element, adding it to the View, adding rules to center it on the X- and Y-axis of the root view, creating a UITextField element, and so on. 

With SwiftUI's declarative UI, you'd just have to tell the framework that you want to group a button and two text fields in a vertical stack on-screen. SwiftUI then does it for you, making assumptions about some details. The framework makes logical assumptions, but if it isn't what you wanted, you can make modifications. 

So far, we can see that because of SwiftUI's declarative UI, it saves developers a lot of work. But that's not its only strength. SwiftUI works across all Apple platforms, so you just have to learn one layout framework and language. There's a lot of time-saving there to benefit from. 

SwiftUI vs. UIkit

History

We've already mentioned how SwiftUI uses declarative programming, whereas UIkit uses imperative, but let's examine the differences between the two frameworks in even more detail. 

UIKit was the backbone of iOS UI development for 10+ years; it's a mature platform that has been used for pretty much every iOS app out there since iPhone OS 2 was released. As a result, this powerful, versatile framework has had plenty of time to develop development resources.If you get stuck on something, it's pretty easy to find the answers you need. This means that developers can use the wealth of existing know-how to create high-performing, elegant apps. 

SwiftUI, however, is bold and ambitious. It could totally transform the way iOS apps are developed in the future. However, it is still brand-new, and it lacks the knowledge base that UIKit has. You have to weigh the pros and cons of both frameworks. SwiftUI makes cumbersome UI development much more intuitive, but if you get stuck, it could be harder to work through it. 

Approachability

SwiftUI is far more approachable to developers who haven't worked with it before. When you start a new SwiftUI project, the interface is minimal and intuitive, whereas a new UIkit project looks pretty overwhelming.

Apple already has published a great series of articles about SwiftUI with code and examples. I, instead, will try to make this article in Question-Answer format. Alright, let’s go.

Performance

There are two aspects to consider here: developer performance and app performance. There isn't a noticeable difference in the final product, regardless of which framework it was built with. SwiftUI uses AppKit and UIkit behind the scenes, so rendering isn't actually any faster. 

But when you look at development build time, the scale tips in SwiftUI's favor. The hierarchy of View is located within value-type structures that are stored on the stack. Therefore, there isn't any costly memory allocation, and, in some situations, there is greater performance. 

Testing

When compared to UIKit, SwiftUI has a noticeable lack of documentation on user behavior simulation and testing. While developers can find their own solutions, it's important to keep in mind that this aspect has been the slowest-maturing for the platform. 

Flutter vs. SwiftUI

While UIKit has long been the predominant method of developing iOS apps, there is another framework - Flutter. It is a multi-platform, open-source mobile SDK from Google and can be used to build Android and iOS apps from the same source code. Like SwiftUI, Flutter is a declarative UI framework. 

Flutter and SwiftUI both allow you to create composable components. The former framework calls them "widgets," and the latter calls them "views."

Everything is a widget in Flutter. If you're creating a Text widget and want to add some styles to the text, you'll need to make another widget, TextStyle, and assign it to the Text widget's parameter. 

In SwiftUI, things are a little different - not everything is a "view." Instead, you work with views and modifiers – so you would make a Text view and then apply fonts and colors via modifiers. 

Another difference is that Flutter has two types of widgets, stateless and stateful. Stateful widgets must be used to store a local state in Flutter. But with SwiftUI, the local state can be added to any view.  

Benefits and Disadvantages of SwiftUI

Benefits

  • We mentioned above how SwiftUI uses declarative UI, which saves developers lots of time. You'll write less code to achieve the result you want. 
  • You can use SwiftUI's preview feature to immediately see how your code changes affect the View. This is particularly useful when you're working with modifiers, as their order can change the results. The preview feature also makes it easier for designers and web developers to work with you. 
  • In time, SwiftUI might become the standard framework used to create iOS interfaces, rather than UIKit, due to its ease of use. Now would be a good time to start learning it. Being an early adopter can give you new opportunities in your iOS career. 

Disadvantages

  • Users on older operating systems might not be able to use your app. Over 90% of iOS users run on iOS 13 or higher – but with over 1 billion active iPhones, that leaves hundreds of millions of people behind. 
  • Problems are harder to solve, as the knowledge base is smaller. SwiftUI has only been out for a couple of years and isn't widely used in production first. If you are the first developer to struggle with a certain problem, you might be stuck for a while. 
  • SwiftUI doesn't have all the features of UIKit yet. There are some missing pieces — for instance, you can't add a text field to an alert. 
  • Best practices aren't fully-formed. Developers who want to increase their value often adopt patterns used by established companies. This is possible with UIKit, as there are many great architectures already. But there hasn't been enough time for these best practices to take hold with SwiftUI.

Before you begin

To work with SwiftUI you need to have the latest Xcode. You have to be a registered Apple Developer as well. Having macOS Catalina+ is not required but recommended (Canvas will not work without it).

Okay, now in Xcode create a new project and make sure "Use SwiftUI” is checked.

Building an App With SwiftUI

Are you ready to see SwiftUI in action? In the SwiftUI example below, we'll use it to create an app that shows a list of frameworks/technologies for developing mobile apps. 

Step 1: Launch Xcode and create a new project. 

Step 2: Select App, then click Next.

Step 3: Type "demo" in the Product Name field, and click Next. Xcode will launch the project. 

Step 4: Find ContentView.swift, which is the file you'll be writing Swift code in. Clear out the code so only these SwiftUI components remain.

Step 5: On Xcode's navigator panel, choose an iPhone simulator. Then, click the play symbol to run the program. The demo app will show you the list of technologies and platforms that can be used to build a mobile app. 

Questions and answers

Where has Interface Builder gone?

In SwiftUI you don’t need Interface Builder anymore – there’s a new Canvas to replace it, an interactive interface editor which has a very close connection with code. While you write the code Canvas automatically generates a visual interpretation and vice versa. Very useful and safe. For example, your app won’t crash if you forgot to remove or update the @IBOutlet connection on some changed property. In this article, I will skip Canvas explanations and focus only on code.

Are there any changes in the app launch with SwiftUI?

Yes, now the top-level object in view hierarchy is not UIWindow, but new UIScene (or its child UIWindowScene). A window is being added to the scene now and these changes are related not to SwiftUI but to iOS 13 itself.

After creating the project you will notice files AppDelegateSceneDelegate and ContentViewSceneDelegate – is a delegate class of UIScene object which is supposed to manage scenes in the app. Methods look very similar to AppDelegate’s methods, aren’t they?

SceneDelegate is set in Info.plistSceneDelegate is set in Info.plist

In the delegate method scene: willConnectTo: options: a new window is being created with UIHostingController as its root controller. You will see that UIHostingController is being initialized with ContentView as its root view.

So ContentView – is our “home” page. And we will code in this file.

What are the differences between "View" and "UIView"?

Let’s explain ContentView contents word by word. First, ContentView is a struct. Very good beginning! This expands developing opportunities. Next, View is a protocol now! And a very simple protocol. The only method that needs to be implemented – is a body property. All your subviews must be inside body and they must have their own body as well.

struct ContentView: View {     
     var body: some View {         
          Text("Hello, world!")     
     }
}

What is “body”?

Body – is our primary container which includes all our subviews. You can think it’s like a body in an html page where ContentView is a page itself. However in this case our body must have just one some View – any class that conforms to the View protocol.

Opaque return types and what is "some" keyword?

some TypeName – is a new thing in Swift 5.1 which is called opaque return type. Opaque return types are used in cases where you need to return an object with no matter which class but conformable to a given type. In our case, we have to return something that conforms to the View protocol. This can be TextImage , or our custom class. But again: just one instance of it. Otherwise, the compiler will throw an error.

Compiler throws an error when trying to return more than one view.Compiler throws an error when trying to return more than one view.

What is the syntax inside {} braces and where is "addSubview"?

In Swift 5.1 there’s a new feature called Function Builders which allows grouping objects in a declarative way for certain operations. This looks like a closure that returns an array but without any commas and a return keyword.

This mechanism has become a primary feature in SwiftUI. A special builder which creates and adds subviews in a declarative way has been called ViewBuilder. All the hard thing is being processed behind the scenes – you just write your views inside a closure block with a new line. SwiftUI automatically adds them to a view hierarchy and makes optimization for you.

//Announcing ViewBuilder in SwiftUI header file
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, \*)
@\_functionBuilder public struct ViewBuilder {

    /// Builds an empty view from an block containing no statements, \`{ }\`.
    public static func buildBlock() -> EmptyView

    /// Passes a single view written as a child view (e..g, \`{ Text("Hello") }\`) through
    /// unmodified.
    public static func buildBlock<Content>(\_ content: Content) -> Content where Content : View
}

How to add Views and controls to view hierarchy?

Creating views is super easy: you write them inside braces and modify appearance with functions. These functions always return view so you can make a chain of functions separated with dots. The hard thing is to learn new syntax and the list of available modifiers and controls. And of course, Xcode 11 Beta is buggy (as usual) so syntax highlighting might not work sometimes.

var body: some View {
     VStack{
        Text("World Time").font(.system(size: 30))
        Text("Yet another subtitle").font(.system(size: 20))
     }
}

Not all the views have their analogs in SwiftUI. Some of them must be implemented in another way. Here is a list of views with their analogs:

  • UITableView → List
  • UICollectionView doesn’t have an analog
  • UILabelText
  • UITextField → TextField
  • UIImageView → Image
  • UINavigationController → NavigationView
  • UIButton → Button
  • UIStackView → HStackVStack
  • UISwitch → Toggle
  • UISlider → Slider
  • UITextView → doesn’t have an analog
  • UIAlertController → AlertActionSheet
  • UISegmentedControl → SegmentedControl
  • UIStepper → Stepper
  • UIDatePicker → DatePicker

How to navigate between screens?

Instead of a navigation controller, you should use NavigationView. Wrap your code inside the braces and add an action to push the new view. This may be a tap on the list’s row (ex UITableView) or a button tap.

An example of pushing a DetailView

var body: some View {
     NavigationView {
     Text("World Time").font(.system(size: 30))
          NavigationLink(destination: DetailView() {
               Text("Go Detail")
          }
     }
}

To present a screen modally you should use the .sheet construction like this:

Button(action: {
        print("Button Pushed")
        self.show\_modal = true
    }) {
        Text("Present Modal")
    }.sheet(isPresented: self.$show\_modal) {
         ModalView()
        }

As being said, your body must return some view, which can be any class. It means that you can even push a Text or an Image!

How to position views on screen?

Generally, you should use stacks. All the views depend on each other now. You can align your views horizontally (HStack), vertically (VStack), or above each other (ZStack). You can also use ScrollView and ListView, add padding , and even set frame. However, the SwiftUI frame works in a different manner. This will be explained in the next article.

By combining all these containers you can make quite a big tree of views. So you may ask about performance in this case. Don’t worry: SwiftUI is optimized in such a way that the use of stacks doesn’t hit performance. It is said in this video from WWDC (starting at 15:32).

var body: some View {
     NavigationView {
          VStack {
               NavigationLink(destination: LargeView(timeString: subtitle)) { Text("See Fullscreen") }
               Text("World Time").font(.system(size: 30))
          }
     }
}

How to show a navigation bar?

Wrapping your view hierarchy in a NavigationView{} won’t be enough. To show a navigation bar you should add a navigation bar title.

NavigationView {
     VStack{}.navigationBarTitle(Text("World Time"),  displayMode: .inline)
}

Keep in mind that the navigationBarTitle modifier is added not to NavigationView but to its child. DisplayMode is a parameter that controls the navigation bar’s style: large or default.

Is there analog to "viewDidLoad"?

Yes, it is. You can use the onAppear{} modifier and add your code inside the closure. This is similar to Javascript. This modifier can be added to any view. In the example below an http-request is being sent:

struct ContentView : View {
     @State var statusString : String = "World Time"
     var body: some View {
          NavigationView {
               VStack {
                 NavigationLink(destination:DetailView()) {
                     Text("Go Detail")
                 }
                 Text(statusString).font(.system(size: 30))
               }.onAppear {
                     self.loadTime()
                 }
          }
     }
     func loadTime(){
          NetworkService().getTime { (time) in
               if let aTime = time {
                    self.statusString = "\\(aTime.date())"
               }
          }
     }
}

We have written a loadTime function which sends an http-request to get the time from a server. We won’t focus on NetworkService class, you can download the source code from the link at the end of this article.

You will notice the var statusString property which has the @State parameter. What does it mean?

Property wrappers or what is "@State"?

Swift UI update 5.1 has introduced a new feature called property wrappers (or property delegates). This is a set of special property attributes which add some amazing functionality to our properties. In SwiftUI property wrappers are used to update or bind one of your view’s state with your property. For example, a Toggle’s value. By using property wrappers you will be able to get changed values of your views without having to write protocols, functions, or even the notification center!

@State attribute lets us read some view’s values without additional code. In the example above we are updating a text on the screen automatically when statusString changes.

In a new example below we bind properties between `Toggle`’s state and our own property by using the $ sign.

//When Toggle’s value changes it also changes our property value.
struct DetailsView: View {
    @State var changeToggle: Bool

    var body: some View {
          Toggle(isOn: $changeToggle) {
              Text("Change Toggle")
          }
    }
}

Property wrappers are very important in SwiftUI and in Swift 5.1 itself. It is quite a big theme so it will need a separate article. You can watch these nice videos from WWDC: this one (starting at 37th minute), this (starting at 12th minute), and this (starting at 19th minute).

Can I add views at a runtime?

Not exactly. You can’t add a subview at any time because SwiftUI is a declarative framework. And it renders a whole view. But you may add conditions inside a body and reload the view when these conditions change. In this example, we use @State — if paired with the isTimeLoaded property.

struct ContentView : View {
 
    @State var statusString : String = "World Time"
    @State var isTimeLoaded : Bool = false
    
    var body: some View {
        NavigationView {           
                VStack {
                    if isTimeLoaded {
                       addNavigationLink()    
                    }
                    Text(statusString).font(.system(size: 30)).lineLimit(nil)                              
                }.navigationBarTitle(Text("World Time"), displayMode: .inline)
            }.onAppear { 
                self.loadTime()
            }        
    }
    
    func addNavigationLink() -> some View {
        NavigationLink(destination: Text("124!!!")) {
            Text("Go Detail")
        }      
    }
    
    func loadTime(){        
        NetworkService().getTime { (time) in
            if let aTime = time {
                self.statusString = "\\(aTime.date().description(with: Locale.current))"
                self.isTimeLoaded = true
            }
        }
    }
}

struct DetailView : View {
    
    var timeString : String
    
    var body : some View {
        Text(timeString).font(.system(size: 40)).lineLimit(nil)
    }
}

BTW in addNavigationLink you may notice there is no the return keyword. It’s a new feature in Swift 5.1. You can now omit the return keyword in a single expression function.

Conclusion

I have covered just a small piece of questions about SwiftUI. Hope this article will help a beginner to understand a new framework. And one last question: should I learn UIKit? Of course, yes. UIKit is still a primary framework for iOS and it continues to improve. Furthermore, many SwiftUI classes are just wrappers around UIKit. And there are no libraries, pods for SwiftUI yet. All has to be done by yourself.

In this SwiftUI guide, I have covered just a small piece of questions about the topic. I hope this article will help beginners understand the new framework and the SwiftUI library. And one last question: should I learn UIKit? Of course, yes. UIKit is still a primary framework for iOS, and it continues to improve. Furthermore, many SwiftUI classes are just wrappers around UIKit. And there are no libraries or pods for SwiftUI yet. It all has to be done by yourself.

Project source code can be downloaded here.

After approval

What to do after successful approval? Add Apphud SDK to your app and find out how much you earn in real-time with many more features.