Category: Mac

Customizing the About Box in Your Mac App

Every Mac app has an About box, which you can access by choosing AppName > About AppName. For a new Cocoa project, the About box looks similar to the following image:


How do you customize the About box? Add a file named Credits.rtf to your project. Fill the file with your app credits. When you build the project, the text you entered in the RTF file will appear in a box between the version number and the copyright notice in the About box.

Choose File > New > File in Xcode to add a new file to the project. The RTF File template is in the Resource section.

Select the Credits.rtf file to edit the credits.


Now build your project and choose AppName > About. Your About box should look similar to the following screenshot:


If your credits are not appearing in a white scroll view, make sure you make the text long enough to trigger the need for a scroll view. When I just added the developer credit in the screenshot, the Credits appeared without a scroll view, and it didn’t look good.

Working with Open and Save Panels in Mac Apps

My short article on changing the button title for open and save panels gave me the idea to write a longer article on working with open and save panels.

You May Not Need to Deal with Open and Save Panels

Apple’s document architecture gives you a lot of default behavior for free. You can write an app that opens and saves files without having to write any code for the open and save panels. The most common case of having to write code that involves open and save panels is importing and exporting data. For example an image editor would use a save panel to export the image to common file formats, such as GIF, JPEG, and PNG.

Creating the Panel

Creating the open or save panel is the easiest step. Call the constructor for NSOpenPanel or NSSavePanel.

Customizing the Panel’s Appearance

When you create an open or save panel, you get the default appearance, which works well most of the time. But if you’re using a save panel to export a file, you want the button to say Export instead of Save because you’re exporting a file. AppKit provides several ways to customize the appearance of open and save panels. Some of the most popular ways include the following:

  • The prompt property lets you change the text of the default button.
  • The title property lets you change the title of the panel.
  • The nameFieldLabel property lets you change the label text in front of the filename text field.
  • The nameFieldStringValue property lets you set the filename currently shown in the filename text field.
  • The directoryURL property lets you set the current directory for the panel.
  • The accessoryView property lets you add an accessory view. A common use of an accessory view is to show a popup button with a list of file types.
  • The allowedFileTypes property lets you limit the file types you can open or save.

Custom Appearance Example

As an example, I’m going to share the code for a custom save panel for an app I’m developing. In this project I’m using a save panel to publish an EPUB book.

Because the save panel is for publishing books, I changed the panel’s title, name field label, and prompt (button text) to let people know this panel is for publishing books. I’m publishing EPUB books so I’m limiting the file types to files with the pub extension.

The most confusing line of code is the line that sets the text field value to self.displayName. The display name is the filename of the document that I’m publishing.

Showing the Panel

There are two ways to show an open or save panel. The easiest way is to show a panel as a modeless window. Call the panel’s begin method and supply a closure.

The other way is to show the panel as a sheet inside the document window. Call the panel’s beginSheetModal method. Supply the window and a closure.

Handling the Default Button Click

After showing the panel, you must handle the clicking of the default button (Open for an open panel, Save for a save panel). What you must do is check if the result of the panel running is NSFileHandlingPanelOKButton, which occurs when someone clicks the default button. Usually you will call a function that performs the task you want to do. For example if you were exporting a file, you would call a function that exports the file.

The Full Example

The following code shows the full example of using a save panel to publish a book:

The code begins by calling the buildPublishSavePanel function I wrote earlier to create the save panel and customize how it looks.

The next line of code calls the save panel’s begin method to show the save panel. The closure in the begin method uses a variable, result that stores the result of someone interacting with the save panel.

The first if statement checks if the person clicks the default (Publish) button. If so, the second if statement runs.

The second if statement gets the URL of the location the person chose to store the published book and calls a function to publish an EPUB book at the supplied URL. In your app you would call a function you wrote that creates (examples: importing or exporting a file) a file at the given URL.

Changing the Button Title for Open and Save Panels

