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

1. Introducing Xcode
Written by Audrey Tam

You’re eager to dive in and create your first macOS app. If you’ve never used Xcode before, take some time to work through this chapter. You want to be sure your tools are working and learn how to use them efficiently.

Getting Started

To develop macOS apps, you need a Mac with Xcode installed. If you have an account on GitHub or similar, you can connect to that from Xcode.

macOS

This book requires Ventura (v13.0) or later. To use the SwiftUI canvas, you need a Mac running Catalina (v10.15) or later. To install Xcode, your user account must have administrator status.

Xcode

To install Xcode, you need 23 GB free space on your Mac’s drive.

➤ Open the App Store app, then search for and GET Xcode. This is a large download — almost 8GB — so it takes a while. Fix yourself a snack while you wait.

➤ When the installation finishes, OPEN it from the App Store page:

Open Xcode after installing it from the App Store.
Open Xcode after installing it from the App Store.

Note: You probably have your favorite way to open a Mac application, and it’ll work with Xcode, too. Double-click it in Applications. Or search for it in Spotlight. Or double-click a project’s .xcodeproj file.

The first time you open Xcode after App Store installation, you’ll see this window:

Select the platforms you would like to install:
Select the platforms you would like to install:

➤ iOS and macOS are built into the 23 GB. Click Install and enter your Mac login password in the window that appears. This doesn’t take long, so don’t go away.

➤ When this installation process finishes, you’ll see this Welcome window:

Welcome to Xcode window
Welcome to Xcode window

If you don’t want to see this window every time you open Xcode, uncheck “Show this window when Xcode launches”. You can manually open this window from the Xcode menu Window ▸ Welcome to Xcode or press Shift-Command-1. And, there is an Xcode menu item to perform each of the actions listed in this window.

Creating a New Xcode Project

You’ll create a new Xcode project just for this chapter.

➤ Click Create a new Xcode project. Or, if you want to do this without the Welcome window, press Shift-Command-N or select File ▸ New ▸ Project… from the menu.

A large set of choices appears:

Choose a template for your new project.
Choose a template for your new project.

➤ Select macOS ▸ App and click Next. Now, you get to name your project:

Choose options for your new project.
Choose options for your new project.

  • For Product Name, type MyFirst.
  • Be sure to leave Team as None for now.
  • For Organization Identifier, type the reverse-DNS of your domain name. If you don’t have a domain name, just type something that follows this pattern, like org.audrey. The grayed-out Bundle Identifier changes to your-org-id.MyFirst. When you submit your app to the App Store, this bundle identifier uniquely identifies your app.
  • For Interface, select SwiftUI.
  • For Language, select Swift.
  • Uncheck the checkboxes.

➤ Click Next. Here’s where you decide where to save your new project.

Decide where to save your project.
Decide where to save your project.

Note: When a project is open, you can find its location by selecting File ▸ Show in Finder from the Xcode menu. If you forget where you saved a project, try looking in the Xcode menu File ▸ Open Recent.

➤ If you’re saving this project to a location that is not currently under source control, click the Source Control checkbox to create a local Git repository. Later in this chapter, you’ll learn how to connect this to a remote repository.

➤ Click Create. Your new project appears, displaying ContentView.swift in the editor pane.

New project appears with ContentView.swift in the editor.
New project appears with ContentView.swift in the editor.

Note: If your preview is paused, click the refresh button.

The app is pretty small. There are four zoom buttons in the lower right corner of the canvas: Zoom Out, Zoom to 100%, Zoom to Fit and Zoom In.

➤ Click Zoom to Fit to get a better look at the app. Then click Zoom to 100% to go back.

Looks like there’s a lot going on! Don’t worry, most macOS developers know enough about Xcode to do their job, but almost no one knows how to use all of it. Plus, Apple changes and adds to it every year. The best (and only) way to learn it is to jump in and start using it.

Ready, set, jump!

A Quick Tour of Xcode

You’ll spend most of your time working in a .swift file:

Xcode window panes
Xcode window panes

