swiftswiftui

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


I want to have two unique alerts attached to the same Button view. When I use the code below, only the alert on the bottom works.

I'm using the official release of Xcode 11 on macOS Catalina.

@State private var showFirstAlert = false
@State private var showSecondAlert = false

Button(action: {
    if Bool.random() {
        showFirstAlert = true
    } else {
        showSecondAlert = true
    }
}) {
    Text("Show random alert")
}
.alert(isPresented: $showFirstAlert) {
    // This alert never shows
    Alert(title: Text("First Alert"), message: Text("This is the first alert"))
}
.alert(isPresented: $showSecondAlert) {
    // This alert does show
    Alert(title: Text("Second Alert"), message: Text("This is the second alert"))
}

I expect first alert to show when I set showFirstAlert to true and I expect the second alert to show when I set showSecondAlert to true. Only the second alert shows when its state is true but the first one does nothing.


Solution

  • 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"))
                }
            }
        }
    }