Open and save panels are how Mac apps let people open and save files. An open panel defaults to using Open as the text for the main button. A save panel defaults to using Save. The default values make sense, but suppose you want to use an open panel to import a file in your app. How do you change the button text from Open to Import?

Use the prompt property to change the button text. The following example shows how to change the button text in an open panel from Open to Import:

Look at the NSSavePanel class reference to see other ways to customize the appearance of save and open panels.

Adding Undo Support for NSTextView

Mac text views support undo and redo for text editing without you having to write any code. All you have to do is set the text view’s allowsUndo property to true, which you can set in the attributes inspector.


Select the Allows Undo checkbox to turn on the text view’s undo/redo support.

Showing a Sheet When Choosing a Menu Item

VoodooPad is a Mac app for building a personal wiki. When you create a new wiki page in VoodooPad, a sheet opens for you to name the page.

I wanted something similar in one of my Mac app projects that uses storyboards. I created a view controller for the sheet. I added a toolbar button, made a connection to the sheet’s view controller and chose Sheet from the list of available storyboard segues.

One of Apple’s guidelines for Mac user interfaces is that every toolbar button should have a menu item. So I created a menu item to show the sheet and made a connection from the menu item to the sheet’s view controller. But there was no Sheet segue available. The only available segues were Custom, Show, and Modal. How do you show a sheet after choosing a menu item?

Solution Overview

You must perform the following steps to show a sheet from a menu in a Mac app using storyboards:

  1. Give the sheet’s view controller a storyboard ID to identify the view controller.
  2. Add an action to the main view controller that creates an instance of the sheet view controller and presents it as a sheet.
  3. Connect the menu item to the action.

Give the Sheet’s View Controller a Storyboard ID

The sheet’s view controller needs a storyboard ID so you can create an instance of the view controller in your code. Select the sheet’s view controller from the storyboard and open the identity inspector.


Enter the ID in the Storyboard ID text field. Remember the ID because you will need it when writing the code to show the sheet.

Add an Action to the Main View Controller to Show the Sheet

You must write an action in the main view controller to show the sheet. When someone chooses the menu item, your app performs the action. The main view controller is the controller that shows the window’s content.

The first step is to get the storyboard where the sheet view controller resides. I’m going to assume the main view controller and sheet view controller reside in the same storyboard so I can use the main view controller’s storyboard property to get the storyboard.

After getting the storyboard, call its instantiateController method, supplying the sheet view controller’s storyboard ID. After instantiating the sheet view controller, call the presentAsSheet method, supplying the sheet view controller.

Connect the Menu Item

The last step is to connect the menu item to the action you wrote to show the sheet. Make a connection from the menu item to the First Responder object in the Application scene. A long list of actions opens. Choose the action you created to show the sheet.

When someone chooses the menu item, your app will call the action you wrote. Calling the action will show the sheet.

Code Signing and the Sparkle Framework

Sparkle is a framework that simplifies updating Mac apps. With Sparkle people can check for updates inside your app. If there is a new version of your app, they can download and install it from your app.

I recently added Sparkle support to an app I’m developing. Sparkle has good setup instructions, but I ran into one issue not covered by their instructions. This article tells the story of me discovering and fixing this issue.

Background Information

Sparkle currently has two branches: a stable version 1 and a beta version 2, which adds support for sandboxed apps. Version 1 has a binary framework and related files that you can download and add to your project. You can also download the Xcode project from GitHub and build the framework yourself. Version 2 must be built from source by downloading the Xcode project.

Sparkle supports CocoaPods, but I chose to download the version 1 binary framework and add it to my project. My app isn’t sandboxed so I didn’t need version 2, and version 1 has much more documentation.

The Problem

I followed the installation instructions on the Sparkle site. I built and run the app, but it kept crashing at launch. The following error message appeared in Xcode’s debug console for the Sparkle framework:

not valid for use in process using Library Validation: mapped file has no Team ID and is not a platform binary

