[SwiftUI] Alerts Can Now Be Displayed Without Bool in Xcode 27

[SwiftUI] Alerts Can Now Be Displayed Without Bool in Xcode 27

Tried SwiftUI's new alert API in Xcode 27. Just pass an Optional Binding to show it, no Bool flag needed. Summarizing differences from the old approach and key implementation points.
2026.10.11

This page has been translated by machine translation. View original

Introduction

In Xcode 27 (iOS 27.0 SDK), an API was added to SwiftUI's alert that allows you to display an alert simply by passing an Optional Binding. These are alert(error:) for errors and alert(_:item:) for passing arbitrary values.

Previously, it was necessary to separately maintain a Bool display flag, so let's compare how this changes things against the traditional approach.

Verification Environment

  • Xcode 27
  • iOS 27.0 Simulator

Displaying Errors

Traditional Approach

alert(isPresented:error:), which displays an alert by passing a LocalizedError, required a Binding<Bool> display flag.

struct ContentView: View {
    @State private var isPresented = false
    @State private var error: SampleLocalizedError?

    var body: some View {
        Button("Load") {
            Task {
                do {
                    try await load()
                } catch let error as SampleLocalizedError {
                    self.error = error
                    isPresented = true
                } catch {}
            }
        }
        .alert(isPresented: $isPresented, error: error) { _ in
            Button("OK") {}
        } message: { error in
            Text(error.recoverySuggestion ?? "")
        }
    }
}

You needed to maintain two State values — the error value and the Bool — and update both. When dismissed, the Bool would return to false, but the error value would remain.

Approach from Xcode 27 Onwards

Simply pass an Optional Binding of the error to alert(error:).

struct ContentView: View {
    @State private var error: SampleLocalizedError?

    var body: some View {
        Button("Load") {
            Task {
                do {
                    try await load()
                } catch let error as SampleLocalizedError {
                    self.error = error
                } catch {}
            }
        }
        .alert(error: $error) { _ in
            Button("OK") {}
        } message: { error in
            Text(error.recoverySuggestion ?? "")
        }
    }
}

State is now just a single error value. Assigning a value shows the alert, and when dismissed, it automatically returns to nil. Not having to manage a Bool is a small but welcome improvement.

The title uses errorDescription, just as before. The body is assembled manually using message:.

Passing a Raw Error

alert(error:) can also accept Error types that do not conform to LocalizedError. Since you can pass an any Error received from catch directly, I tried it out.

@State private var error: (any Error)?

// ...
catch {
    self.error = error
}

.alert(error: $error) {
    Button("OK") {}
}

The title displayed the error's localizedDescription as-is. When I passed URLError(.notConnectedToInternet), the content was as follows:

  • The operation couldn't be completed. (NSURLErrorDomain error -1009.)

Since this is developer-facing language, it feels difficult to use as-is on a screen shown to users (though I wonder when you would actually use it).
In practice, I think most cases involve conforming to LocalizedError and defining the message text yourself.

Displaying with Arbitrary Values

Traditional Approach

alert(_:isPresented:presenting:), which passes values other than errors, also required a Bool display flag.

@State private var isPresented = false
@State private var target: String?

Button("Delete") {
    target = "Sample"
    isPresented = true
}
.alert("Confirmation", isPresented: $isPresented, presenting: target) { _ in
    Button("OK") {}
} message: { target in
    Text(target)
}

Approach from Xcode 27 Onwards

Pass an Optional Binding to alert(_:item:).

@State private var target: String?

Button("Delete") {
    target = "Sample"
}
.alert("Confirmation", item: $target) { _ in
    Button("OK") {}
} message: { target in
    Text(target)
}

This also returns to nil when dismissed.

While sheet(item:) requires conformance to Identifiable, alert(_:item:) does not. You can pass a String directly, as in the code above.

confirmationDialog(_:item:) also has the same style of API added.

Value Changes While Displayed Are Not Reflected

Even if you rewrite the value while the alert is displayed, the content of the alert does not change. This is the same for both alert(error:) and alert(_:item:).

Apple's documentation for alert(_:item:) states that changes after display are ignored. While alert(error:) documentation does not mention this, the same behavior was confirmed in the verification environment (Xcode 27 / iOS 27.0 Simulator), so caution is warranted.

Works on Older OS Versions

Looking at Apple's documentation, alert(error:) and alert(_:item:) support iOS 15.0 and later.

Therefore, building with Xcode 27 means these APIs can be used even in apps supporting older OS versions.

However, the API that accepts a raw Error not conforming to LocalizedError appears to only be available on iOS 27 and later.

Summary

  • alert(error:) and alert(_:item:) were added in Xcode 27
  • The Bool display flag is no longer needed; simply assigning an Optional value displays the alert
  • When dismissed, it automatically returns to nil, so no cleanup code is needed
  • Rewriting the value while the alert is displayed does not change the alert's content
  • alert(error:) can accept a raw Error, but developer-facing language appears as-is in the title
  • Unlike sheet(item:), alert(_:item:) does not require Identifiable
  • Building with Xcode 27 allows use from iOS 15 onwards (iOS 27 onwards when passing a raw Error)

References

https://developer.apple.com/documentation/updates/swiftui

https://developer.apple.com/documentation/swiftui/view/alert(error:actions:)

https://developer.apple.com/documentation/swiftui/view/alert(_:item:actions:)

https://developer.apple.com/jp/documentation/swiftui/view/confirmationdialog(_:item:titlevisibility:actions:)

Share this article