cover

macOS Apps Step by Step

by Sarah Reichelt

Edition 4.0, November 2025
Copyright © 2025 Sarah Reichelt.

Notice of Rights

All rights reserved. No part of this book or corresponding materials (such as text, images, or source code) may be reproduced or distributed by any means without prior written permission of the copyright owner.

Notice of Liability

This book and all corresponding materials (such as source code) are provided on an “as is” basis, without warranty of any kind, express of implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in action of contract, tort or otherwise, arising from, out of or in connection with the software or the use of other dealing in the software.

Trademarks

All trademarks and registered trademarks appearing in this book are the property of their own respective owners.

Attribution

The author originally created portions of this work for the benefit of Kodeco, a community of developers who love to share their knowledge with the world. www.kodeco.com. The original title for the book was macOS by Tutorials.

License

By purchasing macOS Apps Step by Step, you have the following license:

  • You are allowed to use and/or modify the source code in macOS Apps Step by Step in as many apps as you want, with no attribution required.

  • You are allowed to use and/or modify all art, images and designs that are included in macOS Apps Step by Step in as many apps as you want, but must include this attribution line somewhere inside your app: “Artwork/images/designs: from macOS Apps Step by Step.

  • The source code included in macOS Apps Step by Step is for your personal use only. You are NOT allowed to distribute or sell the source code in macOS Apps Step by Step without prior authorization.

  • This book is for your personal use only. You are NOT allowed to sell this book without prior authorization, or distribute it to friends, coworkers or students; they would need to purchase their own copies.

All materials provided with this book are provided on an “as is” basis, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and non-infringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.

All trademarks and registered trademarks appearing in this guide are the properties of their respective owners.

Preface

Welcome to the fourth edition of macOS Apps Step by Step (previously macOS by Tutorials).

The first edition of this book was published in April 2022 and a lot has changed in the Swift and SwiftUI world since then. macOS and Xcode have also seen a lot of changes, especially this year with the introduction of Liquid Glass.

In 2023, Kodeco (formerly raywenderlich.com) changed their approach from being similar to a library to becoming more like a school or college. Sadly, this meant that they were no longer going to publish books like this one. However, since it was out-of-catalog, they allowed me to take over the book and publish in my own name. They have since reversed their stance on publishing, so you can expect to see more Kodeco books in the future.

I’d like to acknowledge their assistance and pay tribute to the editors who worked with me on the original edition: Richard Critz, Audrey Tam and Ehab Amer. Special mention to Manda Frederick who was the book manager for the first edition and without whom, this book would never have been written. Also a big thank you to Matt Derrick, former CEO of Kodeco, who agreed to and arranged the transfer of this book to me.

The major changes in this edition include:

  • A new title!

  • All projects updated to include new features introduced in macOS 26 Tahoe and Xcode 26.

  • All projects use Swift 6 with Approachable Concurrency.

  • OnThisDay has been re-designed to use a more stable HSplitView instead of the problematic NavigationSplitView.

  • Icons are created using Apple’s Icon Composer app.

  • MarkWriter uses SwiftUI’s WebView to display HTML content.

  • MarkWriter adds the ability to work with and edit selected text.

I hope you enjoy this updated edition of macOS Apps Step by Step.

About the Author

I got hooked onto trying to make computers do what I told them a very long time ago and have never stopped loving it. I’m a keen evangelist for developing native Mac apps. When not at my computer, I love coffee, puzzles, reading and cooking — the day hasn’t started until the first cup of coffee is drunk and the crossword is done! — Sarah

Sarah

About the Language Engineer (Editor)

I’m a fan of words and the things you can do with them, and I’ve spent a decade floating between different forms of writing — academic, fiction, technical. I hope that in this book I’ve succeeded at easing the passage of ideas between Sarah’s brain and your own, and I ask only that you blame me for any typos, stray capitals, Australianisms, and unoxfordenised commas that have made it into the final manuscript. — Peter

Peter

Dedication

To Tim who is endlessly supportive, even when listening to me complain about all my bugs.

Introduction

What do I love about programming for the Mac?

I use three Apple devices every day. My iPhone is primarily for communication; my iPad is mostly for entertainment. But the device where I spend most of my time is my Mac. The Mac is the most powerful, flexible, and unrestricted device Apple makes, and I love using it.

When writing Mac apps, I get to do so many things that iOS apps cannot do, are not allowed to do, or are not suited to. I can make beautiful, intricate, powerful apps that I use every day.

I use and write iOS apps, too. There is definitely a place for both, but I feel sad that so many developers don’t even consider the enormous possibilities of the Mac app development world.

I’m so happy that you’ve decided to consider those possibilities and join me on this journey!

What You Need