The Team ID is the code signing development team.

Initial Search to Find the Problem

I started my search for a solution by browsing the GitHub issues for the Sparkle project. I did not find any issues similar to the one I found.

The next step was to paste the console error message into a search engine. I found a thread in Apple’s paid developer forums where an Apple engineer said you have to code sign the framework with your code signing identity.

Yes! Code sign Sparkle with my code signing identity, and the problem will go away.

Code Sign Sparkle

I knew I wouldn’t be able to code sign the Sparkle binary framework. I would have to clone the project in Xcode, change the code signing build settings to use my code signing identity, build the framework, and add it to my project.

I wasn’t able to build version 1 from source (compiler errors) but was able to build version 2 so I went with that. I changed the code signing build settings, built the framework, and added it to the project.

The app continued to crash at launch with the same error message in the debug console. What was going on?

The Solution

Some more Internet searching for the error message took me to an article on testing a Swift Package Manager package. A sentence in the article, “To avoid signing issue, we need to select a Team for all frameworks”, led me to the solution.

I did not need to modify any code signing build settings to fix the issue. I had to change the Signing Certificate value for my app target in the Signing & Capabilities section.


The original Signing Certificate value was Sign to Run Locally. I had to change it to Development.

After changing the Signing Certificate value, I was able to use Sparkle in my app. I was able to use both the binary version 1 framework and the version 2 framework I built from the Xcode project. I ended up using the binary version 1 framework.

It took me several hours to solve the code signing issue. I hope this article keeps someone else from making the same mistakes I made.

Saving Data in a Swift App

People new to iOS development frequently ask how to save data in their apps. Apple provides the following technologies for saving app data on a local device:

  • UserDefaults
  • Keychain
  • Files
  • Core Data

You can use multiple methods to save data in your app. Many iOS and Mac apps use both UserDefaults and Core Data to save data. Use all the methods for saving data that make sense for your app.


Apple provides a UserDefaults class that provides a programming interface to your app’s defaults database. The defaults database contains user settings and preferences.

Use UserDefaults to store small amounts of data, like settings and preferences. If you are writing a plain text editor, you could store the font to use in UserDefaults.


Use the keychain to store small amounts of data securely, such as passwords. If you were writing a Twitter app, you could use the keychain to store the person’s Twitter handle and password.

Apple provides a Keychain Services API for apps to work with the keychain. The documentation is in the System > Security section of Apple’s documentation in Xcode. Choose Help > Developer Documentation in Xcode to read the documentation.


You can use files to store larger amounts of data. Document-based apps, such as text editors, spreadsheets, and drawing apps, usually save their data in files.

Apple provides the Codable protocol to simplify reading and writing data to files. An Internet search for Swift Codable tutorial yields many results, most of which cover JSON.

Core Data

Core Data is Apple’s framework for creating and working with a data model for iOS and Mac apps. One of the things Core Data does is save data for you.

Core Data is a huge subject. People have written books about it. You can find an overview of Core Data in Apple’s documentation in Xcode under App Services. Apple also has a Core Data Programming Guide on their developer website.

Core Data or Files?

Should you use Core Data or files to save larger amounts of data?

Core Data is a powerful framework that does a lot, much more than saving app data. But with that power comes a learning curve.

You must determine if your app’s data needs require Core Data. Avoid Core Data (or at least do some more research before using Core Data) in the following cases:

  • Your app supports non-Apple platforms like Android, Linux, and Windows. Core Data does not run on non-Apple platforms.
  • Your data can easily be saved in one file.
  • Your app data has only one class or struct.
  • You are writing a document-based app. Core Data supports document-based apps, but it doesn’t add much to Apple’s document architecture.

A text editor is an example of a project where you should save your data in a file. The app data is just a bunch of text. The data can easily be saved in one file. Core Data would not help you much.

