How can I have two alerts on one view in SwiftUI?

I improved a litle Ben's answer. You can show multiple alerts dynamically by using .alert(item:) instead .alert(isPresented:):

struct AlertItem: Identifiable {
    var id = UUID()
    var title: Text
    var message: Text?
    var dismissButton: Alert.Button?
}

struct ContentView: View {

    @State private var alertItem: AlertItem?

    var body: some View {
        VStack {
            Button("First Alert") {
                self.alertItem = AlertItem(title: Text("First Alert"), message: Text("Message"))
            }
            Button("Second Alert") {
                self.alertItem = AlertItem(title: Text("Second Alert"), message: nil, dismissButton: .cancel(Text("Some Cancel")))
            }
            Button("Third Alert") {
                self.alertItem = AlertItem(title: Text("Third Alert"))
            }
        }
        .alert(item: $alertItem) { alertItem in
            Alert(title: alertItem.title, message: alertItem.message, dismissButton: alertItem.dismissButton)
        }
    }
}

There's a variation on this solution which only uses one state variable rather than two. It uses the fact that there is another .alert() form which takes an Identifiable item rather than a Bool, so extra information can be passed in that:

struct AlertIdentifier: Identifiable {
    enum Choice {
        case first, second
    }

    var id: Choice
}

struct ContentView: View {
    @State private var alertIdentifier: AlertIdentifier?

    var body: some View {
        HStack {
            Button("Show First Alert") {
                self.alertIdentifier = AlertIdentifier(id: .first)
            }
            Button("Show Second Alert") {
                self.alertIdentifier = AlertIdentifier(id: .second)
            }
        }
        .alert(item: $alertIdentifier) { alert in
            switch alert.id {
            case .first:
                return Alert(title: Text("First Alert"),
                             message: Text("This is the first alert"))
            case .second:
                return Alert(title: Text("Second Alert"),
                             message: Text("This is the second alert"))
            }
        }
    }
}

The second call to .alert(isPresented) is overriding the first. What you really want is one Binding<Bool> to denote whether the alert is presented, and some setting for which alert should be returned from the closure following .alert(isPresented). You could use a Bool for this, but I went ahead and did it with an enum, as that scales to more than two alerts.

enum ActiveAlert {
    case first, second
}

struct ToggleView: View {
    @State private var showAlert = false
    @State private var activeAlert: ActiveAlert = .first

    var body: some View {

        Button(action: {
            if Bool.random() {
                self.activeAlert = .first
            } else {
                self.activeAlert = .second
            }
            self.showAlert = true
        }) {
            Text("Show random alert")
        }
        .alert(isPresented: $showAlert) {
            switch activeAlert {
            case .first:
                return Alert(title: Text("First Alert"), message: Text("This is the first alert"))
            case .second:
                return Alert(title: Text("Second Alert"), message: Text("This is the second alert"))
            }
        }
    }
}

Tags:

Swift

Swiftui