To follow along with this book, you’ll need the following:

  • A Mac computer with an Intel or M series processor, running macOS 26 or later. Any Mac that you’ve bought in the last few years will do, even a Mac mini or MacBook Air.

  • Xcode 26 or later. Xcode is the main development environment for building macOS Apps. It includes the Swift compiler, the debugger, and other development tools you’ll need. You can download the latest version of Xcode for free from the Mac App Store.

  • A basic understanding of Swift and SwiftUI. If you’re new to Swift, you might want to read my macOS Apprentice book first: https://www.kodeco.com/books/macos-apprentice

Where to download the materials for this book

The source code for this book can be cloned or downloaded from the GitHub repository:

Click the link to open it in your browser. You’ll see a big green Code button. Click on that and on Download ZIP. This puts all the code files in your Downloads folder.

Depending on your browser settings, you may need to double-click the ZIP file to expand it. That gives you a folder containing one sub-folder for each chapter. The text of each chapter tells you when you need to use any of these files or folders, but every chapter that involves coding has a final folder showing the end result of working through that chapter.

How to read this book

The chapters in each section are designed to take you from start to finish building a particular kind of app. While the book is fun from the first page to the last, if one section especially piques your interest, you’re free to dive right in there. If you are looking for guidance with a particular aspect of Mac development, scan the table of contents. The new, shorter chapter structure makes it easier to find what you need.

When you get to the code blocks, if the code is unfamiliar to you, then I strongly recommend that you type it in for yourself, rather than copy and paste it. Xcode’s autosuggestions will help you type, and having to get every detail correct will embed the new knowledge in your brain in a way that reading it can’t.

If you intend to use copy and paste for any of the code blocks, then go to Xcode’s Settings and in Editing → Indentation, turn on Re-indent on paste. This will solve a lot of issues with the format of the text in the book differing from Xcode’s format.

This book is available as HTML, PDF, or ePub. I recommend reading this book from the HTML version. It has good light and dark variations and the most accurate formatting. Also, the code blocks have a COPY button for when you want it, and there are no issues with pagination. My next favorite is PDF — use the light or dark version to suit your preference. My least favorite option is the ePub, as its display depends greatly on your ePub reader.

If you find any errors or typos, please report them to me at [email protected]. For problems with the code, please open an issue at https://github.com/trozware/mos_book_code/issues.

This book is split into five sections:

Section I: Your First App: On This Day

In chapters 1-14, you’ll begin your journey developing for macOS by building a full-featured app using SwiftUI. The app, On This Day, accesses a public network API to collect information about events, births, and deaths for a given date. Along the way, you’ll learn how to manage multiple windows, add menu and toolbar commands, and choose multiple display options. You’ll experience first-hand the power of SwiftUI and see just how easy it is to build an app that has all of the look and feel you expect in a macOS app.

Section II: Building a Menu Bar App

In chapters 15-27, you’ll use AppKit to build a Pomodoro-style time tracking app that lives only in the macOS menu bar. Along the way, you’ll learn how to manage timers, update the menu in real-time, and integrate a SwiftUI view into an AppKit app. You’ll also learn about how macOS “sandboxes” apps to protect both them and the system itself.

Section III: Building a Document-based App

In chapters 28-35, you’ll return to using SwiftUI and explore how to build a document-based app. You’ll create a Markdown editor — there can never be enough Markdown editors in the world! — that allows you to preview your text in real time. Along the way, you’ll add menu commands to change the styling of the preview and add formatting to your Markdown text.

Section IV: Advanced Wizardry

Because macOS has its roots in Unix, it provides a vast array of command line tools which allow power users to perform tasks ranging from system management to image manipulation. In chapters 36-44, you’ll learn how to build a graphical front-end for one such command: sips. Once you’ve built your sips GUI, you’ll enable automation to allow your new command to appear in the Services menu and Shortcuts app. When you complete this section, you too will be a wizard!

Section V: Distributing Your macOS Apps

Once you’ve written your app, you’ll want to distribute it to others so they can benefit from your creativity. On macOS, you have more distribution options than you do on iOS. In chapters 45-46, you’ll explore the pros and cons of those options so you can choose which is best for you.

Section I: Your First App: On This Day

Begin your journey developing for macOS by building a full-featured app using SwiftUI. The app, On This Day, accesses a public network API to collect information about events, births and deaths for a given date. Along the way, you’ll learn how to manage multiple windows, add menu and toolbar commands and choose multiple display options. You’ll experience first-hand the power of SwiftUI and see just how easy it is to build an app that has all of the look and feel you expect in a macOS app.

If you haven’t already downloaded it, you can clone or download the source code and assets from the GitHub repository: https://github.com/trozware/mos_book_code

Chapter 1: Creating the Data Models

In this section, you’ll build a SwiftUI app called On This Day which pulls notable events for a day from an API and displays them in various ways. The app uses an interface style that appears in many macOS apps, with a navigation sidebar, details area, toolbar, menus, and settings window. You’ll end up with an app that looks like this:

The finished app
The finished app

When starting a new app, it’s tempting to jump right into the interface design, but this time you’ll start by working out the data models. There are two reasons for this. First, in a SwiftUI app, the data drives the display, so it makes sense to work out the data structure before you start laying out the interface. Second, this data comes from an external source and may not have the structure you’d like. Spending some time now to analyze and parse will save you a lot of time and effort later on.

