Filter @Published array in SwiftUI List removes elements in list

I am trying to implement a list functionality similar to to Handling User Input example, the interface shows a list the user can filter depending on boolean values. I want to add the following differences from the example:

  • Can edit list elements from the row itself
  • Move the filter logic to a ViewModel class

I've tried many approaches without success one of them:

  • ViewModel:
    class TaskListViewModel : ObservableObject  {
    
        private var cancelables = Set<AnyCancellable>()
    
        private var allTasks: [Task] =
            [ Task(id: "1",name: "Task1", description: "Description", done: false),
              Task(id: "2",name: "Task2", description: "Description", done: false)]
    
        @Published var showNotDoneOnly = false
    
        @Published var filterdTasks: [Task] = []

        init() {
        
            filterdTasks = allTasks

            $showNotDoneOnly.map { notDoneOnly in
                if notDoneOnly {
                    return self.filterdTasks.filter { task in
                        !task.done
                    }
                }
                return self.filterdTasks
            }.assign(to: \.filterdTasks, on: self)
            .store(in: &cancelables)
        }
    }
  • View:
struct TaskListView: View {
    
    @ObservedObject private var taskListViewModel = TaskListViewModel()
        
    var body: some View {
        
        NavigationView {
            VStack {
                Toggle(isOn: $taskListViewModel.showNotDoneOnly) {
                    Text("Undone only")
                }.padding()
                List {
                    ForEach(taskListViewModel.filterdTasks.indices, id: \.self) { idx in
                        TaskRow(task: $taskListViewModel.filterdTasks[idx])
                    }
                }
            }.navigationBarTitle(Text("Tasks"))
        }
    }
}
  • TaskRow:
struct TaskRow: View {
    
    @Binding var task: Task

    var body: some View {
        HStack {
            Text(task.name)
            Spacer()
            Toggle("", isOn: $task.done )
        }
    }

}

With this approach the list is filtered when the user enable the filter but when it is disabled the list lose the previously filtered elements. If I change the code to restore the filter elements like this:

    $showNotDoneOnly.map { notDoneOnly in
        if notDoneOnly {
            return self.filterdTasks.filter { task in
                !task.done
            }
        }
        return self.allTasks
    }.assign(to: \.filterdTasks, on: self)

The list lose the edited elements.

I've also tried making allTask property to a @Published dictionary by without success. Any idea on how to implement this? Is ther any better approach to do this in SwiftUi?

Thanks

Terms of Service

Privacy Policy

Cookie Policy