Added support for todo deletion to the TaskListView view in the app target.

This commit is contained in:
2026-02-11 17:34:06 +01:00
parent 8324a64b24
commit 1debdcd322
2 changed files with 78 additions and 24 deletions
@@ -18,17 +18,24 @@ extension TaskListView {
// MARK: Type aliases
/// Closure type for handling task deletion operations.
///
/// - Parameters:
/// - todos: The current array of todo tasks.
/// - offsets: The index set of tasks to delete.
typealias DeleteClosure = ([Todo], IndexSet) -> Void
/// Closure type for handling task reorder operations.
///
/// - Parameters:
/// - tasks: The current array of tasks.
/// - todos: The current array of todo tasks.
/// - source: The index set of items being moved.
/// - destination: The destination index for the moved items.
typealias ReorderClosure = ([Todo], IndexSet, Int) -> Void
// MARK: Properties
/// Query to fetch todos filtered by group and completion status.
/// Query to fetch todo tasks filtered by group and completion status.
///
/// This query is initialized with a todo fetch descriptor that applies predicates
/// based on the group type and completion filter. SwiftData automatically
@@ -37,7 +44,7 @@ extension TaskListView {
/// Tasks are sorted by their `order` property first, then by date-based criteria
/// specific to each group, enabling both manual and automatic ordering.
@Query
private var tasks: [Todo]
private var todos: [Todo]
/// Indicates whether tasks in this view can be reordered through drag and drop.
///
@@ -45,6 +52,12 @@ extension TaskListView {
/// determines whether the `.onMove` modifier is applied to the task list.
private let canReorder: Bool
/// Closure called when tasks are deleted through swipe-to-delete.
///
/// This closure is invoked by the `.onDelete` modifier and is responsible for
/// removing the selected tasks from the model context.
private let onDelete: DeleteClosure
/// Closure called when tasks are reordered through drag and drop.
///
/// This closure is invoked by the `.onMove` modifier and is responsible for
@@ -67,12 +80,14 @@ extension TaskListView {
/// - group: The todo group category to filter by (today, upcoming, overdue, or completed).
/// - showCompleted: Whether to include completed tasks in the results. Only applies to groups other than `.completed`.
/// - onReorder: A closure called when tasks are reordered.
/// - onDelete: A closure called when tasks are deleted.
init(
group: TodoGroup,
showCompleted: Bool,
onDelete: @escaping DeleteClosure,
onReorder: @escaping ReorderClosure
) {
self._tasks = .init(
self._todos = .init(
FetchDescriptor<Todo>(
group: group,
showCompleted: showCompleted
@@ -80,6 +95,7 @@ extension TaskListView {
)
self.canReorder = group.canReorder
self.onDelete = onDelete
self.onReorder = onReorder
}
@@ -97,7 +113,7 @@ extension TaskListView {
/// in read-only mode with chronological sorting.
var body: some View {
Group {
if tasks.isEmpty {
if todos.isEmpty {
ContentUnavailableView(
"view.task-list-content.content-unavailable.title",
systemImage: "checkmark.circle"
@@ -105,19 +121,7 @@ extension TaskListView {
}
else {
List {
if canReorder {
ForEach(tasks) {
TaskListView.Item($0)
}
.onMove { source, destination in
onReorder(tasks, source, destination)
}
}
else {
ForEach(tasks) {
TaskListView.Item($0)
}
}
items
}
.listStyle(.grouped)
}
@@ -127,6 +131,34 @@ extension TaskListView {
}
// MARK: - Helpers
private extension TaskListView.Content {
// MARK: Computed
/// A view that renders the task items with appropriate modifiers.
@ViewBuilder
var items: some View {
let items = ForEach(todos) {
TaskListView.Item($0)
}
.onDelete { offsets in
onDelete(todos, offsets)
}
if canReorder {
items.onMove { source, destination in
onReorder(todos, source, destination)
}
}
else {
items
}
}
}
// MARK: - Previews
#Preview(
@@ -136,7 +168,9 @@ extension TaskListView {
TaskListView.Content(
group: .today,
showCompleted: .random()
) { _, _, _ in
) { _, _ in
// On delete closure
} onReorder: { _, _, _ in
// On reorder closure
}
}
@@ -148,7 +182,9 @@ extension TaskListView {
TaskListView.Content(
group: .today,
showCompleted: false
) { _, _, _ in
) { _, _ in
// On delete closure
} onReorder: { _, _, _ in
// On reorder closure
}
}
@@ -160,7 +196,9 @@ extension TaskListView {
TaskListView.Content(
group: .today,
showCompleted: true
) { _, _, _ in
) { _, _ in
// On delete closure
} onReorder: { _, _, _ in
// On reorder closure
}
}
@@ -172,7 +210,9 @@ extension TaskListView {
TaskListView.Content(
group: .completed,
showCompleted: .random()
) { _, _, _ in
) { _, _ in
// On delete closure
} onReorder: { _, _, _ in
// On reorder closure
}
}
+16 -2
View File
@@ -9,6 +9,10 @@ struct TaskListView: View {
// MARK: Properties
/// The SwiftData model context for performing data operations.
@Environment(\.modelContext)
private var modelContext
/// The view model managing the state and business logic for this view.
///
/// This view model holds the group configuration and completion filter state.
@@ -40,8 +44,18 @@ struct TaskListView: View {
Content(
group: viewModel.group,
showCompleted: viewModel.showCompleted
) { tasks, source, destination in
viewModel.reorderTasks(tasks, from: source, to: destination)
) { tasks, offsets in
viewModel.deleteTasks(
tasks,
at: offsets,
from: modelContext
)
} onReorder: { tasks, source, destination in
viewModel.reorderTasks(
tasks,
from: source,
to: destination
)
}
.navigationTitle(viewModel.navigationTitle)
.toolbar {