Reputation: 310
I am building a small app using SwiftUI and CoreData with the SwiftUI App life cycle(no scene or app delegate). I'm getting the following error when I Run+Build my app:
'executeFetchRequest:error: A fetch request must have an entity.'
I've checked/verified/re-checked the following:
[app name].xcdatamodeld
file name is the same as what I pass into the NSPersistentContainer NSPersistentCloudKitContainer(name: [app name])
Car
is exactly what I pass into the FetchRequest@FetchRequest(entity: Car.entity(), sortDescriptors: []) var car: FetchedResults<Car>
public class Car: NSManagedObject {}
with an extension on Car of Identifiable
.
Here is my whole view struct that should be(to my understanding) passing the environment around to all of its "child" views.
struct AppView: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: Car.entity(), sortDescriptors: []) var car: FetchedResults<Car>
var body: some View {
List {
ForEach(car, id:\.self) { item in
RootView(carData: item)
.environment(\.managedObjectContext, self.moc)
}
}
}
}
and my @main struct
@main
struct AutoMateApp: App {
@StateObject var coreData = PersistentCloudKitContainer()
let persistence = PersistentCloudKitContainer()
var body: some Scene {
WindowGroup {
AppView()
.environment(\.managedObjectContext, coreData.persistentContainer.viewContext)
}
}
}
When I step through with the debugger the crash seems to appear once the WindowGroup is returned. I'm not sure if that's helpful information at all.
I appreciate all your help, thank you.
Upvotes: 5
Views: 5259
Reputation: 119242
I encountered this issue today and I think the problem comes from when things are declared and ordered. Removing the entity from the fetch request (which presumably allows it to do things by type inference, at a later point in the lifecycle) removed the problem:
@FetchRequest(sortDescriptors: [...], predicate: ...)
private var things: FetchedResults<Thing>
It seems possible that Car.entity()
is evaluated before the environment context is created and injected when you are setting up the preview.
Upvotes: 3
Reputation: 9
Replace Car.entity()
to static NSEntityDescription.
example
struct AppView: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: AppView_Previews.entity, sortDescriptors: []) var car: FetchedResults<Car>
var body: some View {
List {
ForEach(car, id:\.self) { item in
RootView(carData: item)
.environment(\.managedObjectContext, self.moc)
}
}
}
}
struct AppView_Previews: PreviewProvider {
static var entity: NSEntityDescription {
return NSEntityDescription.entity(forEntityName: "Car", in: yourViewContext)!
}
}
Upvotes: 0
Reputation: 30549
Try the code from the SwiftUI app project template when Core Data support is checked:
MyApp.swift
import SwiftUI
@main
struct MyApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
Persistance.swift
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
for _ in 0..<10 {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
}
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "MyApp")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
}
Upvotes: 1
Reputation: 903
Okay so removing the lazy
from the line lazy var persistentContainer: NSPersistentCloudKitContainer = {...}()
in the AppDelegate
helped in my case. Because it seemed that Apples boilerplate code handed of the NSManagedObjectModel
in the line let contentView = ContentView().environment(\.managedObjectContext, persistentContainer.viewContext)
and the FetchRequest accessed that object before it was properly instantiated by the closure and therefore would not be able to find the NSManagedObject subclass
. Removing the lazy
from the variable executes the closure right away.
Hope this still helps you and solves the problem in your case.
Upvotes: 2