Show Alert from Anywhere in SwiftUI
Reading time: 2 min
This recipe shows how to show an alert dialog from anywhere in the SwiftUI app. Normally, alert
is tied to a specific view, and its content is handled by that view's state. This solution, however, makes it so that the alert is defined in just a single place and is always presented on top of current view.
The end result (which admittedly doesn't fully convey what's going on) looks like this:
Recipe
Two things need to be put in place for this to work:
- Send a
Notification
from wherever in the app, with its payload object describing the alert to be shown. - Receive that notification in your app file, where an
alert
has been attached to yourContentView
.
First, add the name for the new notification:
public extension Notification.Name {
static let showAlert = Notification.Name("showAlert")
}
Then, define the model for the alert dialog shown. This will be sent in the notification itself and then translated into an Alert
view later on:
struct AlertData {
let title: Text
let message: Text?
let dismissButton: Alert.Button?
static let empty = AlertData(title: Text(""),
message: nil,
dismissButton: nil)
}
Next, open your app file. It's the one that contains a struct that inherits from App
. It should roughly look like this:
@main
struct MyAwesomeApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Now, extend it with:
- Few
@State
vars that represent the alert state. onReceive
to handle incoming notifications to show the alert.alert
to render the alert on top ofContentView
.
Here's what it should look like in the end:
@main
struct MyAwesomeApp: App {
@State private var showAlert = false
@State private var alertData = AlertData.empty
var body: some Scene {
WindowGroup {
ContentView()
.onReceive(NotificationCenter.default.publisher(for: .showAlert)) { notif in
if let data = notif.object as? AlertData {
alertData = data
showAlert = true
}
}
.alert(isPresented: $showAlert) {
Alert(title: alertData.title,
message: alertData.message,
dismissButton: alertData.dismissButton)
}
}
}
}
And that's actually it! Now, just send the notification from anywhere in your app to make the magic happen:
Button("Show alert") {
NotificationCenter.default.post(name: .showAlert,
object: AlertData(title: Text("Content View Alert"),
message: Text("I'm shown on top of everything!"),
dismissButton: .default(Text("OK")) {
print("Alert dismissed")
}))
}