Delete Without Removing Row from List

An alternative solution to SwiftUI's onDelete

You have a list of items and you want to mark one of them as deleted. You reach for the onDelete modifier.

struct Record: Identifiable, Equatable {
    let id = UUID()
    let name: String
    var isDeleted: Bool
}

@State private var records: [Record] = [
    .init(name: "Jane", isDeleted: false),
    .init(name: "Tim", isDeleted: false),
    .init(name: "Sam", isDeleted: false),
]

var body: some View {
    List {
        ForEach(records) { record in
            LabeledContent(
                record.name,
                value: record.isDeleted ? "❌" : "✅"
            )
        }
        .onDelete { indexSet in
            for index in indexSet {
                records[index].isDeleted = true
            }
        }
    }
}

❌ This causes your row to be removed from the list temporarily, not a nice experience.

Let’s try with swipeActions

var body: some View {
    List {
        ForEach(records) { record in
            LabeledContent(
                record.name,
                value: record.isDeleted ? "❌" : "✅"
            )
            .swipeActions(
                edge: .trailing,
                allowsFullSwipe: true,
                content: {
                    Button("Delete", role: .destructive) {
                        let index = records.firstIndex(of: record)!
                        records[index].isDeleted = true
                    }
                }
            )
        }
    }
}

❌ Unfortunately this causes the same problem

What if we remove the button’s role and tint it red?

Button("Delete") {
    let index = records.firstIndex(of: record)!
    records[index].isDeleted = true
}
.tint(.red)

✅ Works perfectly

One tiny improvement is to not allow swipe to delete for an already deleted record. This requires wrapping our delete button in a conditional check.

if record.isDeleted == false {
    Button("Delete") {
        let index = records.firstIndex(of: record)!
        records[index].isDeleted = true
    }
    .tint(.red)
}

Perfect