SwiftUI Patterns

Confirmation Dialogs & Swipe Actions

June 15, 2026
5 min read
Featured image for blog post: Confirmation Dialogs & Swipe Actions

Destructive actions need confirmation. Here's how to combine swipe actions with confirmation dialogs.

Follow along with the code: iOS-Practice on GitHub

Swipe Actions

Add contextual actions to list rows:

ForEach(products) { product in
    ProductRow(product: product)
        .swipeActions(edge: .trailing) {
            Button(role: .destructive) {
                productToDelete = product
                showDeleteConfirmation = true
            } label: {
                Label("Delete", systemImage: "trash")
            }
        }
}

Swipe Action Positioning

// Trailing edge (swipe left)
.swipeActions(edge: .trailing) { ... }

// Leading edge (swipe right)
.swipeActions(edge: .leading) { ... }

// Both sides
.swipeActions(edge: .trailing) { ... }
.swipeActions(edge: .leading) { ... }

Confirmation Dialog

@State private var productToDelete: Product?
@State private var showDeleteConfirmation = false

.confirmationDialog(
    "Delete Product",
    isPresented: $showDeleteConfirmation,
    presenting: productToDelete
) { product in
    Button("Delete \(product.name)", role: .destructive) {
        deleteProduct(product)
    }
    Button("Cancel", role: .cancel) {}
} message: { product in
    Text("Are you sure you want to delete \(product.name)?")
}

Key Components

1. State for tracking item to delete

@State private var productToDelete: Product?

2. State for showing dialog

@State private var showDeleteConfirmation = false

3. Swipe action sets both

.swipeActions(edge: .trailing) {
    Button(role: .destructive) {
        productToDelete = product
        showDeleteConfirmation = true
    } label: {
        Label("Delete", systemImage: "trash")
    }
}

4. Dialog uses presenting

.confirmationDialog(
    "Title",
    isPresented: $showDeleteConfirmation,
    presenting: productToDelete
) { product in
    // product is guaranteed non-nil here
}

Multiple Swipe Actions

.swipeActions(edge: .trailing) {
    Button(role: .destructive) {
        delete(item)
    } label: {
        Label("Delete", systemImage: "trash")
    }

    Button {
        archive(item)
    } label: {
        Label("Archive", systemImage: "archivebox")
    }
    .tint(.orange)
}

Full Swipe to Delete

.swipeActions(edge: .trailing, allowsFullSwipe: true) {
    Button(role: .destructive) {
        delete(item)
    } label: {
        Label("Delete", systemImage: "trash")
    }
}

allowsFullSwipe: true enables full swipe to trigger the first action.

Confirmation After Action

Show feedback after deletion:

private func deleteProduct(_ product: Product) {
    products.removeAll { $0.id == product.id }
    alertMessage = "Deleted: \(product.name)"
    showAlert = true
}

.alert("Deleted", isPresented: $showAlert) {
    Button("OK") {}
} message: {
    Text(alertMessage)
}

Interview Tip

This pattern shows understanding of:

  • Data-driven UI state
  • Proper confirmation flow
  • Role-based button styling
  • SwiftUI presentation coordination

Originally published on pixelper.com

© 2026 Christopher Moore / Dead Pixel Studio

Let's work together

Professional discovery, design, and complete technical coverage for your ideas

Get in touch