Search code examples
core-dataswiftuinsmanagedobjectnsmanagedobjectcontextnsmanagedobjectmodel

CoreData bug in Xcode 12.1


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")
                }
            }
        }
    }
 }

Solution

  • 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)")
                }
            })
        }
    }