Accessing the Document in a SwiftUI Menu

You’re making a SwiftUI document-based Mac app. You have a menu item in your app that performs an action on the document. How do you give the menu access to the document?

Use focused values and focused bindings.

Creating a Focused Value

A focused value provides a way for a SwiftUI app to observe values from the focused view or one of its ancestors. In a document-based SwiftUI Mac app, one of the focused view’s ancestors is the document window. By using a focused value your app can access the focused document.

To create a focused value, you must write two pieces of code. The first is a struct that conforms to the FocusedValueKey protocol. The second is a property for the document as an extension to the FocusedValues struct.

The struct creates a type alias for the document’s type, which is usually a binding to your document type.

The document variable is the value you will use when setting the focused value. The value must match this variable name.

Notice the type of the variable, DocumentFocusedValueKey.Value?. The first part of the type is the name of the struct you created for the focused value. The second part is the name of the type alias you created in the struct.

Setting a Focused Value

The .focusedValue modifier sets a focused value.

The name of the first argument must match the name of the variable you created in the FocusedValues extension.

Apple added the .focusedSceneValue modifier in iOS 15 and macOS 12. This modifier works much better than .focusedValue, which requires the person using the app to be focused on a text view or text field. If you can require iOS 15 and/or macOS 12, use .focusedSceneValue.

Where do you place the code to set the focused value? Attaching the focused value modifier to the content view in the app file is the most common place in a document-based SwiftUI app.

Focused Bindings

A focused binding gives a SwiftUI view access to the focused value. To create a focused binding, declare a variable with the @FocusedBinding property wrapper and supply the name of the focused value in parentheses.

Using Focused Bindings in SwiftUI Menus

To use a focused binding in a SwiftUI menu, you must create a SwiftUI view for the menu items. Create the menu items in the group. The following example shows a menu with items to make text bold and italic:

Creating Menu Commands

If you have a menu with multiple menu items, creating a set of menu commands lets you keep the menu creation code in its own Swift file. To create a set of menu commands, supply the views inside the CommandMenu block.

Add the Menu to the App

The final step is to add the menu to the app.

By creating a struct for the menu commands, the code inside the app’s body is much cleaner.

Credits

I learned much of the information shared in this article from the following article by Lost Moa:

Accessing the document in the SwiftUI macOS menu commands