Chapters

Hide chapters

SwiftUI by Tutorials

Fifth Edition · iOS 16, macOS 13 · Swift 5.8 · Xcode 14.2

Before You Begin

Section 0: 4 chapters
Show chapters Hide chapters

2. Getting Started
Written by Audrey Tam

Heads up... You’re accessing parts of this content for free, with some sections shown as xfqejlwyf text.

Heads up... You’re accessing parts of this content for free, with some sections shown as pmteftbik text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

SwiftUI is some of the most exciting news since Apple first announced Swift in 2014. It’s an enormous step towards Apple’s goal of getting everyone coding; it simplifies the basics so that you can spend more time on custom features that delight your users.

If you’re reading this book, you’re just as excited as I am about developing apps with this new framework. This chapter will get you comfortable with the basics of creating a SwiftUI app and live-previewing it in Xcode.

You’ll create a small color-matching game, inspired by our famous BullsEye app from our book UIKit Apprentice. The goal of the app is to try and match a randomly generated color by selecting colors from the RGB color space:

Playing the game
Playing the game

In this chapter, you will:

  • Learn how to use the Xcode canvas to create your UI side-by-side with its code, and see how they stay in sync. A change to one side always updates the other side.
  • Create a reusable view for the sliders seen in the image.
  • Learn about @State properties and use them to update your UI whenever a state value changes.
  • Present an alert to show the user’s score.

Time to get started!

Getting Started

Open the UIKit/RGBullsEye starter project from the chapter materials, and build and run:

UIKit RGBullsEye starter app
UIKit RGBullsEye starter app

This app displays a target color with randomly generated red, green and blue values. The user moves the sliders to make the other view’s color match the target color. You’re about to build a SwiftUI app that does the exact same thing, but more swiftly!

Exploring the SwiftUI Starter Project

Open the SwiftUI/RGBullsEye starter project from the chapter materials.

Heads up... You’re accessing parts of this content for free, with some sections shown as qjmajllyc text.

Heads up... You’re accessing parts of this content for free, with some sections shown as fzmasbquj text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
@main
struct RGBullsEyeApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}
struct ContentView: View {
  var body: some View {
    Text("Hello, world!")
      .padding()
  }
}

Previewing Your ContentView

In the ContentView file, below the ContentView structure, ContentView_Previews contains a view that contains an instance of ContentView:

struct ContentView_Previews : PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}
Preview Resume button
Briries Faceza vewhum

Heads up... You’re accessing parts of this content for free, with some sections shown as vzsorpzoj text.

Heads up... You’re accessing parts of this content for free, with some sections shown as wzzazknuk text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
Hello World preview
Morhu Yokjb hcibiod

Editor options
Ehijun okbeofg

Creating Your UI

Your SwiftUI app doesn’t have a storyboard or a view controller. The ContentView file takes over their jobs. You can use any combination of code and drag-from-object-library to create your UI, and you can perform storyboard-like actions directly in your code! Best of all, everything stays in sync all the time!

Some SwiftUI Vocabulary

Before you dive into creating your views, you need to know some vocabulary.

Heads up... You’re accessing parts of this content for free, with some sections shown as cmfafdsev text.

Heads up... You’re accessing parts of this content for free, with some sections shown as djkeptgeh text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Creating the Target Color View

In RGBullsEye, the target color view, which is the color your user is trying to match, is a Color view above a Text view. But body is a computed property that returns a single View, so you’ll need to embed them in a container view. In this scenario, you’ll use a VStack (vertical stack).

Embed Text view in VStack
Ihvol Kuwp veed en LPdehx

Edit text in the Attributes inspector.
Edur burl od tse Uhphejiluf ilqsuwwov.

Heads up... You’re accessing parts of this content for free, with some sections shown as gwcynvgix text.

Heads up... You’re accessing parts of this content for free, with some sections shown as smfoktxyl text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
Insert Color into VStack
Azyokt Vuder uxru FCwibb

Color view in VStack
Guxum keuw ox PRkenm

Creating the Guess Color View

The guess color view looks a lot like the target color view, but with different text. It goes below the target color view, so you’ll just add it to the VStack.

Heads up... You’re accessing parts of this content for free, with some sections shown as lkjolxqud text.

Heads up... You’re accessing parts of this content for free, with some sections shown as mjgarzfox text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
VStack {
  Color(red: 0.5, green: 0.5, blue: 0.5)
  Text("R: ??? G: ??? B: ???")
    .padding()
  Color(red: 0.5, green: 0.5, blue: 0.5)
  Text("R: 204 G: 76 B: 178")
    .padding()
}

Creating the Button and Slider

The color sliders and Hit me! button go below the color blocks so again, you’ll just add them to your VStack.

Add Button to code
Ald Fuqdiz ju jega

Insert Slider in Vertical Stack
Ivdogv Mmopes op Jebhafaq Lhobs

Heads up... You’re accessing parts of this content for free, with some sections shown as gjlujfcov text.

Heads up... You’re accessing parts of this content for free, with some sections shown as htjakmdyc text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
Button & Slider in VStack
Gadxar & Wlezeg uy CHvons

Updating the UI

If the UI should update when a SwiftUI view property’s value changes, you designate it as a @State property. In SwiftUI, when a @State property’s value changes, the view invalidates its appearance and recomputes the body. To see this in action, you’ll ensure the properties that affect the guess color are @State properties.

Using @State Properties

Add these properties at the top of struct ContentView, above the body property:

@State var game = Game()
@State var guess: RGB
var target = RGB.random()
ContentView(guess: RGB(red: 0.8, green: 0.3, blue: 0.7))

Heads up... You’re accessing parts of this content for free, with some sections shown as ljzejzwas text.

Heads up... You’re accessing parts of this content for free, with some sections shown as qvsuzftab text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
ContentView(guess: RGB())

Updating the Color Views

Back in ContentView, edit the Color view above Text("R: ??? G: ??? B: ???") to use the target property of the game object:

Color(rgbStruct: game.target)
Random target color
Meccex zedpak becut

Color(rgbStruct: guess)
Guess color set in preview
Maobm cejib son ot fsanaad

Heads up... You’re accessing parts of this content for free, with some sections shown as nrviwccaq text.

Heads up... You’re accessing parts of this content for free, with some sections shown as wdwambjal text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Making Reusable Views

Because the sliders are basically identical, you’ll define one slider view, then reuse it for the other two sliders. This is exactly as Apple recommends.

Making the Red Slider

First, pretend you’re not thinking about reuse, and just create the red slider. You should tell your users its minimum and maximum values with a Text view at each end of the Slider. To achieve this horizontal layout, you’ll need an HStack.

Slider from 0 to 255
Zzosuq ycon 5 te 903

Add horizontal padding
Usw geyujiryiz longurf

Slider(value: $guess.red)
  .accentColor(.red)

Heads up... You’re accessing parts of this content for free, with some sections shown as gzbaxtqap text.

Heads up... You’re accessing parts of this content for free, with some sections shown as xnromlzir text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
Red slider value 0.3
Voj mroyaz lopea 9.1

Bindings

So back to that $. It’s actually pretty cool and ultra-powerful for such a little symbol. By itself, guess.red is just the value. It’s read-only. But $guess.red is a read-write binding. You need it here to update the guess color while the user is changing the slider’s value.

Text(
  "R: \(Int(guess.red * 255.0))"
    + "  G: \(Int(guess.green * 255.0))"
    + "  B: \(Int(guess.blue * 255.0))")
Text(guess.intString())
R value 76 = 255 * 0.3
Z rilui 39 = 496 * 1.9

Heads up... You’re accessing parts of this content for free, with some sections shown as gpwutrbav text.

Heads up... You’re accessing parts of this content for free, with some sections shown as wmfegbrah text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Extracting Subviews

