0
votes

I am working through Paul Hudson's 100 Days of SwiftUI and on Project 11 have hit a frustrating issue with CoreData. This is a direct lift of Paul's code that compiles and runs fine in his video. The Bookworm.xcdatamodeld has a single entity named Student that has two attributes: a UUID named id and a String named name.

It compiles fine, but running it results in a crash on the ForEach, with 'students' underlined in red. The error message that pops up in the console says:

2020-10-31 12:13:47.934507-0400 Bookworm[614:7766183] [error] error: No NSEntityDescriptions in any model claim the NSManagedObject subclass 'Bookworm.Student' so +entity is confused. Have you loaded your NSManagedObjectModel yet ? CoreData: error: No NSEntityDescriptions in any model claim the NSManagedObject subclass 'Bookworm.Student' so +entity is confused. Have you loaded your NSManagedObjectModel yet ? 2020-10-31 12:13:47.934651-0400 Bookworm[614:7766183] [error] error: +[Bookworm.Student entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass CoreData: error: +[Bookworm.Student entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass 2020-10-31 12:13:47.953419-0400 Bookworm[614:7766183] [SwiftUI] Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x6000008d0820>

I have searched a ton, and tried every recommended solution that I have found including: simply closing and reopening Xcode (Step 1), cleaning the project and then repeating Step 1, and deleting all the derived data and repeating Step 1. I have verified that Current Product Module is selected in the inspector for the Module, and that Codegen has Class Definition selected.

import SwiftUI
import CoreData

struct ContentView: View {

    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: Student.entity(), sortDescriptors: []) var students: FetchedResults<Student>

    var body: some View {
        VStack {
            List {
                ForEach(students, id: \.id) { student in
                    Text(student.name ?? "Unknown")
                }
            }
        }
    }
 }
1
Where do you initialize the managedObjectContext?Nikos Polychronakis
According to the tutorial: We don’t need to create this managed object context, because Xcode already made one for us. Even better, it already added it to the SwiftUI environment, which is what makes the @ FetchRequest property wrapper work – it uses whatever managed object context is available in the environment. When it comes to adding and saving objects, we need access to the managed object context that it is in SwiftUI’s environment. This is another use for the @ Environment property wrapper – we can ask it for the current managed object context, and assign it to a property for our use.Scooter
If you did use Xcode template for core data this is accurate. If you didn't, you'll have to implement it yourself. Check SceneDelegate file, if there is a line like this: window.rootViewController = UIHostingController(rootView: contentView) (If you uploaded the app to github, send me a link to check the code.)Nikos Polychronakis
I'm using the SwiftUI lifecycle so there is no SceneDelegate file.Scooter
The entry point of your app still needs to add the CoreData stack to the environment. This is done in the struct marked with @Main and should look something like this: ContentView().environment(\.managedObjectContext, persistenceController.container.viewContext) Have you done that?Dan O'Leary

1 Answers

2
votes

If you are using SwiftUI lifecycle, you should initialize NSPersistentContainer in a parent View (or App) and import managedObjectContext to the environment.

In your case, it could be something like this:

import SwiftUI
import CoreData

@main
struct coreDataParadigmApp: App {
    let persistenceController = PersistenceController.shared
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
        }
    }
}


struct ContentView: View {
    @FetchRequest(entity: Student.entity(), sortDescriptors: []) var students: FetchedResults<Student>
    var body: some View {
        VStack {
            List {
                ForEach(students, id: \.id) { student in
                    Text(student.name ?? "Unknown")
                }
            }
        }
    }
 }

// DONT FORGET TO CHANGE THE NAME OF YOUR FILE
struct PersistenceController {
    static let shared = PersistenceController()

    let container: NSPersistentContainer

    init() {
        container = NSPersistentContainer(name: "coreDataNameOfFile")

        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
    }
}