Use Core Data to save your app’s data in the following cases:

  • You store large amounts of data in the app.
  • The data makes more sense to store in a database than in a single file.
  • Your app data has multiple classes or structs.
  • You are writing a shoebox (not document-based) app.

A blog editor is an example of a project where you should use Core Data. A blog can contain hundreds of posts that would be difficult to store in a file. A blog has multiple pieces of data: titles, tags, categories, and authors. Core Data can do things like show you all the blog posts with a given tag. Take advantage of Core Data if it will help for your app.

If you are still not sure whether or not to use Core Data, use it.

Create a Mac Markdown Editor with Live Preview

I’m working on a Mac app that previews HTML in a web view, and it gave me a good idea for a tutorial. Create a simple Markdown editor with live preview.

I have the project on GitHub for you to download.


To take full advantage of this tutorial, you must be running Xcode 11 or later. I use the Swift Package Manager support added in Xcode 11 to add the Ink parser to the project. It might be possible to build a framework for Ink by cloning the GitHub project, but I have not tried it. You could also try searching GitHub for another Markdown parser that supports earlier Xcode versions.

I recommend reading the Create a Document-Based Mac App in Swift article. It provides an introduction to creating document-based Mac apps and covers some topics that I gloss over in this tutorial. It also has links to other introductory Mac development articles. This tutorial is long enough. If I were to add explanations on basic topics like making connections in storyboards, the tutorial would be as long as a short book.

Create the Project

Create a Cocoa App project in Xcode. Choose Storyboards from the User Interface menu because this project does not use SwiftUI. Select the Create Document-Based Application checkbox. Enter md for the document extension, which is the file extension for Markdown documents.

Deal with the App Sandbox

When I created this project I could not get the live preview to display any HTML. The issue involved the App Sandbox. New Xcode projects use the App Sandbox, which disables all network connections by default.

There are two ways to work around the App Sandbox. You can turn off the App Sandbox or turn on network connections by selecting the two Network checkboxes.


Import the Ink Parser

This article uses John Sundell’s Ink Markdown parser to convert Markdown to HTML. Add the Ink parser to the list of Swift packages.

Select the project from the project navigator to open the project editor. Select the project file in the project editor. Click the Swift Packages button at the top of the project editor to see a list of installed Swift packages. Click the Add button to add a Swift package.

Enter Ink in the search field and press the Return key to see a list of Swift package repositories with Ink in the name.


Select the Ink item whose owner is JohnSundell. Click the Next button.

Now you must determine which version of the Ink parser to use. Click the Branch radio button and enter master. This tells Xcode to use the most recent stable version of the Ink parser.


Click the Next button to finish adding the Ink parser Swift package to the project.

Build the User Interface

The user interface for the Markdown editor consists of a vertical split view with two items. The left view is a plain text text view where you enter Markdown text. The right view is a web view that shows the HTML version of the Markdown text.

Open the storyboard. You will see a window with a view controller. Delete that view controller. Add a vertical split view controller to the storyboard. Make the split view controller the window’s content controller by making a connection from the Window Controller item in the window controller scene to the split view controller and selecting the window content relationship segue.

Adding the split view controller creates view controller scenes for the two items in the split view. Remove the views in the two view controllers. Add a Plain Document Content Text View item to the first view controller. Add a WebKit View item to the second view controller.

Select the text view’s scroll view, open the size inspector, and click the two arrows in the inner autoresizing square. Select the web view and do the same thing. The text view and web view will now resize properly when the window resizes.

Set the Text View’s Delegate

Set the text view’s delegate to the text view controller so the text view controller will get notified when the text view’s contents change.

Make a connection from the text view to the text view controller in the storyboard to set the delegate.

Create View Controller Subclasses

The next step is to create Swift files for the three view controllers you created in the storyboard: split view controller, text view controller, and web view controller. I use the name preview view controller for the web view controller.

Choose File > New > File to add a new file to the project. Select Cocoa Class from the list of Mac file templates. Enter the name of the class. For the subclass enter NSSplitViewController for the split view controller and NSViewController for the other two view controllers. Do not create xib files for the view controllers. You created the view controllers in the storyboard.

The text view controller must conform to the NSTextDelegate protocol to receive any text view notifications, such as the text view contents changing.

Open the storyboard and the identity inspector. Set the custom class for each view controller to the subclass you created.

Create Outlets

The text view controller needs an outlet to access the text view. The preview view controller needs an outlet to access the web view.

Open the storyboard and the view controller source file in separate editors. Make a connection from the view in the storyboard to the class in the source code file to create and connect an outlet.

Accessing the Document

The view controllers need access to the document to access and change its contents. The split view controller has access to the document so you can get the document from the split view controller. Add the following function to the split view controller:

Reaching the document requires some deep navigation. You have to get the view, get the view’s window, and get the window’s window controller to access the document.

Accessing the Other View Controllers

An annoying aspect of Mac split view controllers is they have no easy way for the child view controllers to communicate with each other, even though they’re grouped together in the split view. Each view controller scene is self-contained. For the Markdown editor the text view controller knows nothing about the web view. The web view controller knows nothing about the text view. The view controllers have to go through the split view controller to access the other view controllers.

The split view controller has a children property that contains a list of child view controllers. The text view controller is the first child.

The preview view controller is the second child.

Add those two functions to the split view controller.

Coding the Live Preview

When the text changes, you must convert the Markdown to HTML and display it in the web view. Start by importing the Ink framework in the preview view controller.

To convert the Markdown to HTML in Ink, create a MarkdownParser object and call its html function.

To display the HTML in the web view, call the web view’s loadHTMLString function. Supply the HTML string as an argument. You can also supply a base URL for relative paths, which can help if you’re going to display images.

Add the parse and updatePreview functions to the preview view controller.

Telling the Web View to Update

Call the preview view controller’s updatePreview function to update the web view. Add the following function to the text view controller:

Notice how the text view controller has to go through the split view controller to access the preview view controller to update the preview.

Call updateLivePreview when the text view’s contents change. Add the following function to the text view controller:

Run the project now. You should be able to type in the text view and see the HTML in the web view.

Updating the Document Contents

When the text view’s contents change, the document’s contents should also change. Start by adding the following property to the Document class:

The markdown property stores the document’s contents.

Add the following functions to the text view controller:

The saveTextViewContents function sets the document’s contents to the text view’s contents. The getDocument function is a helper function to give the text view controller access to the document.

Add a call to saveTextViewContents to the textDidChange function. Now the document’s contents update when the text view’s contents change.

Marking the Document as Changed

When someone types in the text view, you must mark the document as being edited so it will autosave. Add the following code to the textDidChange function:

Saving the Document

Open the Document.swift file. Change the data function to the following:

The code takes the Markdown string and converts it to a Data object that can be saved to a file. If the conversion fails, save an empty file.

Loading the Document

Open the Document.swift file. Change the read function to the following:

The code takes the Data object from the saved document and converts it to a Swift string. If there is saved text, set the document’s contents to the saved text.

Fill the Text View When Opening a Document

The last step is to fill the text view with the document’s contents when you open a document. Override the viewDidAppear function in the text view controller and set the text view’s contents to the document’s contents.

While you’re setting the text view contents you should also update the web view so it shows the HTML preview. That’s why there’s a call to updateLivePreview. If you didn’t have the call, the preview would be blank until you typed in the text view.

Now when you run the project you should have a basic Markdown editor. You can create documents, preview the HTML contents, save documents, and open documents.

Using Text Views in a SwiftUI App

SwiftUI currently lacks native support for text views. (Note: Xcode 12 adds text view support to SwiftUI). If you want people to enter large amounts of text in a SwiftUI app, you have to wrap a UIKit or AppKit text view. This article shows you how to use a text view in an iOS app that uses SwiftUI. Most of this material should also apply to Mac apps. Replace anything that has a UI prefix with NS.


