Category: Mac

Running a Command-Line Program from a Mac App

Apple provides the Process class that lets you run Terminal commands and command-line programs inside a Mac app. If you were creating a git GUI client, such as SourceTree or Tower, you would use the Process class to run git commands from your app.

To run a command-line program from your Mac app, you must do the following:

  • Create a Process instance
  • Configure the process
  • Run the process

Create the Process

Creating a process is the easiest step. Call the Process class’s initializer.

Configure the Process

What you must configure depends on the command or program you want to run. You must set the executable URL, the location of the command or program you want to run. Some other things you may need to configure include the following:

  • Arguments to pass to the process
  • The current directory
  • Environment variables
  • Standard error
  • Standard input
  • Standard output

In my help book building app Phel, I run the hiutil command to index the help book. I configure the following items to run the command:

  • The executable URL, which is usr/bin/hiutil.
  • The directory to run the command, which is the Resources folder inside the help book bundle.
  • The arguments, which are -I, -lsm, Caf, the name of the index file, -vv, and ..

I must run the hiutil command a second time to generate a spotlight index file. I replace the lsm argument with corespotlight and supply the name of the spotlight index file.

Run the Process

Call the run function inside a do-catch block to run a process.

The following code runs the hiutil to generate an index file for an Apple help book:

How to Learn SwiftUI to Develop Mac Apps

A common complaint I hear online from Mac developers is that all the SwiftUI learning material focuses on iOS development, and there’s nothing on Mac development. This article provides advice on learning SwiftUI for Mac development.

A lot of iOS SwiftUI learning material also works on Mac

Mac developers have to accept the fact that iOS development is much more popular than Mac development. People creating SwiftUI tutorials, videos, and courses are going to focus on iOS developers.

The good news with SwiftUI is that most of the iOS learning material also applies to Mac development. Apple created SwiftUI so developers can create user interfaces for all of their platforms with one framework. If you find a SwiftUI tutorial or course that you like, such as Hacking with Swift’s 100 Days of SwiftUI course, you can use the tutorial or course to make Mac apps. You may have to make some changes to get things to work on Mac.

Lists are one area of SwiftUI where there are large differences between iOS and Mac. If you read an article about lists and try to use the code in a Mac app, you’ll run into problems. The following articles cover those differences:

You can use AppKit views in SwiftUI Mac apps

SwiftUI is not a finished framework, especially on Mac. Some views that Mac apps need do not have native SwiftUI equivalents, and some of SwiftUI’s views do not work well on Mac. A workaround for SwiftUI’s Mac limitations is to use AppKit views in your app.

To use an AppKit view in a SwiftUI Mac app, create a struct that conforms to the NSViewRepresentable protocol. My Using a UIKit or AppKIt View in SwiftUI article provides more details on using AppKit views in a SwiftUI app.

SwiftUI resources for Mac development

To address complaints about a lack of learning material on Mac development, I compiled a list of Mac development resources. Many of those resources cover using SwiftUI for Mac development.

This site has a number of SwiftUI articles, most of which apply to Mac development. You can see a complete list of articles on the Articles page.

Highlighting the Selected Item Button in a SwiftUI List

If you use a button for list items in SwiftUI, the list item does not highlight when you select it. This is expected behavior for a bordered button. But some apps need to use buttons that look like text to perform actions when selecting the list item. If you have a button that looks like text, you want to highlight the selection so people can easily see the selected item.

How do you highlight the selected item? You must do two things to highlight the selected item. First, you must set the background color for the list row. Second, you must set the foreground color for the list item.

Setting the Background Color

Add a .listRowBackground modifier to the button to set the background color. Use the accent color for the selected item to highlight it and the clear color for the other items to match the standard selection highlighting.

Setting the Foreground Color

When you select an item from a SwiftUI list in an app running in light mode, the text color for the item changes from a dark color to a light color. You must set the color based on the current color scheme or else the text of the selected item will be tough to read.

Start by adding an @Environment property to your SwiftUI view that stores the current color scheme: light or dark.

Next, create computed properties for the item color based on whether or not it’s selected. Use white for the selected item. For unselected items make the text white in dark mode and black in light mode.

Finally add a .foregroundColor modifier to the button. Use the selected item color for the selected item and the unselected item color for the other items.

Creating a SwiftUI Toolbar Button with an Image and a Label

Many toolbar buttons in Mac and iOS apps include an image and a label. The Mac apps Finder, Mail, and Pages are examples of apps whose toolbar buttons have an image and a label.

If you try to create a button in a SwiftUI toolbar with an image and a label, you will notice the label does not appear. You must do the following to create a toolbar button with an image and a label:

  • Create a label style for the button.
  • Add a .labelStyle modifier to your button label and use the label style you created.

Creating a Label Style

To create a label style, you must create a struct that conforms to the LabelStyle protocol. Add a makeBody function to the struct that returns a SwiftUI view.

A label style for a toolbar button should create a VStack with the image first and the text label second. Supply a font to use for the image and the label in the VStack.

Add the .labelStyle Modifier

