DEV Community

Kristaps Grinbergs
Kristaps Grinbergs

Posted on

How to use Alert with SwiftUI

Alerts are one of the most crucial building blocks of iOS applications. We can use them to inform users about an error if something takes more time, like downloading a file or ensuring that everything is OK. Another use case with alerts is asking confirmation if our app users want to delete something or make a wire transfer.

SwiftUI has the Alert component, and there are a couple of ways how to use it. Let's check it out.

Alert

One of the easiest ways how to show the alert with SwiftUI is to have a local state variable to indicate if the alert should be visible or not. After that, we need to add a view modifier .alert that requires returning the Alert view.

@State private var showAlert = false

var body: some View {
  VStack {
    Button("Show alert") {
      showAlert = true
    }
    .alert(isPresented: $showAlert) {
      Alert(title: Text("Title"))
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

In the previous example, we saw that the showAlert state variable keeps a bool value. At first, the alert isn't visible, and it is false. When we press the button, we need to change the state variable to true which triggers the alert to be visible on the screen.

Alert with SwiftUI

We can see that by default, the alert has an OK button to dismiss it. We will try to add more buttons later in this post.

For the Alert view, we can provide not only title but also a message field that is one under another.

Alert(title: Text("Title"),
      message: Text("Message"))
Enter fullscreen mode Exit fullscreen mode

Alert with message

Alert with identifiable item

Another way to present an alert in the SwiftUI is by using an identifiable view state variable. Using this approach SwiftUI layout system passes the value of this state variable, and we can populate the alert title, message, or event buttons using its contents. The bonus is that once this item changes, the SwiftUI layout system would update the alert with the new value, which is a pretty neat feature that we get for free.

Let's see how to do it in the code. At first, we need to set up an object that implements the Identifiable protocol. Let's create a user structure. We need to create an id constant, and we could use the UUID structure that represents a globally unique value that can identify values between them.

struct User: Identifiable {
  let id: UUID
  let name: String
}
Enter fullscreen mode Exit fullscreen mode

Next, let's move to the SwiftUI part and create our view.

@State private var user: User?

var body: some View {
  VStack {
    Button("Show alert") {
      user = User(id: UUID(), name: "Kristaps")
    }
    .alert(item: $user) { user in
      Alert(title: Text("Hello, \(user.name)!"))
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

It is important to note that the view state variable is optional because SwiftUI determines if the alert should be visible on the screen.

When using the .alert view modifier, we have the closure with an input variable which is the current value of the state variable. This way, we could populate, for instance, the alert title.

Alert with identifiable item

Alert Buttons

Now we know how to show an alert on the screen using SwiftUI with two different approaches. But how to show more than one button and customize them?

To add buttons for the alert, we could use either the Alert initializer specifying the dismiss button, or have the way to initialize the Alert, providing the primary and secondary button.

Let's create a confirmation alert that would ask our users if they want to delete something from a list.

@State private var showAlert = false

var body: some View {
  VStack {
    Button("Show alert") {
      showAlert = true
    }
    .alert(isPresented: $showAlert) {
      Alert(title: Text("Do you want to delete this item?"),
        primaryButton: .cancel(),
        secondaryButton: .destructive(Text("Delete")) {
          print("Delete item")
        }
      )
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Let's unwrap the code. To show the alert, we are using our first approach with the view state variable that indicates when we want to show the alert on the screen.

Inside the .alert view modifier where we are constructing the Alert representation, we see that we are using a different initializer to provide information about alert buttons. That is a specific button that represents an operation of an alert. There are four different styles of buttons we could use in SwiftUI:

  • default;
  • cancel - indicates cancellation with a system provided label;
  • cancel - similar like above, but with a custom label;
  • destructive - indicates destructive action, for example, deletion.

We are using the cancel and destructive button styles for our confirmation alert because our users could cancel or delete the item from a list.

Alert with buttons

Show multiple alerts

To show multiple alerts on a view, we have two ways.

Using state variables

The first is to keep a view state variable per each alert. But we need to add the .alert modifier, not for the full view, but we can add it, for example, for the button that invokes the alert. If we add two .alert modifiers one after another, the SwiftUI layout engine will show only the first one.

@State var firstAlertShown = false
@State var secondAlertShown = false

var body: some View {
  VStack(spacing: 50) {
    Button("Show first alert") {
      firstAlertShown = true
    }
    .alert(isPresented: $firstAlertShown) {
      Alert(title: Text("First alert"))
    }

    Button("Show second alert") {
      secondAlertShown = true
    }
    .alert(isPresented: $secondAlertShown) {
      Alert(title: Text("Second alert"))
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Using identifiable state variable

I like the second approach much better - it is more type safe. At first, we need to set up a structure representing the alert that conforms to the Identifiable protocol. Inside this structure, we have a local enumeration type representing all alerts types we would like to show and return the specific alert view. Ideally, it should be separated, but to show this example, I kept it like this.

struct AlertIdentifier: Identifiable {
  enum ActiveAlert {
    case first
    case second

    var alertView: Alert {
      switch self {
      case .first:
        return Alert(title: Text("First alert"))
      case .second:
        return Alert(title: Text("Second alert"))
      }
    }
  }

  var id: ActiveAlert
}
Enter fullscreen mode Exit fullscreen mode

Next, we want to create the SwiftUI view and use it. Our view would have the local state variable with our new AlertIdentifier type when invoking and constructing the alert. We can use it and the computed alertView property that we had built before inside the ActiveAlert enum.

struct MultipleAlertsViewIdentifiable: View {
  @State var alert: AlertIdentifier?

  var body: some View {
    VStack(spacing: 50) {
      Button("Show first alert") {
        alert = AlertIdentifier(id: .first)
      }

      Button("Show second alert") {
        alert = AlertIdentifier(id: .second)
      }
    }
    .alert(item: $alert) {
      $0.id.alertView
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

A lot to unpack here, but the main point is that it's safe to show multiple alerts on the same view without adding a state variable for each alert. Using this approach, we don't need to worry if our alert would be shown to the user because we don't have multiple .alert view modifiers scattered around the view itself.

TL;DR

Showing an alert to inform our app users or ask for confirmation is essential for the user interface. We can show the alert with SwiftUI in two ways. The first one is by using a state variable that indicates if the alert is visible. The second uses an identifiable state variable that can be used to populate the alert title, message, and buttons.

We can set up multiple types of buttons for the alert. If we would like to show more than two, we need to use the SwiftUI sheets.

To present multiple alerts in the same view, we can achieve it either by multiple view state variables or by using one identifiable structure that would be responsible for creating the alert itself.

Links

Top comments (0)