To use UITextView in a SwiftUI app, you must create a struct for the text view and have it conform to the UIViewRepresentable protocol. The UIViewRepresentable protocol is how you wrap UIKit views in SwiftUI apps.

Don’t forget to import the UIKit framework.

Create the View

To conform to the UIViewRepresentable protocol, you must add two functions to the struct you created. The first function is makeUIView, which creates and configures the view. It takes an argument of type Context and returns the type of view you want to create, which is UITextView for a text view. The Context type is a type alias for the context where updates to the UIKit view take place. The following code shows an example of creating and configuring a text view:

At a minimum you must create the view and return it. In the code sample I decided to make the text view scrollable and editable. I also added some padding so the text isn’t on the left edge of the screen.

Update the View

The second function you must write is updateUIView, which handles updates to the view. The updateUIView function takes two arguments: a view and a context.

I will be filling the function later in the article.

Try Creating Your Own Text View

At this point you have enough to add a text view to a SwiftUI app. Create an iOS app project in Xcode. Add a file for the TextView struct. Fill the struct with the code I’ve shown so far in the article. Open the ContentView.swift file and replace the text label in the body property with a text view.

Build and run the app. You should be able to type in the text view.

Connecting to Your Data Model

At this point you can type in the text view, but you’ll lose what you type when you quit the app. For a text view to be useful, it needs a connection to your app’s data model.

In the struct for the text view, add a property for the data model. For a simple app, you can create a @State property wrapper that holds the text.

In a real app you’re more likely to have a reference to the data model in another SwiftUI view. Use the @Binding property wrapper to access data from another SwiftUI view.

Replace MyModel with whatever the name of your data model is.

Now that you have a connection to your data model, you can fill in the updateUIView function. Make the text view show the text from the data model.


In order for a UIKit view to communicate with data in SwiftUI, the view needs a coordinator. To conform to the UIViewRepresentable protocol, you must add a new function to the TextView struct, makeCoordinator. The makeCoordinator function creates the coordinator. The return type takes the form StructName.Coordinator.

The next step is to set the text view’s delegate to the context’s coordinator. Add the following line of code to makeUIView before the return statement:

The last step is to add a class for the coordinator. Create a class for the coordinator inside the struct for the text view. Make sure the name of the class matches the name you supply to the makeCoordinator function.

The Coordinator class inherits from NSObject, which is the base class for all UIKit and AppKit classes. A coordinator for a text view should conform to UITextViewDelegate so it can respond to text view notifications, such as the contents of the text view changing.

The coordinator needs a property for the control it’s going to coordinate. The control property contains a reference to the text view.

The Coordinator class needs an initializer to set its view, which is the control property in the code sample.

The last block of code tells the coordinator to respond when the contents of the text view change. The function sets the model’s contents to the text view’s contents. If you want the text view to handle other notifications, add the functions to the coordinator.

Responding to Text Changes in Mac Apps

Responding to text changes has more differences in Mac apps. Let’s look at the code to respond to text changes on Mac.

The name of the function is textDidChange. Instead of taking the text view as an argument, it takes a notification. The object property of the notification is the object that sent the notification. When entering text, the text view is the object that sends the notification that the text changed. The guard statement ensures the object that sent the notification is a text view. The property to get the text in NSTextView is string, not text.


To use UIKit views in a SwiftUI, you must perform the following tasks:

  • Create a struct for the view that conforms to the UIViewRepresentable protocol.
  • Write the makeUIView function to create and configure the view.
  • Write the updateUIView function so SwiftUI can update the view.
  • Write the makeCoordinator function to create a coordinator for your view to communicate with SwiftUI data.
  • Create a class for the coordinator.

I have a simple plain text editor project on GitHub if you want to see an example of a SwiftUI project that uses a text view.