In this chapter, you’ll use a playground to fetch the data, analyze the structure, and create the data models for the app.

Data model design is a vital first step in the development of any app, so working through this chapter will be a valuable experience, but if you’re already familiar with downloading data, parsing JSON, and creating data structs and classes, feel free to skip ahead. In Chapter 2: Setting Up the Project, you’ll download and import the data model files used in this section and start building the user interface.

Where is the Data Coming From?

You’ll use the API from ZenQuotes.io. Go to today.zenquotes.io in your browser and look around the page. At the top, you’ll see an interesting historical event that happened on this day of the year, and you can scroll down to see more:

today.zenquotes.io
today.zenquotes.io

Keep scrolling until you get to the Quick Start heading. Underneath that, the Fetch Historical Events subsection gives you the format for the URL to access the data. Scroll all the way to the bottom and check the Usage Limits too. When testing, it’s easy to hit this limit, so one of your first tasks is to download a sample set of data to work with.

Under the Quick Start heading, follow the Read the full documentation link to get more information on the structure of the JSON returned by the API call. You’ll get to explore that in detail over the rest of this chapter.

Saving a Sample Data Set

If you haven’t already downloaded the supporting materials, follow the instructions in the Where to download the materials for this book section of the introduction.

Open the playground from the starter folder for this chapter. It’s a macOS playground set up with some functions to get you going. The most important one is getDataForDay(month:day:), which takes in a numeric month and day, assembles them into a URLRequest, and then uses URLSession to download the JSON from that URL.

If the data returned can be converted into a String, the function will save it to a file. But where should you save it? Unlike iOS, macOS gives you full access to the file system. The app sandbox may restrict this, as you’ll learn in later chapters, but in a playground, you can access everything. Since this is data you’re downloading, saving it to the Downloads folder makes the most sense, so now you need to work out the file path for the Downloads folder.

Working with the File System

Your first thought might be to build up the file path as a string. Maybe ~/Downloads would work. But remember not everyone uses English as their system language. My Downloads folder is at /Users/sarah/Downloads, but if I switch my system language to French, it’s at /Utilisateurs/sarah/Téléchargements. You can’t assume there’ll be a folder called Downloads.

The Sources section of the playground contains Files.swift, which holds functions for saving and reading the sample data. Expand the Sources section, if it’s not already expanded, and open Files.swift.

Note
If you can’t see the files list, press Command-1 to open the Project navigator.

The first entry is a computed property called sampleFileURL which returns a URL:

URL.downloadsDirectory.appending(path: "SampleData.json")
  • There’s only one line of code, so I’ve omitted the return keyword as it is implied. The property and functions in this file are public so that the other parts of the playground can access them.

  • The URL struct has a number of properties that you can use to access various system folders. These are properties of the URL struct itself, and not any particular URL.

  • downloadsDirectory is one of the standard system folders that you can locate, and this avoids any translation problems.

  • Append the sample data file name to the URL for the user’s Downloads folder to create the final URL.

Return to the main playground file when you’ve finished looking at this code.

Getting the Data

Since URLSession uses await, getDataForDay(month:day:) is an async function. As a result, you must call it asynchronously, so its usage is wrapped in a Task. Click the Play button in the gutter beside the last line of the playground and wait while it goes off to the API server, gathers the data, and returns it.

Note
If you don’t see a play button in the gutter, your playground is set to run automatically. Click and hold the play or stop button at the bottom of the code, and then choose Manually Run.

Once the download finishes, you’ll see a message in the console saying the playground has saved the sample data to your Downloads folder:

Saving sample data
Saving sample data
Note
If you don’t see the console, press Shift-Command-Y to open it at the bottom of the window.

The Results pane on the right also shows a lot of information about the URL and the URLRequest.

Go to your Downloads folder and open SampleData.json. On my computer, it opens in Xcode, but you may have a different app set up to open JSON files:

Downloaded JSON
Downloaded JSON

Formatting the JSON

The app you used may have formatted the JSON into a more readable form, but as you can see, Xcode has not. Here’s a trick that makes formatting JSON a breeze on any Mac.

Select all the text in the JSON file and copy it. Open Terminal and type in the following line (don’t copy and paste it or you’ll overwrite the JSON that’s in the clipboard):

pbpaste | json_pp | pbcopy

Press Return.

This sequence of three shell commands pastes the clipboard contents to the json_pp command, which "pretty prints" it, then uses pbcopy to copy the neatly formatted JSON back into the clipboard. Under the hood, macOS calls the clipboard the pasteboard which is why it uses pbpaste and pbcopy.

The vertical bar is a pipe symbol. The output from pbpaste is piped json_pp and the output from that is piped to pbcopy.

Return to your original SampleData.json file, delete its contents, and press Command-V to paste in the pretty printed JSON. Save the file again. It should now look something like this: