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