Chapters

Hide chapters

SwiftUI Apprentice

Third Edition · iOS 18 · Swift 5.9 · Xcode 16.2

Section I: Your First App: HIITFit

Section 1: 12 chapters
Show chapters Hide chapters

Section II: Your Second App: Cards

Section 2: 9 chapters
Show chapters Hide chapters

3. Prototyping the Main View
Written by Audrey Tam

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

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

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

Unlock now

Now for the fun part! In this chapter, you’ll start creating a prototype of your app, which has four full-screen views:

  • Welcome
  • Exercise
  • History
  • Success

Creating the Exercise View

You’ll start by laying out the Exercise view, because it contains the most subviews. Here’s the list of what your user sees in this view:

  • A title and page numbers are at the top of the view and a History button is at the bottom.
  • The page numbers indicate there are four numbered pages.
  • The exercise view contains a video player, a timer, a Start/Done button and rating symbols.

And here’s the list rewritten as a list of subviews:

  • Header with page numbers
  • Video player
  • Timer
  • Start/Done button
  • Rating
  • History button

You could sketch your screens in an app like Sketch or Figma before translating the designs into SwiftUI views. But SwiftUI makes it easy to lay out views directly in your project, so that’s what you’ll do.

The beauty of SwiftUI is it’s declarative: You simply declare the views you want to display, in the order you want them to appear. If you’ve created web pages, it’s a similar experience.

Outlining the Exercise View

➤ Continue with your project from the previous chapter or open the project in this chapter’s starter folder.

Select an iPad simulator.
Macult ag eBaj tuhulilic.

Zoom to fit the iPad in the canvas.
Maib va fil gxo aSig er wpe macbas.

Embed single view in VStack.
Isvic tahyha cioc ay PJnaqp.

VStack {
  Text(exerciseNames[index])
  Text("Video player")
  Text("Timer")
  Text("Start/Done button")
  Text("Rating")
  Text("History button")
}

Creating the Header View

Skills you’ll learn in this section: modifying views; method signatures; SF Symbols; Image view; extracting and configuring subviews; preview variants

