Waldo sessions now support scripting! – Learn more
App Development

A Complete Guide to SwiftUI Alert

Juan Reyes
Juan Reyes
A Complete Guide to SwiftUI Alert
June 11, 2021
7
min read

UI alerts are one of the most common views that developers use to get a specific interaction from the user. From confirmations to input, alerts enable the developer a lot of control to ensure that the user flow goes in a particular way.

With SwiftUI, developers have found that implementing alerts has become even more accessible and straightforward than ever.

However, if you’re just dipping your toes and have zero experience with the SwiftUI workflow, don’t worry. This article will take you from zero to hero. So, read along.

This article is intended for any developer who wants to learn by doing. You’ll be building a simple and interactive Swift app, implementing the concepts we’ll explore as you progress. Additionally, you’ll work with some of the most commonly used features in alerts, and you’ll implement testing to confirm your work is in order.

Before you proceed, if you have zero experience with Swift, please check this very comprehensive documentation.

Before the Dive

First, let’s briefly explore what comprises a new SwiftUI project in Xcode.

When you create a new SwiftUI project, you’ll see that your base project contains some files. You’ll notice a ContentView.swift class file and an <APP_NAME>App.swift class file. Don’t worry; they’re pretty simple to understand.

SwiftUI View files have equivalent structures:

  • A ContentView that defines the design of the View.
  • A PreviewView to display the code in the previewer.

To modify the View, put your code in the “body” variable to add more views or elements. For example, you can see that there’s a TextView object with a “Hello World!” in it. You’ll be working there.

Right, now that that’s more clear, let’s dive into our sample project.

SwiftUI Alert Basics

Let’s begin by creating a simple form, so the user can input information and interact with your application.

How do you add a form? Simple—just wrap your element in a Form view, like so.

 
 
import SwiftUI
struct ContentView: View {
    var body: some View {
        Form(content: {
            Text("Hello World!")
        })
    }
}

That will end up looking like the following.

Image shows how to format display a simple preview using "Hello World!"

Well, that doesn’t look much like a form, does it?

Not really! But we’ll get there.

Now, let’s embed your form in a NavigationView and add a heading so it looks cleaner.

 
 
import SwiftUI
struct ContentView: View {
    var body: some View {
        NavigationView {
            Form(content: {
                Text("Hello World!")
            })
            .navigationBarTitle("Alert Me Form")
        }
    }
}
Image shows previous preview of "Hello World!" now including code to add a navigation bar title above it with the text "Alert Me Form"

All right, all right, that looks a bit cleaner. What’s next?

You need to add some fields so the user can input data and interact with the app. How can you do this? Replace the Hello World Text element with a TextView element inside the Form.

 
 
import SwiftUI
struct ContentView: View {
    @State var name: String = ""
    var body: some View {
        NavigationView {
            Form(content: {
                // Name Text field
                TextField("Name", text: $name)
            })
            .navigationBarTitle("Alert Me Form")
        }
    }
}

You might be wondering what that variable prefixed by @State is.

Variables prefixed with a @State directive are “state” variables that persist values and are accessible throughout the lifespan of the view. For example, in this case, state variables are needed for form elements to hold the user information (name) and the state of elements (showingAlert).

Image shows the previous preview, now with a form field to type in a name, instead of just a static sentence.

You can see now that the field displays correctly inside the form. You can even interact with it in the previewer if you click the play button on top of the device emulator.

Great! But you need to add a button so the user can submit their input, and you can finally show your alert.

Ordering Your Form

Have a look at this code:

 
 
import SwiftUI
struct ContentView: View {
    @State var name: String = ""
    var body: some View {
        NavigationView {
            Form(content: {
                Section {
                    // Name Text field
                    TextField("Name", text: $name)
                }
                Section {
                    // Submit button
                    Button(action: {
                    }, label: {
                        Text("Alert me!")
                    })
                }
            })
            .navigationBarTitle("Alert Me Form")
        }
    }
}

Notice that I segregated the elements into Segments, which help organize the fields inside a form.

Image shows previous preview now with both the form to submit a name and also a button labeled "Submit" to submit the form.

With the fields separated, it all looks much better.

You can now add your alert and link it to the submit button to show when it’s tapped.

 
 
import SwiftUI
struct ContentView: View {
    @State private var showingAlert = false
    @State var name: String = ""
    var body: some View {
        NavigationView {
            Form(content: {
                Section {
                    // Name Text field
                    TextField("Name", text: $name)
                }
                Section {
                    // Submit button
                    Button(action: {
                        showingAlert = true
                    }) {
                        Text("Alert me!")
                    }
                    .alert(isPresented: $showingAlert) {
                        Alert(title: Text("Alert!"),
                              message: Text("You have been alerted.\nThank you \(name)"),
                              dismissButton: .default(Text("OK")))
                    }
                }
            })
            .navigationBarTitle("Alert Me Form")
        }
    }
}

Notice that the alert is added directly to the button with a modifier. A modifier is simply a method that expands the view appearance or functionality. If you want to go a bit deeper, please check here.

This modifier requires a binding state variable that will inform the view if the alert is being displayed. To pass a binding variable, just append a “$” character to the beginning of the variable name. Once you do this, the value of this variable will  update automatically every time the alert displays or is dismissed.

