I have a SwiftUI + CoreData simple Todo app, and everything works properly, but my updateTodo function which is supposed to handle the click on a todo and turn in from done to undone and vice versa, isn't working properly. When I click on a todo nothing happens in the UI, but when I go a screen back and come back to the todos screen, I can see the UI change, also it does persist so when I close and open the app the change is being reflected in the app. So my problem is that the 'isDone' property is not being toggled in the UI in real-time, and only when the view reappears it actually shows the change that has been made.
ViewModel (CoreData) :
class TodoViewModel:ObservableObject {
let container: NSPersistentContainer
@Published var categories = [CategoryTodo]()
@Published var todos = [Todo]()
init() {
container = NSPersistentContainer(name: "UniversityProject")
container.loadPersistentStores { (description, error) in
if let error = error {
fatalError("Error: \(error.localizedDescription)")
}
}
}
//MARK: - Todos
func getTodos() {
let request = NSFetchRequest<Todo>(entityName: "Todo")
let sort = NSSortDescriptor(key: #keyPath(Todo.dateCreated), ascending: false)
request.sortDescriptors = [sort]
do {
try todos = container.viewContext.fetch(request)
} catch {
print("Error getting data. \(error)")
}
}
func addTodo(todo text: String, category categoryName:String) {
let newTodo = Todo(context: container.viewContext)
newTodo.todo = text
newTodo.category = categoryName
newTodo.id = UUID().uuidString
newTodo.isDone = false
newTodo.dateCreated = Date()
saveTodo()
}
func saveTodo() {
do {
try container.viewContext.save()
getTodos()
} catch let error {
print("Error: \(error)")
}
}
func deleteTodo(indexSet: IndexSet) {
let todoIndex = indexSet[indexSet.startIndex]
let object = todos[todoIndex]
container.viewContext.delete(object)
saveTodo()
}
func updateTodo(item: Todo) {
item.setValue(!item.isDone, forKey: "isDone")
saveTodo()
}
}
TodosView:
struct TodosView: View {
@EnvironmentObject var viewModel: TodoViewModel
let categoryName:String
var body: some View {
List {
ForEach(viewModel.todos.filter{$0.category == categoryName}) { item in
TodoItem(item: item)
.onTapGesture {
withAnimation(.linear) {
viewModel.updateTodo(item: item)
}
}
}
.onDelete(perform: viewModel.deleteTodo)
}.onAppear { viewModel.getTodos() }
.navigationBarTitle(categoryName)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
HStack {
EditButton()
NavigationLink(destination: AddTodoView(category: categoryName)) {
Image(systemName: "plus.circle")
.resizable()
.foregroundColor(.blue)
.frame(width: 25, height: 25)
}
}
}
}
}
}