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.