0

I'm trying to use a ViewModel between the ContentView and Core Data in SwiftUI. Xcode builder runs the App but I get an immediate error: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) for var recList.

Can anyone help?

Following a simple example of what I'm doing:

ListViewModel:

class ListViewModel: ObservableObject {

    var recRequest: FetchRequest<Newdb>
    var recList: FetchedResults<Newdb>{recRequest.wrappedValue}    <-------- error appears here

    @Published var records = [ViewModel]()

    init() {
        self.recRequest = FetchRequest(entity: Newdb.entity(), sortDescriptors: [])
        fetchEntries()
    }


    func fetchEntries() {

        self.records = recList.map(ViewModel.init)
    }

}

ViewModel:

class ViewModel {

    var name: String = ""

    init(db: Newdb) {
        self.name = db.name!
    }

}

ContentView:

struct ContentView: View {

        @ObservedObject var listViewModel: ListViewModel

        init() {
            self.listViewModel = ListViewModel()
        }


    var body: some View {


        ForEach(listViewModel.records, id: \.name) { index in

            Text(index.name)
        }



    }
}
schnitz
  • 33
  • 4

2 Answers2

0

two things I noticed; your ListViewModel is an ObservableObject but you do not have any @Published var ... Also when creating a class such as ListViewModel you cannot use "recRequest" as you do in recList, because it is not created yet. It is created in the init() method not before. Do your "recList = FetchedResults{recRequest.wrappedValue}" somewhere else, like in the fetchEntries().

  • Many thanks for your answer. I changed var records = [ViewModel]() to @Published var records = [ViewModel](). No, recList doesn't appear in any other class/view etc. That is all code I created. But if I just move the "recList = FetchedResults{recRequest.wrappedValue}" to the init() part I get still the same error. Any other idea? – schnitz Apr 25 '20 at 23:49
  • don't get me wrong, I was not suggesting you put your code in init(). In fact I strongly recommend you do not. Try something like a static func createListViewModel(...) to create an instance of your class with a callback in case there was an error in the fetching. When you do all this in init() you never know if there was and error or if the object is ready when you want to use it. – workingdog support Ukraine Apr 26 '20 at 00:08
  • Thanks again for your answer. After I tried several things I can't find any solution. Are you able to provide me an example of how to do it? – schnitz Apr 26 '20 at 22:47
0

From what I can tell, FetchRequest is a property wrapper.

It is supposed to wrap something, e.g.;

@FetchRequest(
    entity: User.entity(),
    sortDescriptors: []
) var users: FetchedResults<User> // users are 'wrapped' in a FetchRequest instance

It makes sense that wrappedValue is nil because there's nothing to be wrapped in

self.recRequest = FetchRequest(entity: Newdb.entity(), sortDescriptors: [])

You might want to double-check its usage.

Jim lai
  • 1,224
  • 8
  • 12