0

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?

4
  • Does this answer your question stackoverflow.com/a/61435489/12299030, stackoverflow.com/a/61431459/12299030? Commented Dec 23, 2020 at 16:36
  • @Asperi not really, this solution requires you to create a binding while I don't want a binding, but simply to pass a variable to another view. Commented Dec 23, 2020 at 16:44
  • self.viewModel.updateViewState() is UIKit thinking. In SwiftUI you need to create variables that manage the state automatically. medium.com/flawless-app-stories/… Commented Dec 23, 2020 at 17:15
  • Fair enough, but even without all that code the foreach crashes on removal of the last element in the rooms list in the viewmodel. Commented Dec 23, 2020 at 17:32

1 Answer 1

0

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)
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.