
SwiftUI Reusable Alert
Problem:
While developing an app in SwiftUI, I added duplicate code for displaying alert message on Views, And this is not good practice.
Later I was looking for same approach as we do in UIKit + Swift by adding UIViewController’s extension and add Alert there. We have achieved in previous story Link
So keeping in mind, To create a reusable alert class in SwiftUI that can be used throughout your app, you can define a AlertManager
class.
Create the AlertManager Class
import SwiftUI
import Combine
@Observable
class AlertManager {
static let shared = AlertManager()
var isShowingAlert = false
var alertTitle = ""
var alertMessage = ""
// Property to store multiple buttons
@Published var alertButtons: [Alert.Button] = []
// MARK: - Make the initializer private to enforce the singleton pattern
private init() {}
func showAlert(alertTitle: String, alertMessage: String, alertButtons: [Alert.Button]) {
self.alertTitle = alertTitle
self.alertMessage = alertMessage
self.alertButtons = alertButtons
self.isShowingAlert = true
}
func dismissAlert() {
self.isShowingAlert = false
self.alertButtons = [] // Reset buttons after dismissing
}
}
AlertManager
is a singleton class (shared
instance) so you can access it globally across the app.
uses @Observable macro to notify views when the alert should be shown or hidden.
Create an Alert Modifier for SwiftUI Views
Create a custom modifier to make it easier to attach an alert to any view
struct AlertModifier: ViewModifier {
@State var alertManager = AlertManager.shared
func body(content: Content) -> some View {
content
.alert(isPresented: $alertManager.isShowingAlert) {
if alertManager.alertButtons.count == 2 { // In case we have two buttons e.g. Accept & Decline
return Alert(
title: Text(alertManager.alertTitle),
message: Text(alertManager.alertMessage),
primaryButton: alertManager.alertButtons[0],
secondaryButton: alertManager.alertButtons[1]
)
} else if alertManager.alertButtons.count == 1 { // In case we have only one custom button e.g. Accept
return Alert(
title: Text(alertManager.alertTitle),
message: Text(alertManager.alertMessage),
dismissButton: alertManager.alertButtons[0]
)
} else { // In case we have only one default button with title 'OK'. e.g. no internet message
return Alert(
title: Text(alertManager.alertTitle),
message: Text(alertManager.alertMessage),
dismissButton: .default(Text("OK")) {
alertManager.dismissAlert()
}
)
}
}
}
}
Create an extension of View
extension View {
func globalAlert() -> some View {
self.modifier(AlertModifier())
}
}
How To Use
In your view
struct ToDoView: View {
var body: some View {
VStack {
Button("Two Button Alert With Action") {
towButtonAlertWithAction()
}
.padding()
Button("Single Button Alert With Action") {
singleButtonAlertWithAction()
}
.padding()
Button("Single Button Alert NO Action") {
singleButtonAlertNoAction()
}
.padding()
}
.globalAlert() // Attach the global alert modifier
}
}
private extension ToDoView {
func towButtonAlertWithAction() {
AlertManager.shared.showAlert(
alertTitle: "Action Alert",
alertMessage: "This alert has two buttons with actions.",
alertButtons: [
.default(Text("Accept")) {
print("Accept tapped")
},
.cancel(Text("Decline").) {
print("Decline tapped")
}
]
)
}
func singleButtonAlertWithAction() {
AlertManager.shared.showAlert(
alertTitle: "Action Alert",
alertMessage: "This alert has single button with action.",
alertButtons: [
.default(Text("Accept")) {
print("Accept tapped")
}
]
)
}
func singleButtonAlertNoAction() {
AlertManager.shared.showAlert(
alertTitle: "Alert",
alertMessage: "This alert has single OK button with no action.",
alertButtons: []
)
}
}
Now when we tap on Single button alert we have

We have made it. 🎉
Feel free to add your suggestion please or if you have better approach to achieve it.