After creating the label style for your toolbar button, add a .labelStyle modifier to the button’s label and supply the name of the struct you created for the label style. The following code shows an example of a SwiftUI button that uses a custom label style:

Credits

The answer from Eduardo to the following Stack Overflow question helped me with the code for this article:

Add Button with Image and Text in ToolbarItem SwiftUI

Adding a Help Menu to a SwiftUI App

When you create and run a SwiftUI Mac app project, the Help menu has a menu item named AppName Help, where AppName is the name of your app. If you choose that menu item, an alert opens saying help isn’t available for the app.

Help Books

The included menu item works only if your app has a Help Book bundled in the app. Many of Apple’s apps have Help Books. Choose Help > Xcode Help in Xcode to see an example of a Help Book.

Creating Help Books is painful. Apple’s Help Book Programming Guide was last updated in 2013, and there aren’t many articles online on creating Help Books.

Take Users to Your Site

For most apps it’s easier to add a page to your app’s website with your app’s help and add a menu item to go that page. You can even include a link on the page for people to download a user guide for your app. Creating a menu item that takes someone to your site involves the following steps:

  • Create a view for the menu item.
  • Create a SwiftUI link for the menu item.
  • Add the menu item to the menu bar.

Creating the Link and Menu Item

A SwiftUI link takes two arguments. The first argument is the link name, which is the menu item text for menus. The second argument is the link destination. The following code shows an example of a menu item with a link:

Adding the Menu Item to the Menu Bar

Add a .commands modifier to the window group or document group in the App struct for your app. Add a command group for the menu, replacing the default Help menu item with your Help menu items.

Do you want to add a help book to your app?

Earlier in this article I mentioned that creating a help book for a Mac app is painful. To help developers deal with this pain, I’m developing Phel, an app that creates help books from Markdown files.

Write your help pages in Markdown, import the help pages and images, hit the Publish button, and get an indexed help book you can add to your Xcode project.

You can download and try Phel at the Phel page.

Showing a SwiftUI sheet from a Mac Menu

Most articles on showing sheets in SwiftUI apps show an example of a button with a .sheet modifier that shows the sheet. But if you try to apply the example to a menu in a Mac app,

The sheet does not appear when you choose the menu item. How do you show the sheet when someone chooses the menu item?

  • Add a focused value for showing the sheet.
  • Set the focused scene value in your main view.
  • Add the menu item to show the sheet.
  • Add the menu item to the app’s menu bar.

Read the Accessing the Document in a SwiftUI Menu article to learn more about focused values and working with SwiftUI menus.

Add a Focused Value

Add a focused value that your app uses to control whether or not to show the sheet. In this article I’m going to use the example of a menu item that previews some content in a sheet.

Set the Focused Scene Value

After creating the focused value to show the sheet, you must make that value a focused scene value to show the sheet from a menu. Start by adding a property to the main SwiftUI view.

The next step is to add a .focusedSceneValue modifier to the view. The first argument is the name of the variable you created for the focused value. The second argument is a binding using the property you created in the view.

Apple added the .focusedSceneValue modifier in macOS 12.

Add the Menu Item

Menus in SwiftUI apps are SwiftUI views. To show a sheet from the menu, start by creating a property with the @FocusedValue property wrapper that contains the focused value to show the sheet.

Create a button for the menu item. The action for the button is to set the focused value’s wrapped value. The focused value for showing a sheet is a Boolean value so you give it the value of true, which shows the sheet when someone chooses the menu item.

Add the Menu to the App

After creating the menu you must add it to your app’s menu bar. Add a .commands modifier to the app’s window group or document group to add the menu to the menu bar.

Credits

Thanks to Jason Armstrong for his answer to the following Stack Overflow question:

Calling a sheet from a menu item

Showing Local HTML Content in a WKWebView

Apple provides the WKWebView class to show web content in Swift apps. Most apps use web views to show content from websites, but you can also show local HTML in a web view. There are two common ways to show local HTML in a web view.

  • Loading HTML strings
  • Loading HTML files

Loading HTML Strings

The easiest way to display HTML content in a web view is to create a string for the HTML and call the loadHTMLString function.

A common reason to load an HTML string is to preview Markdown content. You can see examples of loading HTML strings in my WikiDemo and SwiftUIMarkdownEditor GitHub projects.

The biggest limitation of loading HTML strings and showing them in a web view is that images will not show. Inserting images in a HTML string is a security risk so the web view won’t display them.

Loading HTML Files

If you want to show images in a web view, you must create a HTML file and show it in the web view by calling the loadFileURL function. The function takes two arguments. The first argument is the URL of the file to show in the web view. The second argument is a URL that you give the web view access to read. You normally give read access to a folder that contains subfolders you want to show, such as image and CSS files.

I recently added code in an app to preview EPUB books in a web view. An EPUB book has an OEBPS folder that holds most of the book’s content. The OEBPS folder has the following subfolders:

  • A Text folder that contains the book’s chapters.
  • An Images folder that contains the book’s images.
  • A Styles folder that contains any CSS files to style the book.

