Show an Alert from a Menu Item in a SwiftUI Mac App

SwiftUI provides an .alert view modifier you can apply to a view to show an alert in your app. If you add the .alert modifier to a SwiftUI view that is part of a CommandGroup menu, the alert does not appear. SwiftUI does not support directly showing an alert from a menu in the menu bar because a menu does not have a view for SwiftUI to attach the alert. How do you show the alert?

There are two ways to show an alert from a Mac menu item in a SwiftUI app. The first way is to apply the .alert modifier to the app’s content view. The second way is to use AppKit and NSAlert.

Apply .alert to the Content View

To get the alert to open, apply the .alert modifier to the content view in the body of the App struct.

@State private var showAlert = false

var body: some Scene {
  WindowGroup {
    ContentView()
      .alert("Alert message text", 
        isPresented: $showAlert) {
          Button("OK", role: .cancel) { }
      }
  }
}

In the menu item’s view, add a binding for showing the alert.

@Binding var showAlert: Bool

Set the showAlert property to true in the menu item code to tell SwiftUI to show the alert.

Finally, pass the property from the App struct to the menu item.

CommandGroup() {
  MenuItemView(showAlert: $showAlert)
}

Applying the .alert modifier to the content view works well if your app has only one main window or uses SwiftUI’s document architecture. If your app shows multiple windows of the same type using SwiftUI’s WindowGroup, which I wrote about in the Open Document-like Windows Using SwiftUI WindowGroup article, you will notice a problem when you apply the .alert modifier to the content view.

The alert opens once for each open window. If no windows are open, the alert doesn’t open.

NSAlert

To open the alert once no matter how many windows are open, use AppKit to show the alert. The AppKit way to show an alert is to create an NSAlert object, set the alert text, and call the runModal function.

let alert = NSAlert()
alert.messageText = "Alert Title"
alert.informativeText = "More explanatory text"
alert.runModal()

Place the NSAlert code inside a function and call the function when you want to show the alert.

Get the Swift Dev Journal Newsletter

Subscribe and get exclusive articles, a free guide on moving from tutorials to making your first app, notices of sales on books, and anything I decide to add in the future.

    We won't send you spam. Unsubscribe at any time.