SwiftUI Basics
SwiftUI Basics
com
No part of this production may be reproduced, stored in a retrieval system or transmitted in any form or by any means, electronic, mechanical,
photocopying, recording or otherwise, by you to any third party without the written permission of the operators of “www.blckbirds.com”.
Unauthorized reproduction or distribution of this content is punishable by law. Copyright infringements - including those that do not have a monetary
background - will not be tolerated and in any event the owner will seek damages and prosecution.
Important:
❗ We strongly recommend you to not only read this book but to try
everything yourself immediately. This helps strengthen your skills!
SwiftUI is still pretty new. This means that there can occur some bugs and
❗
it’s likely that Apple will update its framework more often than usual,
which can lead to some problems within the code used in this book.
However, whenever this is the case, this book will get updated as fast as
possible. You can always contact us if you have some problems,
suggestions or wishes!
Table of content
Chapter 1: Preface - What is SwiftUI?
Chapter 1
First of all, we wanted to thank you for downloading this eBook
Preface
and taking the time to read it!
After that, you should already be able to write your own little
application with SwiftUI!
What is SwiftUI 🤷
With SwiftUI you use code to describe how your app And that’s one great thing about SwiftUI! Everything you
should look like! With the new syntax, you just describe write down gets instantly reflected in your live preview. No
how your interface should look like and how it should time-intense run & compile workflow anymore! Everything
behave. Look at the image above. You see that every part you describe within your code get’s instantly displayed to
of the UI, the map, the circled image and the texts, is you!
described by code.
And that’s not all: You can even manipulate the UI inside nutshell: With imperative programming, we write how we
the live preview itself. For example, you can drop and drag react when the state of our app changes and accordingly
another text objects or manipulate the color of a certain execute code. With declarative programming instead, we
object, as we’ll see later. Every change in the live preview describe the interface of our app for all possible states in
affects your code and vice versa! advance. Here's a great article if you want to dive deeper.
This new workflow saves us a lot of time and simplifies app- Technical requirements for running
building tremendously. And because code and UI are
SwiftUI 🏃
always linked and dependent, you don’t have to worry
about broken outlets and actions anymore.
SwiftUI got shipped with Xcode 11, which you can
How does SwiftUI differ from download for free from the Mac App Store. Note that
working with UIKit + storyboards❓ SwiftUI is only compatible with running at least MacOS
Catalina.
If you never developed iOS apps before, feel free to skip Personal requirements 🤓
this paragraph. However, if you already worked with UIKit
and storyboards you’re maybe wondering how the Although we try to explain everything as easy possible and
programming approach used in SwiftUI differs from always step-by-step, we recommend you to be at least
working with UIKit and storyboards. As you saw on the last familiar with the basics of theSwift programming language.
page, the syntax used in SwiftUI really differs from what you However, you don't have to be a professional programmer
used to know in iOS programming. This is because SwiftUI to understand and apply what you will learn in this book. If
follows a declarative approach instead of an imperative you are a beginner, we recommend you to read our free
one. Imperative programming was the common Swift 5 Programming for Beginners eBook!
programming approach. But what's the difference? In a
By the way, you don’t necessarily need to be familiar with
building iOS apps using UIKit, although it could give you a
slight advantage. But don’t worry when you don’t know
what we’re talking about right now!
That’s all, let’s get our hands dirty and start building some
awesome SwiftUI apps!
Before we are getting started with creating our first SwiftUI
projects, let’s take a quick tour through Xcode.
Chapter 2 If you are already familiar with Xcode and worked with it before,
Introduction
feel free to skip this chapter. However, if you are completely new to
iOS development, make sure you read this chapter carefully since
it’s crucial for learning iOS app development!
In Xcode you set up the user interface of your app, organize and
write the code that makes your app run. Xcode also offers you the
possibility to run and test your app in a virtual simulator on your
Mac (and of course on a real iOS device).
Creating an Xcode Project and Fortunately, the interface is designed pretty straight
forward. The interface you see now basically consists of 5
choosing the User Interface
sections:
To get in touch with the IDE, just open Xcode and click on
"Create a new Xcode Project" (by the way: "Playgrounds",
the option below creating a new project, is an option to
test new concepts and ideas quickly and easily, here you
can read more about it). Next, click on "Single View App“,
then on "Next" and give your app any name you want.
The Toolbar 🛠
Here you write the code and compose the interface of your
Similar to the Editor Area, the appearance of the Utility
app. The appearance of the Editor Area depends on which
Area depends on what file type you have selected. Here
file type is opened.
you can access, for instance, metadata, references, etc. of
files or/and their components. Especially this area often
confuses beginners, because the use of this area depends
on the particular situation. But the more you work with
Xcode the more you get a feeling for it. You'll see, it's much
easier than it looks at first sight.
The Debug Area 👷 Conclusion 🎊
When you run your app, you will find all relevant Congratulations! You now know the basics of Xcode.
information about errors etc. that Xcode provides you. This
Don’t feel insecure if you are a little overwhelmed. You'll
area becomes very important when it comes to finding and
see, the more you deal with it, the more comprehensible
fixing errors and bugs of your app.
everything becomes.
Often the output is very long, so you can use the filter to
In case you have trouble with working something out, do
easily find certain output.
not hesitate to write a message.
Creating a SwiftUI Project 🚀
Chapter 3
Let’s dive right into it and start creating our first SwiftUI project. For
Building doing this, open Xcode 11 and click on “Create a new Xcode
project”. Select “Single View App” as the application type and click
on Next. Now you can give the project any name you want, in this
• Getting to know Apple’s in-house Now you see a split-screen. The left side is the SwiftUI code we use
IDE
to build our app’s interface. This code consists of two blocks:
• How Xcode is structured
• The View struct: This struct is the place where we write our code
• How to create app projects for building up our app. You see that the struct itself contains a
body property. Inside this body, we create all the components
we need for our UI, arrange and modify them.
• The Previews struct: This struct then renders the View with its
body and displays it in the Preview Simulator on the right screen.
Every time you make some changes within your View struct, You can add a modifier to an object like this:
the Previews struct notices and displays the updated the
Text("My first SwiftUI app")
View inside the live preview! .font(.largeTitle)
At default, Xcode inserts a Text object inside the View’s In this example, we choose the .largeTitle option. To see all
body, reading “Hello World”. Let’s modify this object by of the other options, you can delete the code inside the
changing its string to “I love SwiftUI!”. braces and just write an “.”, which shows you all available
text styles. Feel free to try them out!
Text(“I love SwiftUI!”)
You see that this change gets instantly reflected in the live
preview. Awesome!
You see that the Text turns blue and also the code gets
updated! To choose another color, you can edit the code.
The Preview struct renders the View struct and sends it to the live preview To see all available system colors, you can again delete
the code inside the braces and just write an “.”. But for
now, we’re fine with the blue color.
Let’s see how Stacks work! Let’s say we want to place VStack {
another Text below our current one. Because they should //...
}
be aligned vertically, we have to use a VStack for this. .frame(width: 300, height: 100)
For embedding our Text view into a VStack, you can simply By using the .frame modifier, we are positioning the
CMD-click on it (on the code side) and click on “Embed in VStack’s elements within an invisible frame.
VStack”.
At the moment the texts are centered. Instead, we want to
align them on the left side. To do this, we can add braces
behind the VStack and use the alignment argument to
change the alignment mode of the views wrapped into the
VStack. For aligning the objects on the left side we use
the .leading option. We also want to increase the vertical
spacing of the Texts by using the spacing parameter.
VStack(spacing: 50) {
VStack(alignment: .leading, spacing: 10) { VStack(alignment: .leading, spacing: 10) {
//... //...
} }
.frame(width: 300, height: 100) .frame(width: 300, height: 100)
Button(action: {}) {
Let’s add another object below our Text views: a button! Text("Button")
}
But this time, we try doing it by using the live preview. Click
}
on the “+”-symbol, open the view library (first icon) and
Inside the curly braces we define the UI of the button. We
search for Button. Now drag and drop it below our second
want our button to read “Show me the logo!”
Text view (make sure you insert it below the VStack and not
into it. Button(action: {}) {
Text("Show me the logo!")
Great, you see that SwiftUI automatically created another }
We want the Button’s text view to have a blue background,
VStack and placed the one we created earlier into it. Below
a white font and rounded corners. To do this, we use the
this wrapped VStack, we saw our new button view! Again
following modifiers:
we want to increase the vertical spacing:
Text("Show me the logo!")
.background(Color.blue)
.cornerRadius(10)
.foregroundColor(.white)
We want to enlarge the Button’s background. To add When applying another modifier, in this case,
spacing around individual views, we use to .padding the .cornerRadius modifier, SwiftUI created another new
modifier. view by grabbing the created view (the view created from
the .background modifier) and added a background to it.
Text("Show me the logo!")
.background(Color.blue) Applying the modifier (.foregroundColor) grabbed the last
.cornerRadius(10)
created view and initializes a new view out of it with
.foregroundColor(.white)
.padding() changing the text font color. And so on!
Let’s take a close look at our Text view. We initialize the Text
view containing the String “Show me the logo!”. We added
a background to it by applying the .background modifier.
What happened next is that the modifier grabbed the Text
view and created a new view out of it with changing the
font type. So the key takeaway to remember is that
applying a modifier to a view does not really modify the
view itself but rather create a new, customized view!
And this is why applying the .padding modifier as the last This is how your preview canvas should look so far:
one does not work as expected. The .padding modifier of
the Text view grabs the last created view, a Text view
without blue background and rounded corners. When we
use the .padding modifier as the first modifier, the
background modifier uses a Text view that already has
some spacing around it and fills it with color! So let’s
change our button’s text view:
Chapter 4
When tapping on the “Show me the logo!” button, we want to
Data flow in show the SwiftUI logo above our Text views. By doing this we are
learning the basic data flow concepts used in SwiftUI to react to
external events.
SwiftUI
• Using image views in SwiftUI
Before we move on, we first need to import the SwiftUI icon into
our project. To do this, open the Assets.xcassets folder and drag
and drop the image file into it. Make sure the image is named
correctly.
Using images in SwiftUI 🖼 Next, we need to resize the image so that it fits into the
frame without distorting the dimensions of the original
image. To change the scaling of an image, we must use the
The SwiftUI icon should be placed right above our two Text .resizable modifier first. The .resizable modifier must be
views. Therefore, we insert an Image view above our inner applied to enable changing the scaling of an image. It is
VStack. important that the .resizable modifier is the first modifier of
Image("logo")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 170, height: 170)
.clipped()
VStack(spacing: 50) {
Getting familiar with @State if showIcon {
properties 🔎 Image("logo")
//...
}
//...
}
We want the SwiftUI icon to be only displayed after the
user tapped on the “Show me the logo button!”. But how We could now change the showIcon value by using the
can we achieve this functionality? To keep track of whether Button’s action parameter. But note the following:
the corresponding image view should be shown or not we Whenever the user taps on the button, the showIcon
declare a showIcon variable above our ContentView’s property would get toggled though. But this wouldn’t tell
body. At default, the logo shouldn’t be shown, so we the ContentView update its body. This means that the
assign false to it. showIcon would get true when the user taps the button,
but the ContentView wouldn’t notice and therefore
struct ContentView: View {
wouldn’t initialise the Image view by executing the code
var showIcon = false
inside the if-statement.
var body: some View {
//... So how can we get the view to rebuild itself when the user
}
} taps the button?
We only want to show the SwiftUI icon, when the showIcon For this purpose, Apple provided @State properties within
property is true. Thus, we conditionally initialise the Image SwiftUI. These @-keywords (yes, there are more of them),
view by using an if-statement inside the outer VStack. Note are called property wrappers. Property wrappers in SwiftUI
that if-else-statements can only be used inside Stacks! equip variables with a specific logic depending on the type
of the property wrapper. This logic is the core of the data
flow concept in SwiftUI, so understanding property
wrappers is really crucial.
State is probably the most frequently used property Let’s explain it by taking a look at our app: Every time the
wrapper in SwiftUI. We’ll get to know most of the other user taps the button, the showIcon property gets toggled.
ones throughout this book, but we start with this, basic one. Because this property is a State, its related view, the
ContentView’s body, notices and rebuilds itself with
You can easily declare a State by putting the @State
checking the showIcon property and based on its value
keyword in front of a variable. Let’s do this with our
executing the if-statement with eventually showing/hiding
showIcon property.
the image view!
@State var showIcon = false
Try it out in the live preview! To use interactive elements
Hint: State properties must always be related to a view in like buttons in the canvas, you need to tap on the “Play”
SwiftUI. So make sure you always declare them inside a button next to it for starting a live preview. When you now
View struct (but not inside the View’s body)! tap the button, the showIcon State gets true which causes
the whole view to rebuild itself with eventually showing us
We can now toggle the showIcon State when the user taps
the SwiftUI. If we tap on the button again, the showIcon
the button. Toggling means that the property gets true
State gets false again, which causes the ContentView to
when it’s false and vice versa.
hide the Image view again!
Button(action: {self.showIcon.toggle()}) {
Text("Show me the logo!") As said, this topic is very important for mastering SwiftUI,
//...
} so make sure you really understood it!
Again, what does a State property do? Well, you can read
out and manipulate data just as you do with regular
variables in Swift. But the key difference is that every time
the data of a State changes, the related view gets rebuilt.
Pushing views with Spacers ↔ VStack(spacing: 50) {
if showIcon {
Image("logo")
//...
When showing and hiding the SwiftUI icon, the position of }
Spacer()
the remaining views changes. This is because the outer VStack(alignment: .leading, spacing: 10) {
VStack place all of its elements in the center of the screen. //...
}
Therefore, when the image view is not being shown the //...
}
remaining views are getting pushed to the center. But how
could we tell SwiftUI that the text views and buttons should However we want our elements to be a bit more far away
always be pushed to the bottom, regardless of whether the from the upper and lower edge. Therefore we add a top
image view is displayed. padding to the image view …
LogoButton(showIcon: $showIcon)
Chapter 5
In this chapter, we are going to create a contacts app. By doing this
Using Lists and we’ll learn how to present data in rows by using SwiftUI Lists. We’ll
also learn how to let the user navigate between multiple views, as
Let's start with creating a new project and importing the A List is like a Table View you perhaps know from UIKit and
images we will need later on. Open Xcode 11 and create a contains multiple rows of data in a single column. Let’s
new Xcode project. Then select Single View App and click embed the default “Hello World” text into a List to see how
on Next. Give the project a suitable name and make sure it looks like:
"Use SwiftUI" is selected. Then click on Create.
List {
Text("Hello, World!")
Our Xcode project shows up with presenting the default }
ContentView.swift file.
Each row in our List should contain the contact’s photo, its Let’s choose the .leading alignment mode for our VStack:
name and its phone number. Let’s start composing the
VStack(alignment: .leading) {
row’s UI by embedding a Text view into a VStack, replacing //...
}
its String with a sample contact name and placing another
text view reading a sample phone number below it. On the left side of the contact’s name and number, we
want to show its photo. To do this, we need to embed the
List {
VStack { VStack into a HStack first.
Text("Christine Clapper")
Text("+1(141)-5115553") List {
} HStack {
} VStack(alignment: .leading) {
//...
Let’s highlight the contact name by applying the .font }
}
modifier with the custom option to the corresponding Text. }
Text("Christine Clapper") Next, we can insert a suitable image view above the VStack
.font(.system(size: 21, weight: .medium,
design: .default)) for creating a sample contact photo.
HStack {
Image("christineClapper") Defining the data model 🛠
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 60, height: 60)
.clipped() Let's create a new Swift file by clicking File - New - File and
.cornerRadius(50) then selecting Swift File. Let's call this file Contact. Make
VStack(alignment: .leading) {
//... sure the Foundation and SwiftUI Kit is imported. For
}
} handling our contacts we create a class called Contact
inside this file.
For keeping our code clean, let’s outsource our HStack and
by CMD-Clicking on it, choosing “Extract subview” and import Foundation
import SwiftUI
calling it ContactRow.
struct Contact {
Before we can make our List dynamic, we need to define a
}
data model for representing the data for each contact
We want to know the following information of each
inside the list.
contact: Its name, phone number, email, its address and
the name of the corresponding image inside our assets
folder. Therefore we declare corresponding attributes
inside our class.
struct Contact {
let imageName: String
let name: String
let phone: String
let email: String
let address: String
}
Because we will use our Contact data model to wrap Making our List dynamic 🔄
multiple instances of it into a List, we need it to conform to
the Identifiable protocol. This is required to pass custom
class instances into Lists in SwiftUI (it's also the reason why Back to our ContentView: We are now ready to pass our
we imported the SwiftUI framework into our Contact.swift contacts data set to our List like this:
file). The Identifiable protocol has only one mandatory
List(contacts) { contact in
requirement: It needs the class to contain an attribute to ContactRow()
}
identify every instance by a unique id. Therefore we simply
declare an id attribute and assign an UUID instance to it By doing this, our List cycles through our contacts array
when initialising a Contact. Using UUID instances is and creates one ContanctRow for every Contact instance
automatically creating unique id’s for us. inside it. Your preview canvas should now look like this:
Feel free to copy and paste the array from here into. Of
course, you can edit this array to your wishes!
let contacts = [
//...
]
Of course, not every row should display the same sample If you take a look at your preview canvas you see that the
contact data. Thus, we add a contact variable to our List successfully cycles through all elements in the contacts
ContactRow struct … array and creates one ContactRow for each by passing that
particular Contact instance to the ContactRow view.
struct ContactRow: View {
List(contacts) { contact in
ContactRow(contact: contact)
}
HStack {
Image(contact.imageName)
//...
VStack(alignment: .leading) {
Text(contact.name)
//...
Text(contact.phone)
}
}
Composing the DetailView 🖌 Now we’re ready to setup the UI of our DetailView. We start
with replacing the default “Hello World” Text with an Image
view to display the contact’s photo.
When the user taps on a particular contact row, we want to
present a new view with detailed information about this var body: some View {
Image(contact.imageName)
contact. .resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 150, height: 150)
To do this, we create a new SwiftUI view and call it .clipped()
DetailView. As we did in our ContactRow view, we declare a .cornerRadius(150)
.shadow(radius: 3)
contact property which will be initialised in our }
ContentView later on.
Next, we wrap the image view into a VStack and place a
struct DetailView: View { Text view below it for displaying the contact’s name.
canvas. For this purpose, we just use the first element of presenting the contact’s phone number, email and its
Form {
HStack {
//...
}
HStack {
//...
}
HStack {
//...
}
}
Looks much better now! But what did the form actually do The preview of the DetailedView should look like this:
for us? A form groups views in a manner you know for
example from the system’s settings app. The cool thing
about forms is that SwiftUI automatically adapts the layout
and appearance of the views wrapped inside the form for
us! Let’s say we want to add two views two our row: one
button for texting the contact and one for calling him. But
we want those two to be separated from the other views
inside the form. To do this, we wrap those into Sections like
this:
Form {
Section {
//...
}
Section {
Button(action: {
print("Send message")
}) {
Text("Send message")
}
Button(action: {
print("Call")
}) { Now it’s time to connect it to the ContentView in a manner
Text("Call")
} that when the user taps on a particular contact row it opens
} the DetailView with showing the detailed information
}
about the selected contact.
NavigationView {
List(contacts) { contact in
ContactRow(contact: contact)
}
}
List(contacts) { contact in
ContactRow(contact: contact) To navigate to the DetailView when tapping on a row, we
}
.navigationBarTitle(“Contacts") wrap each ContactRow instance that gets created inside
the List into a NavigationLink with choosing that instance as
SwiftUI appends a large navigation bar by default. If you
the link’s destination.
want to use the smaller one, you can use the displayMode
argument with choosing the .inline option. But for now, we List(contacts) { contact in
NavigationLink(destination: DetailView(contact:
are fine with the default navigation bar style. contact)) {
ContactRow(contact: contact)
}
}
The NavigationLink tells SwiftUI to push the DetailView
equipped with the passed Contact instance on top of the
navigation hierarchy. We can see how this looks like by
running our app in the live preview.
Chapter 6
In this chapter, we’re going to create a timer app. By doing this
Timer app we’ll practice creating more complex and dynamics UI’s and how
to use @ObservableObjects as view models. The timer app will
look like this:
• Understanding
@ObservableObjects and how to
use them as your view models
Image(systemName: "play.circle.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 180, height: 180)
.foregroundColor(.orange)
Well, that's super easy! Just use the free Apple SF symbols
app. This macOS app provides you with a set of over
1,500 symbols you can use in your app.
}
.labelsHidden()
}
● These classes can contain data, for example, a string is running, paused or in its initial state. Therefore declare a
You maybe noticed that the code inside the reset function
Setting the timer length by using
is the same that we execute in our start function when the
user defaults
secondsLeft hit zero. To refactor our code, we simply call
the reset function from our start function.
We’re already able to start, pause and stop our timer.
if self.secondsLeft == 0 {
self.reset() However, we can’t set the timer length by using the picker
} yet.
In our ContentView, we want to call the pause function
To store the chosen time persistently, we’re going the use
when the user taps on the pause icon. To achieve this we
the UserDefaults. UserDefaults can be used to store small
update our .onTapGesture modifier as follows:
chunks of data, such as the timer length setting.
For this purpose, we add a function called setTimerLength The secondsLeft property is still 60 when the
to our TimerManager. The function should intake the TimerManager gets initialised. Instead, we want to retrieve
desired time length as an Integer: the stored value from the specified “timerLength” key:
We can access the UserDefaults like this: We want to retrieve to timer length from the UserDefaults
inside our reset function as well:
func setTimerLength(minutes: Int) {
let defaults = UserDefaults.standard func reset() {
} //...
self.secondsLeft =
UserDefaults.standard.integer(forKey:
The structure of the UserDefaults is very similar to a "timerLength")
dictionary. We can store pieces of data under a certain key //...
}
and retrieve it again by using the specified key. We call the
setTimerLength function we want to store the given
We want to call to set the timer length whenever the user is
minutes under a key we call “timerLength”. Finally, we set
starting the timer. Therefore, we call the setTimerLength
the published secondsLeft property to this value when the
function from the ContentView’s Play/Pause Image view but
function gets called.
only when the timerMode is .initial. For the minutes
func setTimerLength(minutes: Int) { parameter, we use the selected minutes from our picker.
let defaults = UserDefaults.standard
defaults.set(minutes, forKey: "timerLength") .onTapGesture(perform: {
secondsLeft = minutes if self.timerManager.timerMode == .initial {
}
self.timerManager.setTimerLength(minutes:
self.availableMinutes[self.selectedPickerIndex]*60)
}
//...
})
Converting seconds to a time stamp
If we run our app now, we’re able to set the desired timer
length by using the picker. However, when selecting for
instance 12 minutes our app shows us the plain seconds
left. Instead, we want to display a time stamp for better
readability. To achieve this, just add this function to your
Helper.swift file.
return "\(minuteStamp):\(secondStamp)"
}
Text(secondsToMinutesAndSeconds(seconds:
timerManager.secondsLeft))
//...
If we start our timer now, we see that the secondsLeft get
converted into a timestamp string!
CONCLUSION
That's it! We are finished with creating our timer app. We
learned how to create more complex and dynamic
interfaces with SwiftUI. We also explored
@ObservableObjects and how we can use them as our
view’s model.
You can find the whole source code of the app here.
Congratulations, you just finished this book!
Chapter 7 That’s it for now, thank you for reading this book! You just got
Where to go
familiar with the basics of SwiftUI. You learned how to compose
interfaces step-by-step by using SwiftUI views. You learned how
data flow works in SwiftUI and know what @State, @Bindings and
from here @ObservableObjects are. You also explored how to present data
by using Lists and how to navigate between screens in a view
hierarchy.
If you want to learn more about SwiftUI, visit BLCKBIRDS for many
free SwiftUI tutorials!
Want to dive deeper into SwiftUI? Then check out our Mastering
SwiftUI book. In over 14 chapters, we’re creating multiple useful
apps (e.g. a To-do app and a chat messenger) and learn a lot more
about SwiftUI. With lifetime-updates and full code support, we
teach you how to develop your own iOS apps by using SwiftUI!
For inspiration, more content about iOS development and tips for
iOS developers, follow us on Instagram and Twitter.