Skip to content

How to show Alerts in SwiftUI app | SwiftUI Bootcamp #32

Communicating success, errors, or destructive confirmations through a native iOS alert requires just a few lines in SwiftUI. This lesson shows how to present dynamic, context-aware alerts using an enum to select the right message — a pattern that scales cleanly as your app adds more alert types.

What You'll Learn

  • How to attach .alert(isPresented:content:) to a view and control it with a Boolean flag
  • How to use an enum to dynamically select which alert to display from a shared presentation Boolean
  • How to create alerts with one button, two buttons, default actions, and destructive actions

Mental Model

Think of .alert like a PA announcement system in a building. The building (your view) has one PA speaker — the alert presentation system. You don't add a new speaker for every announcement. Instead, you queue up a message (set alertType = .error) and press the broadcast button (set showAlert = true). The PA system handles the delivery. Next time you want a different announcement, change the message first, then press the button again.

This is why the pattern uses both alertType and showAlert together: the enum describes what to say, and the Boolean describes when to say it. They work together so that one alert presenter can show many different messages over its lifetime.

Detailed Explanation

.alert(isPresented: $showAlert, content: { getAlert() }) binds the alert's visibility to showAlert. When showAlert becomes true, SwiftUI calls getAlert() to build the Alert value and presents it. When the user dismisses the alert (by tapping any button), SwiftUI automatically sets showAlert back to false — you don't have to manage the reset manually.

getAlert() is a plain Swift function that returns an Alert. The switch statement on alertType determines which Alert to build. This pattern is much cleaner than having a separate Boolean for every alert type — you'd need showErrorAlert, showSuccessAlert, showDeleteAlert, and so on.

Alert has several forms:

  • Alert(title:) — single button ("OK") dismiss alert
  • Alert(title:message:dismissButton:) — title, body text, one custom button
  • Alert(title:message:primaryButton:secondaryButton:) — two-button alert, one often .destructive

.default(Text("OK"), action: { ... }) creates a standard button with an optional action. .destructive(Text("Delete"), action: { ... }) creates a red button that signals a dangerous action. .cancel() creates a system cancel button that always dismisses the alert.

Code Structure

AlertBootcamp.swift manages two buttons that each set a different alertType before toggling showAlert. The getAlert() function uses a switch on the enum to return the appropriate Alert. The commented-out code demonstrates an alternative approach using dynamic string properties instead of an enum.

Complete Code

AlertBootcamp.swift

swift
import SwiftUI

struct AlertBootcamp: View {
    
    @State var showAlert: Bool = false          // controls whether any alert is currently presented
    @State var alertType: MyAlerts? = nil       // identifies which alert to show when showAlert is true
    //@State var alertTitle: String = ""
    //@State var alertMessage: String = ""
    @State var backgroundColor: Color = Color.yellow
    
    enum MyAlerts {
        case success
        case error
    }
    
    var body: some View {
        ZStack {
            backgroundColor.edgesIgnoringSafeArea(.all)
            
            VStack {
                Button("BUTTON 1") {
                    alertType = .error      // set the alert content first
                    //alertTitle = "ERROR UPLOADING VIDEO"
                    //alertMessage = "The video could not be uploaded"
                    showAlert.toggle()      // then trigger the presentation
                }
                Button("BUTTON 2") {
                    alertType = .success    // different content, same presentation mechanism
                    //alertTitle = "Successfully uploaded video 🥳"
                    //alertMessage = "Your video is now public!"
                    showAlert.toggle()
                }
            }
            .alert(isPresented: $showAlert, content: {
                getAlert() // called by SwiftUI when showAlert becomes true
            })
        }
    }
    
    func getAlert() -> Alert {
        switch alertType {
        case .error:
            return Alert(title: Text("There was an error!")) // minimal one-button alert
        case .success:
            return Alert(title: Text("This was a success!"), message: nil, dismissButton: .default(Text("OK"), action: {
                backgroundColor = .green  // side effect on dismiss — changes the background to celebrate
            }))
        default:
            return Alert(title: Text("ERROR")) // fallback for the nil case
        }
        
//        return Alert(
//            title: Text(alertTitle),
//            message: Text(alertMessage),
//            dismissButton: .default(Text("OK")))
//        return Alert(
//            title: Text("This is the title"),
//            message: Text("Here we will describe the error."),
//            primaryButton: .destructive(Text("Delete"), action: {
//                backgroundColor = .red
//            }),
//            secondaryButton: .cancel())
        //Alert(title: Text("There was an error!"))
    }
}

struct AlertBootcamp_Previews: PreviewProvider {
    static var previews: some View {
        AlertBootcamp()
    }
}

Code Walkthrough

  1. @State var alertType: MyAlerts? = nil — An optional enum that starts as nil. It's set before showAlert.toggle() runs. If getAlert() is called and alertType is still nil, the default branch of the switch catches it.

  2. Setting alertType before showAlert.toggle() — Order matters here. If you toggle showAlert first, SwiftUI might call getAlert() before alertType has been updated. Always set the content selection state before triggering the presentation.

  3. .alert(isPresented: $showAlert, content: { getAlert() }) — The modifier lives on the VStack. SwiftUI calls getAlert() when the alert is about to appear. After the user dismisses, SwiftUI sets showAlert back to false automatically — no cleanup needed.

  4. case .error: return Alert(title: Text("There was an error!")) — The simplest Alert form. One title, one system "OK" button, no custom action. Use this for informational errors that don't require user choices.

  5. dismissButton: .default(Text("OK"), action: { backgroundColor = .green }) — The dismiss button has an action closure. After the user taps OK, SwiftUI dismisses the alert and then runs this closure. This is where you'd trigger a follow-up behavior (like refreshing data or navigating away).

  6. Commented-out two-button alertprimaryButton: .destructive(...) combined with secondaryButton: .cancel() creates the standard iOS confirmation pattern for destructive operations (like delete). The destructive style renders the button in red.

Common Mistakes

Mistake: Attaching .alert to a view deep inside the hierarchy when multiple alerts are needed
Only one .alert modifier can be active per view (earlier alerts may be silently ignored if multiple are attached to the same view). Consolidate all alerts into a single .alert on a container view, using the enum pattern shown here.

Mistake: Toggling showAlert without setting alertType first
If alertType is nil when the alert is presented, the default branch fires and you see a generic "ERROR" alert. Always assign the enum case before toggling the Boolean.

Mistake: Adding logic to reset alertType in the dismiss button action
SwiftUI resets showAlert to false automatically on dismiss. You don't need to reset alertType because the next button tap will set it again. Adding unnecessary reset code creates clutter and can cause ordering bugs.

Key Takeaways

  • .alert(isPresented:content:) is driven by a Boolean; SwiftUI resets it to false automatically when the user dismisses the alert.
  • An enum combined with a shared Boolean is the cleanest pattern for showing different alerts from one view — avoids the proliferation of separate Boolean flags.
  • Alert buttons come in three styles: .default (standard), .destructive (red, for dangerous actions), and .cancel (safe exit) — choose based on what the action does, not just preference.

Last updated: June 27, 2026

Released under the MIT License.