Note: If you don’t see the file extension .swift in the Project navigator, press Command-, to open Settings… then, in the General tab, set File Extensions to Show All:

Show all file extensions.
Show all file extensions.

The Xcode window has three main panes: Navigator, Editor and Inspectors. When the app is running, the Debug Area opens below the Editor. When you’re viewing a SwiftUI View file in the Editor, you can view the preview canvas side-by-side with the code.

The toolbar button just above the Navigator pane hides or shows it, and the button above the Inspectors pane does the same for it. The debug area has a hide/show button in its own toolbar. You can also hide any of these three panes by dragging its border to the edge of the Xcode window.

And all three have keyboard shortcuts:

  • Hide/show Navigator: Command-0
  • Hide/show Inspectors: Option-Command-0
  • Hide/show Debug Area: Shift-Command-Y

Note: There’s a handy cheat sheet of Xcode keyboard shortcuts in the assets folder for this chapter. It’s not a complete list; it covers the ones many people use.

Navigator

The Navigator has nine tabs. When the navigator pane is hidden, you can open it directly in one of its tabs by pressing Command-1 to Command-9:

  1. Project: Add, delete or group files. Open a file in the editor.

  2. Source Control: View Git repository working copies, branches, commits, tags, remotes and stashed changes.

  3. Symbol: Hierarchical or flat view of the named objects and methods.

  4. Find: Search tool.

  5. Issue: Build-time and runtime errors and warnings.

  6. Test: Create, manage and run unit and UI tests.

  7. Debug: Information about CPU, memory, disk and network usage while your app is running.

  8. Breakpoint: Add, delete, edit and manage breakpoints.

  9. Report: View or export reports and logs generated when you build and run the project.

The Filter field at the bottom is different for each tab. For example, the Project Filter lets you show only the files you recently worked on. This is handy for projects with a lot of files in a deeply nested hierarchy.

Editor

When you’re working in a code file, the Editor shows the code and a Minimap. The minimap is useful for long code files with many properties and methods. You can hover the cursor over the minimap to locate a specific property, then click to go directly to it. You don’t need it for the apps in this book, so you may want to hide it via the Adjust Editor Options button in the upper right corner of the editor.

When you’re working in a SwiftUI view file, Option-Command-Return shows or hides the preview canvas.

The editor has browser features like tabs and the ability to go back/forward. Keyboard shortcuts for tabs are the same as for web browsers: Command-T to open a new tab, Shift-Command-[ or -] to move to the previous or next tab, Command-W to close the tab and Option-click a tab’s close button to close all the other tabs. The back/forward button shows a list of previous/next files, but the keyboard shortcuts are Control-Command-right or -left arrow.

Inspectors

The Inspectors pane has three, four or five tabs, depending on what’s selected in the Project navigator. When this pane is hidden, you can open it directly in one of its tabs by pressing Option-Command-1 to Option-Command-5:

  1. File: Name, Full Path, Target Membership.
  2. History: Source Control log.
  3. Quick Help: Short form of Developer Documentation if you select a symbol in the editor.
  4. Accessibility: Accessibility information if you select a symbol in the preview.
  5. Attributes: Properties of the symbol you select in the editor.

All five tabs appear when you select a file in the Project navigator. If you select a folder, you get only the first three tabs. If you select Assets.xcassets, you don’t get the accessibility tab.

This quick tour just brushes the surface of what you can do in Xcode. Next, you’ll use a few of its tools while you explore your new project.

Navigation Settings

In this book, you’ll use keyboard shortcuts to examine and structure your code. Unlike the fixed keyboard shortcuts for opening navigator tabs or inspectors, you can specify settings for which shortcut does what. To avoid confusion while working through this book, you’ll adjust your settings to match the instructions you’ll see.

➤ Press Command-, to open Settings. In the Navigation tab, set:

  • Command-click on Code to Selects Code Structure
  • Option-click on Code to Shows Quick Help
  • Navigation Style to your choice of Open in Tabs or Open in Place.

Adjust Navigation Settings.
Adjust Navigation Settings.

ContentView.swift

The heart of your new project is in ContentView.swift, where your new project opened. This is where you’ll lay out the initial view of your app.