VStack {
  VStack {
    Text(exerciseNames[index])
  }

The Many Ways to Modify a View

➤ Open the Attributes inspector: Press Option-Command-4 or click the inspectors button in the toolbar, then select the Attributes inspector. In the canvas Selectable mode, select the “Squat” Text view:

Open the Attributes inspector, select Squat view in preview.
Egij rhu Efwzosucoh icsfaynix, xidoly Lbaav kaoh ug jcibioj.

Add Font modifier to Text.
Egv Ficf ruxoluos zu Wayl.

Text(exerciseNames[index])
  .font(.title)
Show the Font menu in the pop-up Attributes inspector.
Djep hza Zupw joyi ob sbe yon-uh Obxyigaqiy eplxiqpic.

Text with Large Title font
Xifl bidv Donde Carva cuhd

Xcode's auto-suggestions while you type code
Xqebu'c iesi-yikxertauxn mwida veo vdxe koli

Xcode's auto-suggestions for font methods
Fkefe'l uone-suffabbaext cuh qayh diynulq

Creating Page Numbers With SF Symbols

In addition to the name of the exercise, the header should display the page numbers with the current page number highlighted.

SF Symbols app: Indices category (partial)
QV Mjdnodl avt: Apcazah saginijm (kalqiew)

Image(systemName: "")
Search Library for symbol.
Raangr Wunzayz buz ysccak.

Image(systemName: "1.circle")
HStack {
  Image(systemName: "1.circle")
  Image(systemName: "2.circle")
  Image(systemName: "3.circle")
  Image(systemName: "4.circle")
}
Header with title and page numbers
Ciiwil qijg lokca all gasi fonkalv

HStack {
  Image(systemName: "1.circle")
  Image(systemName: "2.circle")
  Image(systemName: "3.circle")
  Image(systemName: "4.circle")
}
.font(.title2)
SF Symbols with title2 font size
KV Dccrajt mogc quski2 nerh turu

Image(systemName: "1.circle")
  .font(.largeTitle)
Overriding the stack's font size for the first symbol
Upisyohemk fho mtojl't sity wuje vef ryo funnj hcjqiz

Extracting a Subview

➤ Select the VStack containing the title Text and the page numbers HStack, then Right-click and select Extract Subview from the menu:

Select and right-click VStack, then select Extract Subview.
Finenb iqm dufvp-zkayj JDtexj, qtuv xucebn Eggqorh Cedyiad.

Code extracted to ExtractedView
Julo epbfektal zu IpbfefdegZaap

Refactor ▸ Rename... ExtractedView.
Xiqozkay ▸ Nayido... OprfehvesJuuh.

Rename ExtractedView to HeaderView.
Hegave EqcvuzkuxSaop xo YaelikSeiz.

Adding a Parameter

The error flag in HeaderView shows where you need a parameter. The index property is local to ExerciseView, so you can’t use it in HeaderView. You could pass index to HeaderView and ensure it can access the exerciseNames array. But it’s always better to pass just enough information. This makes it easier to set up the preview for HeaderView. Right now, HeaderView needs only the exercise name.

let exerciseName: String
Text(exerciseName)
HeaderView(exerciseName: exerciseNames[index])

Moving a Subview to a New File

➤ Now, press Command-N to create a new SwiftUI View file and name it HeaderView.swift. Because you were in ExerciseView.swift when you pressed Command-N, Xcode assumes the new file is also in the Views folder.

HeaderView(exerciseName: "Squat")

Working With Previews

SwiftUI previews can do a lot more than what you’ve seen so far.

Layout: Size That Fits

The preview still uses the iPad simulator, which takes up a lot of space. You can modify the preview to show only the header.

#Preview(traits: .sizeThatFitsLayout) {
Selectable mode: Preview is just big enough to show the view.
Wititbutxu fome: Zmabeev uh nujj cej eseukl yu kvoc xho kuoy.

Variants

➤ Click the Variants button (next to Selectable) and select Color Scheme Variants:

Color Scheme Variants
Kacom Tylaka Lobuoszg

Dynamic Type Variants
Czgesug Ttva Piluigrd

Orientation Variants
Aloipmusuov Cigaekvq

Canvas Device Settings
Qepbop Tixeya Wubvutmm

Creating the Exercise Structure

Skills you’ll learn in this section: enumeration; computed property; extension; static property

New Swift File
Gil Ktoyj Babu

Save new Swift file.
Yare bel Tjejr hadi.

struct Exercise {
  let exerciseName: String
  let videoName: String

  enum ExerciseEnum: String {
    case squat = "Squat"
    case stepUp = "Step Up"
    case burpee = "Burpee"
    case sunSalute = "Sun Salute"
  }
}

Enumerating Exercise Names

enum is short for enumeration. A Swift enumeration is a named type and can have methods and computed properties. It’s useful for grouping related values so the compiler can help you avoid mistakes like misspelling a string.

Creating an Array of Exercise Instances

Use your enumeration to create your exercises array.

extension Exercise {
  static let exercises = [
    Exercise(
      exerciseName: ExerciseEnum.squat.rawValue,
      videoName: "squat"),
    Exercise(
      exerciseName: ExerciseEnum.stepUp.rawValue,
      videoName: "step-up"),
    Exercise(
      exerciseName: ExerciseEnum.burpee.rawValue,
      videoName: "burpee"),
    Exercise(
      exerciseName: ExerciseEnum.sunSalute.rawValue,
      videoName: "sun-salute")
  ]
}

Swift Tips: Type Property, Array Literal, Type Extension

In an extension to Exercise, you initialize the exercises array as a type property.

Refactoring ContentView & ExerciseView

Now, you’ll modify ContentView and ExerciseView to use your new Exercise.exercises array.

ForEach(Exercise.exercises.indices, id: \.self) { index in
var exercise: Exercise {
  Exercise.exercises[index]
}
HeaderView(exerciseName: exercise.exerciseName)
Exercise views work after refactoring.
Anesyowe woafl vogj uzxul laveykoretk.

Playing a Video

Skills you’ll learn in this section: AVPlayer and VideoPlayer; bundle files; optional types; make conditional; GeometryReader; adding padding

import AVKit
VideoPlayer(player: AVPlayer(url: url))

Getting the URL of a Bundle File

You need the URL of the video file for this exercise. The videoName property is the name part of the file. All the files have file extension .mp4.

if true {
  VideoPlayer(player: AVPlayer(url: url))
} else {
  EmptyView()
}
if let url = Bundle.main.url(
  forResource: exercise.videoName,
  withExtension: "mp4") {
Text("Couldn't find \(exercise.videoName).mp4")
  .foregroundColor(.red)

Getting the Screen Dimensions

The video takes up a lot of space on the screen. You could set the width and height of its frame to some constant values that work on most devices, but it’s better if these measurements adapt to the size of the device.

GeometryReader { geometry in
.frame(height: geometry.size.height * 0.45)
Video player uses 45% of screen height.
Piwae dmiqad ayir 67% om rwmaun foobgt.

Adding Padding

➤ The header looks a little squashed. Control-Option-click HeaderView to add padding to its bottom:

Add bottom padding to Header view in Exercise view.
Iyl tabjep tedcizw me Doodeg baap uf Igoqlexa kuus.

Padding under Header view
Yexxacr ewmeq Duuvan taad

HIITFit pages
CIILTud miguc

Creating Timer, Buttons & Rating

Skills you’ll learn in this section: Text with date and style parameters; types in Swift; Date(); Button, Spacer, foregroundColor; repeating a view; unused closure parameter

Creating the Timer View

➤ Add this property to ExerciseView, above body:

let interval: TimeInterval = 30
Text(Date().addingTimeInterval(interval), style: .timer)
  .font(.system(size: geometry.size.height * 0.07))
Exercise view with 30-second timer
Afocfowi vaaz qupz 45-qakezh pewid

Creating Buttons

Creating buttons is simple, so you’ll do both now.

Button("Start/Done") { }
  .font(.title3)
  .padding()
Spacer()
Button("History") { }
  .padding(.bottom)
Exercise view with buttons
Aciqhipo wuuq venm kucjifw

Creating the Rating View

➤ Create a new SwiftUI View file in the Views group named RatingView.swift. This will be a small view, so replace its #Preview { with this line:

#Preview(traits: .sizeThatFitsLayout) {
Image(systemName: "")
  .foregroundColor(.gray)
SF Symbols Health category: ECG waveform
QN Hhlqayb Luemzb cucayiyr: IDR hebolaqj

Image(systemName: "waveform.path.ecg")
  .foregroundColor(.gray)
right-click Image, select Repeat.
kuddr-cyapw Onavo, dokevh Rakoeq.

ForEach(0 ..< 5) { item in
  Image(systemName: "waveform.path.ecg")
    .foregroundColor(.gray)
}
HStack {
  ForEach(0 ..< 5) { item in
    Image(systemName: "waveform.path.ecg")
      .foregroundColor(.gray)
  }
}
.font(.largeTitle)
SF Symbols with largeTitle font size
MQ Wzbyepp wirq qigyaYovsa vepd fawa

ForEach(0 ..< 5) { item in
ForEach(0 ..< 5) { _ in
RatingView()
  .padding()
Exercise view with Rating subview
Icolketu naoq xahb Nuxucw faztoeq

Challenges

ExerciseView will be easier to understand if all its components are in separate view files.

Challenge: Create VideoPlayerView

➤ Move most of the VideoPlayer code to a separate SwiftUI view file named VideoPlayerView.swift, so you can call it in ExerciseView like this:

VideoPlayerView(videoName: exercise.videoName)
  .frame(height: geometry.size.height * 0.45)

Key Points

  • Declare SwiftUI views in the order you want them to appear.
  • Create separate views for your user interface elements. This makes your code easier to read and maintain.
  • Put each view modifier on its own line. This makes it easy to move or delete a modifier.
  • Xcode and SwiftUI auto-suggestions and default values are often what you want.
  • Let Xcode help you avoid errors: Use the Command-menu to embed or extract views.
  • The SF Symbols app and Xcode’s Symbols Library provide icon images you can configure like text.
  • Preview variants make it easy to check your interface for different user settings.
  • An enumeration is a named type, useful for grouping related values so the compiler can help you avoid mistakes like misspelling a string.
  • Swift is a strongly typed programming language.
  • GeometryReader enables you to set a view’s dimensions relative to the screen dimensions.

Where to Go From Here?

In the next chapter, you’ll lay out views for History, Welcome and Success.

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 scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now