Deleting Rows in List : SwiftUI

DevTechie Inc
May 11, 2023

Swipe to delete is supported in List via ForEach. In this article, we will add support for Swipe to delete items from the list.

We will start with data model for our view. We will use DevTechie video course list for this example.

struct DevTechieCourse: Identifiable {
    let id = UUID()
    var name: String
    var category: String
    var color: Color {
        if category == "SwiftUI" {
            return .indigo
        } else {
            return .mint
        }
    }
}

Let’s add some data to work with

extension DevTechieCourse {
    static var sample: [DevTechieCourse] {
        [
            .init(name: "Mastering WidgetKit iOS 16", category: "SwiftUI"),
            .init(name: "Practical iOS 16", category: "UIKit"),
            .init(name: "New in SwiftUI: Charts", category: "SwiftUI"),
            .init(name: "Complete Machine Learning", category: "UIKit")
        ]
    }
}

For our view, we will add a State variable which will hold DevTechie courses.

@State private var courses = DevTechieCourse.sample

In the view body, we will add a List and inside the List, we will add ForEach to iterate over the courses in the array. ForEach conforms to the RandomAccessCollection protocol meaning accessing elements at various indices is efficient via ForEach.

RandomAccessCollection: A collection that supports efficient random-access index traversal.

List {
    ForEach(courses) { course in
        VStack(alignment: .leading, spacing: 5) {
            Text(course.name)
                .font(.title3)
            Text(course.category)
                .padding(.horizontal, 5)
                .font(.caption)
                .foregroundColor(.white)
                .background(course.color, in: RoundedRectangle(cornerRadius: 5))
        }
    }
}

We will add a private function to handle delete functionality.

private func deleteItem(at indexSet: IndexSet) {
    courses.remove(atOffsets: indexSet)
}

Let’s add support for swipe to delete of a row with onDelete(perform:) at ForEach level.

.onDelete(perform: deleteItem(at:))

Last but not the least, we want to show an empty state for the view when all courses have been removed from the list.

if courses.isEmpty {
    Text("Nothing to show here")
        .font(.largeTitle)
        .foregroundStyle(Color.secondary.shadow(.drop(radius: 2)))
}

Here is how the complete code will look like.

struct DevTechieCourse: Identifiable {
    let id = UUID()
    var name: String
    var category: String
    var color: Color {
        if category == "SwiftUI" {
            return .indigo
        } else {
            return .mint
        }
    }
}extension DevTechieCourse {
    static var sample: [DevTechieCourse] {
        [
            .init(name: "Mastering WidgetKit iOS 16", category: "SwiftUI"),
            .init(name: "Practical iOS 16", category: "UIKit"),
            .init(name: "New in SwiftUI: Charts", category: "SwiftUI"),
            .init(name: "Complete Machine Learning", category: "UIKit")
        ]
    }
}struct DevTechieDeleteFromListExample: View {
    
    @State private var courses = DevTechieCourse.sample
    
    var body: some View {
        NavigationStack {
            Group {
                if courses.isEmpty {
                    Text("Nothing to show here")
                        .font(.largeTitle)
                        .foregroundStyle(Color.secondary.shadow(.drop(radius: 2)))
                } else {
                    List {
                        ForEach(courses) { course in
                            VStack(alignment: .leading, spacing: 5) {
                                Text(course.name)
                                    .font(.title3)
                                Text(course.category)
                                    .padding(.horizontal, 5)
                                    .font(.caption)
                                    .foregroundColor(.white)
                                    .background(course.color, in: RoundedRectangle(cornerRadius: 5))
                            }
                        }
                        .onDelete(perform: deleteItem(at:))
                    }
                }
            }
            .navigationTitle("DevTechie Courses")
        }
    }
    
    private func deleteItem(at indexSet: IndexSet) {
        courses.remove(atOffsets: indexSet)
    }
}

Build and run