➤ If ContentView.swift isn’t in the editor, select it in the Project navigator.

The first several lines are comments that identify the file and you, the creator.

import

The first line of code is an import statement:

import SwiftUI

This works just like in most programming languages. It allows your code to access everything in the built-in SwiftUI module. See what happens if it’s missing.

➤ Click the import statement line, then press Command-/.

What happens if import SwiftUI is missing
What happens if import SwiftUI is missing

Note: All code on our site uses 2-space indentation to save space on the page. Xcode defaults to 4-space indentation. You can change this in Xcode Settings ▸ Text Editing ▸ Indentation.

You commented out the import statement, so compiler errors appear, complaining about View and PreviewProvider.

➤ Press Command-Z to undo.

Below the import statement are two struct definitions. A structure is a named data type that encapsulates properties and methods.

struct ContentView

The name of the first structure matches the name of the file. Nothing bad happens if they’re different, but most developers follow and expect this convention.

struct ContentView: View {
  var body: some View {
    VStack {
      Image(systemName: "globe")
        .imageScale(.large)
        .foregroundColor(.accentColor)
      Text("Hello, world!")
    }
    .padding()
  }
}

Looking at ContentView: View, you might think ContentView inherits from View, but Swift structures don’t have inheritance. View is a protocol, and ContentView conforms to this protocol.

The required component of the View protocol is the body computed property, which returns a View. In this case, it returns a VStack (vertical stack) that displays a globe image and the usual “Hello, world!” text.

Swift Tip: A computed property returns the computed value. If there’s only a single code statement, you don’t need to explicitly use the return keyword.

The VStack view has a padding modifier — an instance method of View — that adds space around the stack. You can see it in this screenshot:

VStack padding and Accessibility Inspector
VStack padding and Accessibility Inspector

This also shows the Accessibility inspector for the Image and Text subviews in VStack — label, value, identifier and traits.

Image(systemName: "globe") and its modifiers display a globe symbol. You’ll learn about the Image view in Chapter 5, “Beginning SwiftUI”.

Text("Hello, world!") displays the string “Hello, world!”.

➤ Click the Selectable button below the preview canvas, then click the Text view in the canvas and select the Quick Help inspector.

Text Quick Help and Developer Documentation
Text Quick Help and Developer Documentation

If you don’t want to use screen real estate for this inspector, Option-click Text in the code editor to see the same information in a pop-up window. Scroll down in either window to see the Open in Developer Documentation button, which opens a window with more information.

➤ Now, select the Attributes inspector. Click in the Add Modifier field and wait a short while until the modifiers menu appears:

Text attributes inspector and modifiers menu
Text attributes inspector and modifiers menu

Scrolling through this list goes on and on and on.

This inspector is useful when you want to add several modifiers to a View. If you just need to add one modifier, Control-Option-click the view in the code editor to open the Attributes inspector pop-up window.

struct ContentView_Previews

Below ContentView is a ContentView_Previews structure.

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}

ContentView_Previews is what appears in the canvas on the right of the code editor. Again, see what happens if it’s missing.

➤ Select the five lines of ContentView_Previews and press Command-/.

No PreviewProvider so there's nothing in the canvas.
No PreviewProvider so there's nothing in the canvas.

Without ContentView_Previews, there’s nothing in the canvas.

➤ Press Command-Z to undo or, if the five lines are still selected, press Command-/ to uncomment them.

You’ll sometimes want to give more space to the code editor, so your code doesn’t have to wrap.

➤ Press Option-Command-Return to hide the canvas. Press the same keyboard shortcut to show the canvas.

For most apps, ContentView.swift is just the starting point. Often, ContentView only defines the app’s organization, orchestrating several subviews. And usually, you’ll define these subviews in separate files.

Creating a New SwiftUI View File

Everything you see in a SwiftUI app is a View. Apple encourages you to create as many subviews as you need in order to avoid redundancy and organize your code to keep it manageable. The compiler takes care of creating efficient machine code, so your app’s performance won’t suffer.

