I am learning SwiftUI at the moment and ran into this problem which I can't seem to fix. There might be a very simple solution. Essentially, I have a viewmodel which contains a list of NSManagedObjects (Room):
class RoomViewModel: ObservableObject, ViewModelProtocol {
enum ViewState {
case Loading
case Normal
case NoData
}
@Published var viewState: ViewState = .Loading
@Published var rooms: [Room] = []
// Get all the rooms
func getRooms() {
viewState = .Loading
rooms = CoreDataManager.shared.getRooms()
updateViewState()
}
// MARK: Update the view state
func updateViewState() {
viewState = rooms.count == 0 ? .NoData : .Normal
}
}
On deletion of the last element from the rooms list in the viewmodel the foreach crashes:
if viewModel.rooms.count > 0 {
List {
ForEach(Array(viewModel.rooms.enumerated()), id: \.self.element.id) { index, room in
RoomRow(room: self.viewModel.rooms[index], viewModel: viewModel, index: index)
}.listRowBackground(Color.backgroundColor)
}
} else {
NoData()
}
When I delete a room this is the code that runs:
PersistenceController.shared.container.viewContext.delete(self.viewModel.rooms[selectedIndex])
self.viewModel.rooms.remove(at: selectedIndex)
PersistenceController.shared.saveContext()
The app then crashes on the ForEach loop in the view stating that the index was out of bounds. Is this some async problem, where the viewState updates before the element is deleted from the list in the viewmodel?
I found the solution to my problem. The problem appears to be that the ForEach is trying to look at the difference between the previous and the new data and is trying to grab deleted core data objects. I fixed the problem by changing the ForEach loop to check if the object we are looking at has been deleted
ForEach(Array(viewModel.rooms.enumerated()), id: \.self.element.id) { index, room in
if room.isFault {
EmptyView()
} else {
RoomRow(room: self.viewModel.rooms[index], viewModel: viewModel, index: index)
}
}