8

I'm trying to run a basic test SwiftUI app for macOS using Core Data, and I'm hitting a problem. When I use this in my view:

@FetchRequest(entity: Note.entity(), sortDescriptors: []) let notes: FetchedResults<Note>

The app crashes with the following errors:

NoteTaker [error] error: No NSEntityDescriptions in any model claim the NSManagedObject subclass 'NoteTaker.Note' so +entity is confused.  Have you loaded your NSManagedObjectModel yet ?
CoreData: error: No NSEntityDescriptions in any model claim the NSManagedObject subclass 'NoteTaker.Note' so +entity is confused.  Have you loaded your NSManagedObjectModel yet ?

NoteTaker [error] error: +[NoteTaker.Note entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
CoreData: error: +[NoteTaker.Note entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass

NoteTaker executeFetchRequest:error: A fetch request must have an entity.

Now, if I used the alternate form of FetchRequest, it then works fine though the code is much uglier:

// outside of the view
func getAllNotes() -> NSFetchRequest<Note> {
  let request: NSFetchRequest<Note> = Note.fetchRequest()
  request.sortDescriptors = []
  return request
}

// in the view
@FetchRequest(fetchRequest: getAllNotes()) var notes: FetchedResults<Note>

Also, if I turn this into an iOS app instead, then the entity version of the @FetchRequest works fine.

Any ideas?

Deano
  • 91
  • 3
  • 2
    Whilst writing this, I found that if I change the auto-generated persistantContainer code in AppDelegate so it's no longer lazy then it works. So it looks like it's an issue with the container not being instantiated before the entity style of fetchRequest is made. Whilst this is a fix, it's a bit janky as obviously we only want to handle the cost of instantiating the persistant container when we really need to, and not when the app loads (hence the point of it being lazy). Also it feels really bad that it's going against when Apple is auto-creating for you. – Deano Nov 11 '19 at 00:04
  • 1
    Did you ever get this resolved? – Daniel Dec 26 '19 at 14:24
  • 1
    What was the fix? – Oleg991 Dec 28 '19 at 11:22
  • 5
    Removing "lazy" below "// MARK: - Core Data stack" in the auto-generated AppDelegate.swift also worked for me. Wasted tons of hours with this. – Florian Schulz Feb 10 '20 at 14:27
  • 1
    If you think about it, this makes perfect sense. The app delegate creates ContentView() before then modifying it with environment(\.managedObjectContext, persistentContainer.viewContext). So the persistent container is lazily created after ContentView is created with its `@FetchRequest` property. If you were to have a view created inside of the body that had an`@FetchRequest` property instead, that would work as well since body is called later. – Jason Bobier Apr 26 '20 at 12:35
  • I couldn't find a 'lazy' to remove (maybe the autogenerated code is different in Xcode 11.3.1 or in a document-based app) but the suggestion in @JasonBobier's comment works for me. Put the `@FetchRequest` into another view and create that view from the body of the ContentView. – Angela May 01 '20 at 21:53

3 Answers3

1

I was getting this error in several places in my code for fetching and dynamic fetching requests as well as when creating core data objects this is what worked for me:

During fetch requests I used this:

@FetchRequest(entity: NSEntityDescription.entity(forEntityName: "Your Entity Name", in: managedObjectContext), sortDescriptors: [NSSortDescriptor(key: "Your Key", ascending: true)])

In dynamic fetch requests I used this:

var entity: Entity
var fetchRequest: FetchRequest<Your Entity>
init(_ entity: Entity) {
self.entity = entity
self.fetchRequest = FetchRequest(entity: NSEntityDescription.entity(forEntityName: "Your Entity", in: managedObjectContext), sortDescriptors: [NSSortDescriptor(key: "Your Key", ascending: true)], predicate: NSPredicate(format: "entity.uniqueIdentifier == %@", entity.uniqueIdentifier!))
}

var youEntities: FetchedResults<Your Entity> {
fetchRequest.wrappedValue
}

When creating core data objects:

let entityDescription = NSEntityDescription.entity(forEntityName: "Your Entity", in: managedObjectContext)

let object = Your Entity(entity: entityDescription!, insertInto: managedObjectContext)
Dharman
  • 30,962
  • 25
  • 85
  • 135
raisin
  • 59
  • 2
  • Using `NSEntityDescription.entity(forEntityName: "Your Entity Name", in: managedObjectContext)` worked for me! – Kukiwon Mar 17 '21 at 20:51
-1

Use the Storyboard template and then set up the view using SwiftUI in the view controller.

override func viewWillAppear() {
        super.viewWillAppear()

        let context = (NSApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        let contentView = ContentView().environment(\.managedObjectContext, context)

        view = NSHostingView(rootView: contentView)
    }
-2

Adding this line: .environment(.managedObjectContext, context)

in the SceneDelegate.swift

at

// Create the SwiftUI view and set the context as the value for the managedObjectContext environment keyPath.
// Add `@Environment(\.managedObjectContext)` in the views that will need the context.
let contentView = ContentView().environment(\.managedObjectContext, context)