➤ In the Project navigator, select ContentView.swift and press Command-N. Alternatively, right-click ContentView.swift then select New File… from the menu.

Select New File... from right-click menu.
Select New File... from right-click menu.

Xcode Tip: A new file appears in the Project navigator below the currently selected file. If that’s not where you want it, drag it to where you want it to appear in the Project navigator.

The new file window displays a lot of options! The one you want is macOS ▸ User Interface ▸ SwiftUI View. In Chapter 6, “Getting Data Into Your App”, you’ll get to create a Swift File.

Choose macOS  ▸ User Interface ▸ SwiftUI View.
Choose macOS ▸ User Interface ▸ SwiftUI View.

Naming a New SwiftUI View

➤ Select SwiftUI View then click Next. The next window lets you specify a file name. By default, the name of the new view will be the same as the file name. You’ll define the ByeView in this file, so replace SwiftUIView with ByeView.

SwiftUI view file name matches the new SwiftUI View.
SwiftUI view file name matches the new SwiftUI View.

Swift Tip: Swift convention is to name types (like struct) with UpperCamelCase and properties and methods with lowerCamelCase.

This window also lets you specify where (in the project) to create your new file. The default location is usually correct — in this project, in this group (folder) and in this target.

➤ Click Create to finish creating your new file.

The template code for a SwiftUI view is simpler than the ContentView of a new project.

import SwiftUI

struct ByeView: View {
  var body: some View {
    Text("Hello, World!")
  }
}

struct ByeView_Previews: PreviewProvider {
  static var previews: some View {
    ByeView()
  }
}

The view’s body contains only Text("Hello, World!") — no Image, so you don’t need a VStack, and no padding. Another subtle difference: The “Hello, World!” string is a token. It’s just a placeholder. Clicking anywhere in it highlights the token so you can type in a new value.

Using Your New SwiftUI View

➤ Click the “Hello, World!” token and change it so the line looks like this:

Text("Bye bye, World!")

➤ Next, in ContentView.swift, in the code editor, delete the Text view, then type bye. Xcode suggests some auto-completions:

Xcode suggests auto-completions.
Xcode suggests auto-completions.

Notice you don’t have to type the correct capitalization of ByeView.

Xcode Tip: Using descriptive names for your types, properties and methods is good programming practice, and auto-completion is one way Xcode helps you do the right thing. You can also turn on spell-checking from the Xcode menu: Edit ▸ Format ▸ Spelling and Grammar ▸ Check Spelling While Typing.

➤ Select ByeView() from the list, so the line looks like this:

ByeView()

You’re calling the initializer of ByeView to create an instance of the view.

➤ In the canvas, if the preview is paused, click the refresh button:

Replace Text view with ByeView.
Replace Text view with ByeView.

Your new ByeView has replaced the original Text view.

You’ll create many new SwiftUI view files and Swift files to develop the apps in this book.

What Else is in Your Project?

The Project navigator lists several files and folders.

  • MyFirstApp.swift: This file contains the code for your app’s entry point. This is what actually launches your app.
@main
struct MyFirstApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}

The @main attribute marks MyFirstApp as the app’s entry point. You might be accustomed to writing a main() method to actually launch an app. The App protocol takes care of this.

The App protocol requires only a computed property named body that returns a Scene. And a Scene is a container for the root view of a view hierarchy.

For a macOS app, the default setup is a WindowGroup scene containing ContentView() as its root view. A common customization is to set different root views, depending on whether the user has logged in. In a macOS or iPadOS app, WindowGroup can manage multiple windows.

  • Assets.xcassets: Store your app’s images and colors here. AppIcon is a special image set for all the different sizes and resolutions of your app’s icon.

Assets: A small sample of an AppIcon set
Assets: A small sample of an AppIcon set

  • MyFirst.entitlements: The entitlements file stores the project’s Signing & Capabilities settings. By default, App Sandbox and Access User Selected Files (Read-only) are on. Any settings you change appear here.

  • Preview Content: If your views need additional code and sample data or assets while you’re developing your app, store them here. They won’t be included in the final distribution build of your app.

