Skip to main content

Command Palette

Search for a command to run...

How to Use Multiple Alerts in a Single SwiftUI View

Updated
3 min read
How to Use Multiple Alerts in a Single SwiftUI View

Ever tried adding multiple .alert() modifiers to a SwiftUI view, only to watch in frustration as only one actually works? You're not alone. SwiftUI's alert system has a quirk that catches developers off guard: chaining alert modifiers simply doesn't work the way you'd expect. But there's good news—with the right approach, you can handle as many alerts as you need from a single view, and the solution is actually more elegant than you might think.

This is the most elegant approach using SwiftUI's built-in alert modifier with identifiable items.

struct ContentView: View {
    enum ActiveAlert: Identifiable {
        case delete, save, error

        var id: Int {
            hashValue
        }
    }

    @State private var activeAlert: ActiveAlert?

    var body: some View {
        VStack(spacing: 20) {
            Button("Show Delete Alert") {
                activeAlert = .delete
            }

            Button("Show Save Alert") {
                activeAlert = .save
            }

            Button("Show Error Alert") {
                activeAlert = .error
            }
        }
        .alert(item: $activeAlert) { alertType in
            switch alertType {
            case .delete:
                return Alert(
                    title: Text("Delete"),
                    message: Text("Are you sure you want to delete?"),
                    primaryButton: .destructive(Text("Delete")) {
                        // Handle delete
                    },
                    secondaryButton: .cancel()
                )
            case .save:
                return Alert(
                    title: Text("Save"),
                    message: Text("Do you want to save changes?"),
                    primaryButton: .default(Text("Save")) {
                        // Handle save
                    },
                    secondaryButton: .cancel()
                )
            case .error:
                return Alert(
                    title: Text("Error"),
                    message: Text("Something went wrong"),
                    dismissButton: .default(Text("OK"))
                )
            }
        }
    }
}

Why this works: SwiftUI's .alert(item:) modifier watches for changes to an optional identifiable value. When the value changes from nil to a specific case, the corresponding alert appears. This uses a single alert modifier that dynamically displays different alerts based on the enum value.

Method 2: Using a Custom Alert Manager

For complex scenarios with many alerts across multiple views, create a dedicated manager to handle alert state.

class AlertManager: ObservableObject {
    enum AlertType: Identifiable {
        case delete(item: String)
        case save
        case error(message: String)
        case success

        var id: String {
            switch self {
            case .delete: return "delete"
            case .save: return "save"
            case .error: return "error"
            case .success: return "success"
            }
        }
    }

    @Published var currentAlert: AlertType?

    func showAlert(_ type: AlertType) {
        currentAlert = type
    }
}

struct ContentView: View {
    @StateObject private var alertManager = AlertManager()

    var body: some View {
        VStack(spacing: 20) {
            Button("Show Delete Alert") {
                alertManager.showAlert(.delete(item: "Photo"))
            }

            Button("Show Save Alert") {
                alertManager.showAlert(.save)
            }

            Button("Show Error Alert") {
                alertManager.showAlert(.error(message: "Network error"))
            }
        }
        .alert(item: $alertManager.currentAlert) { alertType in
            switch alertType {
            case .delete(let item):
                return Alert(
                    title: Text("Delete \(item)"),
                    message: Text("This action cannot be undone"),
                    primaryButton: .destructive(Text("Delete")) {
                        // Handle delete
                    },
                    secondaryButton: .cancel()
                )
            case .save:
                return Alert(
                    title: Text("Save Changes"),
                    message: Text("Save your work?"),
                    primaryButton: .default(Text("Save")) {
                        // Handle save
                    },
                    secondaryButton: .cancel()
                )
            case .error(let message):
                return Alert(
                    title: Text("Error"),
                    message: Text(message),
                    dismissButton: .default(Text("OK"))
                )
            case .success:
                return Alert(
                    title: Text("Success"),
                    message: Text("Operation completed"),
                    dismissButton: .default(Text("OK"))
                )
            }
        }
    }
}

Why this works: The AlertManager encapsulates all alert logic in a separate class, making it reusable across your app. You can inject this manager into multiple views via the environment, allowing centralized alert handling throughout your application.

For more detail visit

More from this blog

A

ArshTechPro

43 posts

A mobile expert primarily focused on the iOS ecosystem. Passionate about building robust, user-centric apps and exploring the latest in Swift, UIKit and SwiftUI.