Next, the purpose of this section is to create a reusable view from the red slider HStack. To be reusable, the view needs some parameters. If you were to Copy-Paste-Edit this HStack to create the green slider, you’d change $guess.red to $guess.green and .red to .green. So these are your parameters.

Extract HStack to subview
Isszaxq QFgifn so jabceij

@Binding var value: Double
var trackColor: Color
Slider(value: $value)
  .accentColor(trackColor)

Heads up... You’re accessing parts of this content for free, with some sections shown as gppekztuc text.

Heads up... You’re accessing parts of this content for free, with some sections shown as dfhonktob text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
ColorSlider(value: $guess.red, trackColor: .red)
ColorSlider(value: $guess.green, trackColor: .green)
ColorSlider(value: $guess.blue, trackColor: .blue)
Three sliders
Tklao fhojann

ContentView(guess: RGB())

Live Preview

You don’t have to fire up Simulator to play the game: In the Preview toolbar, click the Live Preview button:

Live preview button
Reya mkiqaac qebyon

Playing the game
Mlepaqz ype jixi

Heads up... You’re accessing parts of this content for free, with some sections shown as ldkosqmyz text.

Heads up... You’re accessing parts of this content for free, with some sections shown as jcnutjboq text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Presenting an Alert

After using the sliders to get a good color match, your user taps the Hit Me! button, just like in the original UIKit game. And just like in the original, an Alert should appear, displaying the score.

Button("Hit Me!") {
  // action
}
@State var showScore = false
showScore = true
game.check(guess: guess)

Heads up... You’re accessing parts of this content for free, with some sections shown as nkgehwnex text.

Heads up... You’re accessing parts of this content for free, with some sections shown as pptafsxex text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
.alert(isPresented: $showScore) {
  Alert(
    title: Text("Your Score"),
    message: Text(String(game.scoreRound)),
    dismissButton: .default(Text("OK")) {
      game.startNewRound()
      guess = RGB()
    })
}

Displaying the Target Values

There’s one last bit of functionality you need to implement. When showAlert is true, the target color label should display the correct color values, so your user can compare these with their slider values.

Text("R: ??? G: ??? B: ???")
Embed Text view in an if-else
Ulkik Pipl haal ej am oj-ehti

if !showScore {
  Text("R: ??? G: ??? B: ???")
    .padding()
} else {
  Text(game.target.intString())
    .padding()
}

Heads up... You’re accessing parts of this content for free, with some sections shown as nrlardcow text.

Heads up... You’re accessing parts of this content for free, with some sections shown as ljzaqhniw text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now
Score!
Lkiro!

Making it Prettier

Your app has all its functionality, so now’s a good time to start improving how it looks. Instead of colored rectangles, how about circles?

Circle()
  .fill(Color(rgbStruct: game.target))
Circle()
  .fill(Color(rgbStruct: guess))
Color circles
Wuzov homvqob

Challenge

Challenge: Create a ColorCircle Subview

Create a ColorCircle subview so that you can replace the Circle().fill... lines with these:

ColorCircle(rgb: game.target)
ColorCircle(rgb: guess)

Heads up... You’re accessing parts of this content for free, with some sections shown as pzzavpkuk text.

Heads up... You’re accessing parts of this content for free, with some sections shown as krwajqbej text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Key Points

  • The Xcode canvas lets you create your UI side-by-side with its code, and they stay in sync: A change to one side always updates the other side.
  • You can create your UI in code or the canvas or using any combination of the tools.
  • You organize your view objects with horizontal and vertical stacks, just like using stack views in storyboards.
  • Preview lets you see how your app looks and behaves with different initial data, and Live Preview lets you interact with your app without firing up Simulator.
  • You should aim to create reusable views. Xcode’s Extract Subview tool makes this easy.
  • SwiftUI updates your UI whenever a @State property’s value changes. You pass a reference to a subview as a Binding, allowing read-write access to the @State property.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2025 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as kfdexxjis text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now