In this list, the last item is a group. Groups in the Project navigator appear to be folders, but they don’t necessarily match up with folders in Finder.

Note: Don’t rename or delete any of these files or groups. Xcode stores their path names in the project’s build settings and will flag errors if it can’t find them.

You’ll learn how to use these files in the rest of this book.

Xcode Settings

Xcode has a huge number of settings you can adjust to make your time in Xcode more productive.

Themes

You’ll be spending a lot of time working in the code editor, so you want it to look good and also help you distinguish the different components of your code. Xcode provides several pre-configured font and color themes for you to choose from or modify.

➤ Press Command-, to open Settings, then select the Themes tab:

Settings: Font and color themes
Settings: Font and color themes

Go ahead and explore these. You can customize them or create your own. I’ll wait here. ;]

Matching Delimiters

SwiftUI code uses a lot of nested closures. It’s really easy to mismatch your braces and parentheses. Xcode helps you find any mismatches and tries to prevent these errors from happening.

➤ In Settings, select Text Editing ▸ Editing:

Settings ▸ Text Editing ▸ Editing
Settings ▸ Text Editing ▸ Editing

Most of the Code Completion items are super helpful. Although you can copy and paste code from this book, you should try to type the code as much as possible to learn how these aids work.

Here’s a big hint that something’s wrong or you’re typing in the wrong place: You’re expecting Xcode to suggest completions while you type, but nothing useful appears. When this happens, it’s usually because you’re outside the closure you need to be in.

➤ Now select the Text Editing ▸ Display tab. Check Code folding ribbon and, if you like to see them, Line numbers:

Settings ▸ Text Editing ▸ Display
Settings ▸ Text Editing ▸ Display

So what’s a code folding ribbon? Between the line numbers and the code, you see darker gray vertical bars.

