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 alertAlert(title:message:dismissButton:)— title, body text, one custom buttonAlert(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
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
@State var alertType: MyAlerts? = nil— An optional enum that starts asnil. It's set beforeshowAlert.toggle()runs. IfgetAlert()is called andalertTypeis stillnil, thedefaultbranch of the switch catches it.Setting
alertTypebeforeshowAlert.toggle()— Order matters here. If you toggleshowAlertfirst, SwiftUI might callgetAlert()beforealertTypehas been updated. Always set the content selection state before triggering the presentation..alert(isPresented: $showAlert, content: { getAlert() })— The modifier lives on theVStack. SwiftUI callsgetAlert()when the alert is about to appear. After the user dismisses, SwiftUI setsshowAlertback tofalseautomatically — no cleanup needed.case .error: return Alert(title: Text("There was an error!"))— The simplestAlertform. One title, one system "OK" button, no custom action. Use this for informational errors that don't require user choices.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).Commented-out two-button alert —
primaryButton: .destructive(...)combined withsecondaryButton: .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 tofalseautomatically 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