To preview the book I needed to pass the URL of the current chapter’s HTML file as the first argument to loadFileURL and the URL of the OEBPS folder as the second argument. Giving the web view access to the OEBPS folder gives the web view access to the images and CSS files.

Running a Mac App Outside of Xcode

When you are developing a Mac app, you run it often from Xcode. But eventually you will run it outside of Xcode. You have two ways to run a Mac app outside of Xcode.

  • Locate the build folder
  • Archive and export the project

Locate the Build Folder

If you’re actively developing your app, locating the app from its build folder is the easiest way to run the app outside of Xcode. In Xcode choose Product > Show Build Folder in Finder to show the app’s build folder.

Go to the Finder. The app will be in a folder inside the Products folder. Usually the name of the folder is Debug or Release.

Archive and Export the Project

When you have your app at a point where you can use it regularly on your Mac or share with other people, you should archive and export the project to create an app file that is not buried deep inside Xcode’s Derived Data folder.

In Xcode choose Product > Archive to archive the project. Xcode’s Organizer window contains your app archives. Choose Window > Organizer in Xcode to open the Organizer.

OrganizerWindow

Click the Archives item on the left side of the Organizer to show your app’s archives. If you have archived multiple projects, you may have to choose your app from the app menu that is above the Archives item.

Select the archive from the archive list and click the Distribute App button to export the archive. When you click the Distribute App button, a sheet opens asking you to choose a method of distribution. Xcode provides the following distribution methods:

  • App Store Connect
  • Developer ID
  • Development
  • Copy App

The first two methods require a paid Apple developer membership. I’m not sure about the Development method. If you don’t have a paid membership or are making an app for personal use, choose Copy App and click the Next button. Choose a location to save the app and click the Export button.

Now you have an app file that you can run on your Mac just like any other app. You can also use the file to copy the app to another Mac.

The Other Distribution Methods

The App Store Connect distribution method is for apps that are going to be in the Mac App Store.

The Developer ID distribution method is for apps that you are going to have people download from your own website. When you choose Developer ID, Apple notarizes the app so that people who download and install your app can trust it. The How to Notarize a Mac App article has more details on the Developer ID distribution method.

The Development distribution method is for creating an app file to share with people you know. When I tried the Development distribution method for a Mac app, I saw no difference between the Development and Copy App distribution methods.

Xcode Multiplatform App Targets

Starting in Xcode 14, when you create a multiplatform app project, Xcode creates a single app target with destinations for each platform you want to support: iPhone, iPad, Apple TV and Mac. This article provides an introduction to multiplatform app targets.

When to Use the Multiplatform App Target

Use the Multiplatform App Target if you want people to buy one version of your app on the App Store and run the app on all the platforms you support: iPhone, iPad, AppleTV, and/or Mac. Use separate targets if you plan to charge for each platform separately.

If you plan to sell a Mac version of your app outside the App Store, create a separate app target. Use the multiplatform app target to sell on the App Store and the Mac app target to sell outside the App Store.

Viewing, Adding, and Removing Platform Destinations

Select the target from the target list on the left side of the project editor and click the General button at the top of the project editor to see a list of the app’s platform destinations:

AppTargetSupportedDestinations

The supported destinations list has buttons to add and remove destinations. If you created a project in an older version of Xcode, you must add destinations to have a single app target that supports iPhone, iPad, AppleTV, and Mac. Older versions of Xcode have separate targets for each platform.

Mac Destination Choices

When adding the Mac destination to an app target, you have the following choices:

  • Mac
  • Mac Catalyst
  • Designed for iPad

Choosing Mac uses SwiftUI and/or AppKit for the Mac app. Mac is the best choice for a new app, especially one that uses SwiftUI.

Choosing Mac Catalyst uses UIKit for the Mac app. Mac Catalyst is the best choice if you want to make a Mac version of an existing iOS app.

Choosing Designed for iPad runs the iPad version of the app on Macs with Apple Silicon chips. Choose Designed for iPad if you want a Mac version of your iOS app and don’t want to do any work converting the app.

Choosing the Platform to Build and Run

Xcode can build and run for only platform at a time. How do you specify the platform to build and run?

There’s a jump bar in the project window toolbar.

ChoosePlatformToRun

Click the right part of the jump bar to choose the platform: Mac, a connected iOS device, or an iOS simulator.

Compiling Files for Specific Platforms

Previous versions of Xcode have separate targets for each platform. If you have separate targets with code that should be compiled for a specific platform, make the file a member of that target. But you can’t do that with the multiplatform app target because there’s only one target. What do you do if you have platform-specific source code files?

Tell Xcode what platforms a source code file should compile for. Click the Build Phases button at the top of the project editor and examine the Compile Sources build phase.

FilterDestinations

Xcode initially sets each source code file to build for each destination. Click on the Filter column for a source code file to open a popover.

PlatformFilterPopover

Deselect the Allow any platform checkbox and deselect the destinations you don’t want to use. Now that file compiles only for the platforms you specified.