➤ Hover your cursor over one — anywhere between VStack { and padding().

It highlights the start and end braces of that closure:

Code folding ribbon: Hover to show matching braces.
Code folding ribbon: Hover to show matching braces.

Other ways to see matching delimiters:

  • Option-hover over {, (, [ or a closing delimiter: Xcode highlights the start and end delimiters.
  • Double-click a delimiter: Xcode selects the delimiters and their contents.

➤ Now click the bar (ribbon) to collapse (fold) those lines of code:

Code folding ribbon: Click to fold code.
Code folding ribbon: Click to fold code.

This can be incredibly useful when you’re trying to find your way around some complex, deeply-nested code.

➤ Click the ribbon to unfold the code.

Adding Accounts

You can access some Xcode features by adding login information for your Apple ID and source control accounts.

➤ In Settings, select Accounts:

Settings ▸ Accounts
Settings ▸ Accounts

➤ Click the + button in the lower left corner and add your Apple ID. If you have a separate paid Apple Developer account, add that too.

To add capabilities like push notifications or Apple Pay to your app, you need to set Team to a Developer Program account. You’d do this in the Signing & Capabilities tab of the target.

  • If you have an account at Bitbucket, GitHub or GitLab, add it here if you want to push your project’s local git repository to a remote repository.

Add a source control account.
Add a source control account.

Bitbucket Server, GitHub and GitLab accounts require a personal access token. If the button to open GitHub’s token-creation page doesn’t work, open this URL.

➤ To set up a remote repository, open the Xcode Source Control menu and select New Git Repositories…

If you remembered to check Create Git repository on my Mac when you created your project, you can skip this step.

New Git Repositories...
New Git Repositories...

➤ Click Create:

Create Git repository.
Create Git repository.

➤ Now, open the Source Control navigator (Command-2), click the Repositories tab and expand the repository:

Expand Git repository in Source Control navigator.
Expand Git repository in Source Control navigator.

Control-click Remotes and select New “MyFirst” Remote…:

Control-click Remote, select New 'MyFirst' Remote...
Control-click Remote, select New 'MyFirst' Remote...

➤ Select your options, then click Create:

Create-remote options
Create-remote options

And here it is:

New remote repository created.
New remote repository created.

Running Your project

So far, you’ve relied on the preview canvas to see what your app looks like. You can use Live Preview to interact with your app. But some features don’t work in Live Preview, so then you need to build and run your app on your Mac.

The Xcode Toolbar

First, a quick tour of the toolbar:

Xcode window toolbar
Xcode window toolbar

Xcode Tip: Press Option-Command-T to show or hide the toolbar. If this keyboard shortcut conflicts with another app, select the command from the Xcode View menu.

So far, you’ve only used the buttons at either end of the toolbar, to show or hide the navigator or inspector panes.

Working from left to right after the navigation pane button:

  • Run button: Build and run (Command-R) the project.
  • Stop button appears when project is running: Stop (Command-.) the running project.
  • Source control button: Shows branches and lets you create a pull request.
  • Scheme menu: This button’s label is the name of the app. Select, edit or manage schemes. Each product has a scheme. MyFirst has only one product, so it has only one scheme.
  • Run destination menu: This menu defaults to your Mac.
  • Activity view: A wide gray field that shows the project name, status messages and warning or error indicators.
  • Library button: This button’s label is a + sign. It opens the library of views, modifiers, code snippets, media, colors stored in Assets and system symbols. Option-click this button to keep the library open.

Now that you know where the controls are, it’s time to use some of them.

Build and Run

➤ Check the run destination is your Mac, then click the run button or press Command-R.

After a short wait, your app appears!

Debug navigator, debug area and running app
Debug navigator, debug area and running app

There’s not much happening in this app, but the debug toolbar appears below the editor window. For this screenshot, I showed the debug area, selected the Debug tab in the Project navigator pane, then selected the CPU item.

The app itself is just a small window showing the globe symbol and “Bye bye, World!” text. The window is so small, the text is truncated! Now, take a closer look:

Resize or open new windows and merge them.
Resize or open new windows and merge them.

You can resize the window any way you want, make it full screen or minimize it. You can close the window and open a new one. In fact, you can open lots of new windows. When you have a few windows open, take a look at the Window menu. All the usual window and tab tools are already there and available to you.

The only control on this window is a static Text view, so there’s nothing to edit, but the Edit menu is there and ready for action, as are all the other standard menus that people expect to see in a Mac app.

Not Stopping

Here’s a trick that will make your Xcode life a little easier.

Don’t click the stop button. Yes, it’s enabled. But trust me, you’ll like this. :]

➤ In ByeView.swift, replace “Bye bye” with “Hello again”:

Text("Hello again, World!")

➤ Click the run button or press Command-R.

Up pops this message:

Check Don't ask again.
Check Don't ask again.

Don’t click Replace yet, although that will work: The running process will stop, and the new process will run. This dialog will appear every time you forget to stop the app. It’s just a moment, but it jars a little. Every time. And it’s easy to get rid of.

➤ Check Don’t ask again, then click Replace or Add.

The app loads with your new change. But that’s not what I want to show you.

➤ In ByeView.swift, replace “Hello again” with “Hey hey”:

Text("Hey hey, World!")

➤ Once more: Click the run button or press Command-R.

No annoying message, no “doh!” moment, ever again! You’re welcome. ;]

Note: Depending on which button you clicked, Xcode will either replace your app or add a new one. If you chose Add, you can now use the app switcher to move between the different versions. You can reset “Don’t Ask Me” warnings in Settings ▸ General ▸ Dialog Warnings.

In the rest of this book, you’ll create much more interesting apps.

Key Points

  • The Xcode window has Navigator, Editor and Inspectors panes, a Toolbar and a Debug Area, plus a lot of Settings.
  • You can specify navigation keyboard shortcuts in Settings, to match the instructions in this book.
  • The template project defines an App that launches with ContentView, displaying a globe symbol and “Hello, world!”.
  • You can view Quick Help documentation in an inspector or with a keyboard shortcut. Or, you can open the Developer Documentation window.
  • When you create a new SwiftUI view file, give it the same name as the View you’ll create in it.
  • Xcode’s auto-completion, delimiter-matching, code-folding and spell-checking help you avoid errors.
  • You can choose one of Xcode’s font and color themes, modify one or create your own.
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.