Image shows previous preview, now with an alert popping up after trying to submit a form. The alert bubble says "Alert! You have been alerted! Thank you Josh"

When you input some value in the text field and tap the “alert me!” button, you can see the alert displays as expected.

Appending Functionality to Your Alert

Sometimes you’ll need to add a specific behavior when an alert is dismissed. Or maybe your alert is intended to get some input from the user.

To do this, you can simply add a closure to the button itself in the alert constructor.

 
 
import SwiftUI
struct ContentView: View {
    @State private var showingAlert = false
    @State var name: String = ""
    var body: some View {
        NavigationView {
            Form(content: {
                Section {
                    // Name Text field
                    TextField("Name", text: $name)
                }
                Section {
                    // Submit button
                    Button(action: {
                        showingAlert = true
                    }) {
                        Text("Alert me!")
                    }
                    .alert(isPresented: $showingAlert) {
                        Alert(title: Text("Alert!"),
                              message: Text("You have been alerted.\nThank you \(name)"),
                              primaryButton: .default(Text("OK")) {
                                name = ""
                              },
                              secondaryButton: .cancel())
                    }
                }
            })
            .navigationBarTitle("Alert Me Form")
        }
    }
}

Notice that the constructor used for this is different from the one-button alert.

Now, when you press the OK button, the text field clears.

Customizing Your Alert Appearance

Now that you have a form with the SwiftUI alert functionality included, let’s explore how you can customize your alert appearance.

There are several alert constructors at your disposal.

 
 
Alert(title: Text("Alert!"))
Alert(title: Text("Alert!"),
      message: Text("You have been alerted.\nThank you \(name)"),
      dismissButton: .default(Text("OK")))
Alert(title: Text("Alert!"),
      message: Text("You have been alerted.\nThank you \(name)"),
      primaryButton: .default(Text("OK")),
      secondaryButton: .cancel(Text("Cancel")))
Alert(title: Text("Alert!"),
      primaryButton: .default(Text("OK")),
      secondaryButton: .cancel(Text("Cancel")))

All of them are slight variations of the default alert, with an extra button or no description text. You can test them all and see how they display.

You might ask, “But what if I want to have more than two buttons?”

Well, unfortunately, there’s no way to do that in a SwiftUI alert as of yet. However, there’s another view better suited for this kind of need: ActionSheets.

To implement an ActionSheet, simply replace the “alert” modifier for the “actionsheet” modifier, like this.

 
 
import SwiftUI
struct ContentView: View {
    @State private var showingAlert = false
    @State var name: String = ""
    var body: some View {
        NavigationView {
            Form(content: {
                Section {
                    // Name Text field
                    TextField("Name", text: $name)
                }
                Section {
                    // Submit button
                    Button(action: {
                        showingAlert = true
                    }) {
                        Text("Alert me!")
                    }
                    .actionSheet(isPresented: $showingAlert) {
                        ActionSheet(title: Text("Alert!"),
                                    message: Text("You have been alerted.\nThank you \(name)"),
                                    buttons: [
                                        .default(Text("One")) { },
                                        .default(Text("Two")) { },
                                        .default(Text("Three")) { },
                                        .cancel()
                                    ])
                    }
                }
            })
            .navigationBarTitle("Alert Me Form")
        }
    }
}

As you can see, you now have more latitude to add buttons and functionalities to the form.

Want to have even more control over the alerts? You can find more information here.

Don’t Forget to Test

We can’t finish this article without introducing some proper testing into your project. Don’t worry. With SwiftUI and XCTest, doing tests has never been simpler.

First, find the Tests_iOS.swift file, and replace the testExample method with this code.

 
 
func testExample() throws {
    // UI tests must launch the application that they test.
    let app = XCUIApplication()
    app.launch()
    app.textFields["Name"].tap()
    app.textFields["Name"].typeText("Josh")
    app.textFields["Name"].typeText("\n")
    app.buttons["Alert me!"].tap()
    XCTAssertTrue(app.alerts["Alert!"].waitForExistence(timeout: 1))
    XCTAssert(app.alerts["Alert!"].staticTexts["You have been alerted.\nThank you Josh"].exists)
    // Use recording to get started writing UI tests.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
}

That’s all!

Now, execute the test, and see how the SwiftUI alert behaves.

For a more thorough and convenient testing solution, you can consider reviewing Waldo's extensive toolset for UI testing. It requires no coding, and it’s very approachable, even for non-developers. There’s even a free trial and a useful blog.

Staying Alert

Creating user interfaces that are interactive and well designed is a challenge that continues to evolve. For SwiftUI and all things Apple, the tools at our disposal have never been more convenient and approachable for newcomers—while staying flexible and extendable for the veterans who need more control.

Nevertheless, it’s always essential to keep our knowledge sharp and updated so we can take advantage of the advances that the platform is making in the market. So stay hungry for learning, stay curious for novelty, and stay alert for change.

Automated E2E tests for your mobile app

Waldo provides the best-in-class runtime for all your mobile testing needs.
Get true E2E testing in minutes, not months.

Reproduce, capture, and share bugs fast!

Waldo Sessions helps mobile teams reproduce bugs, while compiling detailed bug reports in real time.