Chapters

Hide chapters

macOS Apprentice

First Edition · macOS 13 · Swift 5.7 · Xcode 14.2

Section II: Building With SwiftUI

Section 2: 6 chapters
Show chapters Hide chapters

Section III: Building With AppKit

Section 3: 6 chapters
Show chapters Hide chapters

5. Beginning SwiftUI
Written by Sarah Reichelt

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

In Section 1, you installed Xcode and used various tools to run Swift code on your Mac. In this section, you’ll apply that knowledge to create an entire app for your Mac using Swift and SwiftUI.

Chapter 1 gave a tour of Xcode, explained the basic structure of a Mac app project and showed you how SwiftUI views and previews work together. This chapter builds on that.

The app you’re about to create is a word guessing game called Snowman. The computer picks a word and you enter letters to guess the word. Every time you choose an incorrect letter, part of the snowman disappears. If the snowman vanishes completely before you’ve guessed the word, you lose. :[

Snowman
Snowman

Setting Up Your App

Start Xcode as you’ve done many times now. Create a new project from the Welcome window or by selecting File ▸ New ▸ Project…. When you get to the template chooser, select macOS ▸ App. Click Next and set the details like this:

  • Product Name: Snowman
  • Team: If you have a developer team, select it, or leave this set to None.
  • Organization Identifier: Enter your reverse domain name as you did in Chapter 1.
  • Bundle Identifier: Xcode fills this in based on your previous entries.
  • Interface: SwiftUI
  • Language: Swift
  • Leave the checkboxes unchecked.

When your settings look like this, click Next:

Setting up the new project.
Setting up the new project.

Select where you want to save your project and click Create.

The project window appears and you’re ready to code.

What is SwiftUI?

When developing apps for the Mac, you have a choice of two layout frameworks. AppKit is the older one, and you’ll learn about it later. If you’ve done any iOS programming, it’s similar to UIKit. SwiftUI is the new framework, which Apple describes as “a modern way to declare user interfaces for any Apple platform”.

Laying Out the User Interface

Looking at the app image at the start of this chapter, there’s a sidebar listing the games played and a larger area to display the current game.

// 1
NavigationSplitView {
  // 2
  Text("Sidebar")
} detail: {
  // 3
  Text("Game view")
}
Previewing the split view.
Wquxoanowk cma bdbeb leul.

Splitting Up the Subviews

Your views will get more complicated, so it’s a good idea to split them out into their own files and structures.

Grouping the view files.
Lkuikibv rbi baul lolot.

// 1
struct SidebarView: View {
  // 2
  var body: some View {
    // 3
    Text("Hello, World!")
  }
}
var body: some View {
  // 1
  NavigationSplitView {
    // 2
    SidebarView()
  } detail: {
    // 3
    GameView()
  }
}
Separated subviews
Biralokay dohvuogk

Designing the Game View

One of the fundamentals of SwiftUI is that you can build complex views from a set of component views. It looks like the game view has a lot of parts, but if you break it down into components, you can add them one at a time. That way you don’t lose yourself in complexity.

The game view components
Fge keru yoar fuyvazinvz

The Snowman Images

SwiftUI has an Image view that can display a picture, but first, you need the images to show. Open the downloaded materials for this chapter and look in the assets folder. There’s a folder called Snowmen with images for both light and dark mode.

Importing images.
Urrekyiht icakek.

Snowman images
Btumgip udotoj

// 1
Image("0")
  // 2
  .resizable()
  // 3
  .aspectRatio(contentMode: .fit)
  // 4
  .frame(width: 230)
Previewing an image.
Vcefiugidy ug efega.

Stacking

SwiftUI offers various stack views for arranging components in your layout. HStack arranges them horizontally, VStack arranges them vertically and ZStack piles them on top of each other.

Text("Enter a letter to guess the word.")
  .font(.title2)

Looping Through Views

To create the letters view, you’ll loop through the letters in the word, using Text views to show each letter and overlaying this with a rounded rectangle to draw the box.

let word = ["S", "N", "O", "W", "M", "A", "N"]
// 1
HStack {
  // 2
  ForEach(word, id: \.self) { letter in
    // 3
    Text(letter)
      .font(.title)
      .bold()
      .frame(width: 20, height: 20)
      .padding()
      // 4
      .overlay(
        // 5
        RoundedRectangle(cornerRadius: 10)
          .stroke(lineWidth: 2)
          // 6
          .foregroundColor(.accentColor)
          // 7
          .padding(2))
  }
}
The Word view
Pte Babg fier

Buttons

Now, you’ll add the New Game button. It goes in the VStack underneath the letters view. Collapse the HStack that contains the ForEach loop and add a blank line after it.

// 1
Button("New Game") {
  // 2
  print("Starting new game.")
}
// 3
.keyboardShortcut(.defaultAction)
Running the app to see the default button.
Xejpomx pme egc ci xaa dto voveurc lerpov.

Adding the Final Components

The last section is the area that shows the letters guessed and allows you to guess new ones. First, you’ll need some test data to hold the guesses.

let guesses = [ "E", "S", "R", "X"]
// 1
HStack {
  // 2
  Text("Letters used:")
  // 3
  Text(guesses.joined(separator: ", "))
}
// 1
LabeledContent("Guess a letter:") {
  // 2
  Text("Q")
}
GameView preview
SaguVoal nzebeup

Spacing

When you look back at the original design, you’ll see that you have all the parts, but SwiftUI has clustered them into the center.

VStack spacing
CDcumn mkupelr

VStack(spacing: 30.0) {
  Spacer()    // NEW
  
  Text("Enter a letter to guess the word.")
Spacer()    // NEW

Button("New Game") {
  print("Starting new game.")
}
.keyboardShortcut(.defaultAction)

Spacer()    // NEW
Fixing the spacing.
Jolehv rqa pjevizk.

Framing the Window

Time for another run, so press Command-R to build and run the app:

Running the app.
Cusdijn dya ihv.

Image("2")
  .resizable()
  .aspectRatio(contentMode: .fit)
  .frame(width: 230)

Spacer()    // NEW

VStack(spacing: 30.0) {
}    // end of VStack
.padding()

Spacer()    // NEW
Large window
Niqvi cahnud

Snowman with no frame.
Cwakgis harl hi tyase.

Screenshot info
Flqaurmjum oxse

NavigationSplitView {
  SidebarView()
} detail: {
  GameView()
}
// 1
.frame(minWidth: 850, minHeight: 500)
Minimum frames set
Huciwut sninon jez

Tidying Your Code

You’ve finished the layout work for your GameView, but GameView.swift has become long and complicated. Now is a good time to separate out some of this code into subviews.

LettersView()
GuessesView()
struct GuessesView: View {
  let guesses = [ "E", "S", "R", "X"]

  var body: some View {
    VStack {
      HStack {
        Text("Letters used:")
        Text(guesses.joined(separator: ", "))
      }
      
      LabeledContent("Guess a letter:") {
        Text("Q")
      }
    }
  }
}

Key Points

  • SwiftUI is a framework that allows you to layout your user interface programmatically. You tell SwiftUI what you want and it decides how to do it.
  • You build your interface by assembling components and grouping them into stacks.
  • Modifiers change the views and you can chain multiple modifiers together.
  • With a Mac app, it’s important to set a minimum size for your window.

Where to Go From Here

Your game view interface is complete. In the next chapter, you’ll start making it live with real data instead of placeholders.

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.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now