CoreData und SwiftUI: Der Kontext in der Umgebung ist nicht mit einem persistenten Speicherkoordinator verbunden

10

Ich versuche, mir Core Data beizubringen, indem ich eine App zum Verwalten von Hausaufgaben erstelle. Mein Code funktioniert einwandfrei und die App läuft einwandfrei, bis ich versuche, der Liste eine neue Zuordnung hinzuzufügen. Ich erhalte diesen Fehler Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1c25719e8)in der folgenden Zeile : ForEach(courses, id: \.self) { course in. Die Konsole hat auch diesen Fehler : Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x2823cb3a0>.

Ich weiß sehr wenig über Core Data und weiß nicht, was das Problem sein könnte. Ich habe im Datenmodell die Entitäten "Zuweisung" und "Kurs" eingerichtet, wobei der Kurs eine Eins-zu-Viele-Beziehung zur Zuweisung hat. Jede Aufgabe wird einem bestimmten Kurs zugeordnet.

Dies ist der Code für die Ansicht, die der Liste eine neue Zuordnung hinzufügt:

    struct NewAssignmentView: View {

    @Environment(\.presentationMode) var presentationMode
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: Course.entity(), sortDescriptors: []) var courses: FetchedResults<Course>

    @State var name = ""
    @State var hasDueDate = false
    @State var dueDate = Date()
    @State var course = Course()

    var body: some View {
        NavigationView {
            Form {
                TextField("Assignment Name", text: $name)
                Section {
                    Picker("Course", selection: $course) {
                        ForEach(courses, id: \.self) { course in
                            Text("\(course.name ?? "")").foregroundColor(course.color)
                        }
                    }
                }
                Section {
                    Toggle(isOn: $hasDueDate.animation()) {
                        Text("Due Date")
                    }
                    if hasDueDate {
                        DatePicker(selection: $dueDate, displayedComponents: .date, label: { Text("Set Date:") })
                    }
                }
            }
            .navigationBarTitle("New Assignment", displayMode: .inline)
            .navigationBarItems(leading: Button(action: {
                self.presentationMode.wrappedValue.dismiss()
            }, label: { Text("Cancel") }),
                                trailing: Button(action: {
                                    let newAssignment = Assignment(context: self.moc)
                                    newAssignment.name = self.name
                                    newAssignment.hasDueDate = self.hasDueDate
                                    newAssignment.dueDate = self.dueDate
                                    newAssignment.statusString = Status.incomplete.rawValue
                                    newAssignment.course = self.course
                                    self.presentationMode.wrappedValue.dismiss()
                                }, label: { Text("Add").bold() }))
        }
    }
}

BEARBEITEN: Hier ist der Code in AppDelegate, der den persistenten Container einrichtet:

lazy var persistentContainer: NSPersistentCloudKitContainer = {
    let container = NSPersistentCloudKitContainer(name: "test")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

Und der Code in SceneDelegate, der die Umgebung einrichtet:

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

    // Get the managed object context from the shared persistent container.
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

    // 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)

    // Use a UIHostingController as window root view controller.
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }
}
Kevin Olmats
quelle
Wo fügen Sie den Kontext des verwalteten Objekts zur Umgebung hinzu? Wie wird dieser verwaltete Objektkontext erstellt? Es scheint, dass Sie es nicht mit einem beständigen Geschäftskoordinator verbunden haben,
Paulw11
Ich habe den Code hinzugefügt, in dem ich den Moc in meinem ursprünglichen Beitrag für Sie zur Umgebung hinzufüge.
Kevin Olmats
@ KevinOlmats Hat meine Antwort geholfen?
Fulvio
Überprüfen Sie, ob Sie einen Kontext über die Umgebung zugewiesen haben.environment(\.managedObjectContext, viewContext)
onmyway133
@ onmyway133 das ist die richtige Antwort
Kevin Olmats

Antworten:

8

Sie speichern den Kontext nicht wirklich. Sie sollten Folgendes ausführen:

let newAssignment = Assignment(context: self.moc)
newAssignment.name = self.name
newAssignment.hasDueDate = self.hasDueDate
newAssignment.dueDate = self.dueDate
newAssignment.statusString = Status.incomplete.rawValue
newAssignment.course = self.course

do {
    try self.moc.save()
} catch {
    print(error)
}

Auch du @FetchRequest(...)könntest so aussehen:

@FetchRequest(fetchRequest: CourseItem.getCourseItems()) var courses: FetchedResults<CourseItem>

Sie können Ihre CourseItemKlasse so ändern , dass sie sortDescriptorswie folgt behandelt wird:

public class CourseItem: NSManagedObject, Identifiable {
    @NSManaged public var name: String?
    @NSManaged public var dueDate: Date?
    // ...etc
}

extension CourseItem {
    static func getCourseItems() -> NSFetchRequest<CourseItem> {
        let request: NSFetchRequest<CourseItem> = CourseItem.fetchRequest() as! NSFetchRequest<CourseItem>

        let sortDescriptor = NSSortDescriptor(key: "dueDate", ascending: true)

        request.sortDescriptors = [sortDescriptor]

        return request
    }
}

Dann würden Sie ForEach(...)Folgendes wie folgt ändern und können auch das Löschen von Elementen ganz einfach handhaben:

ForEach(self.courses) { course in
    // ...
}.onDelete { indexSet in
    let deleteItem = self.courses[indexSet.first!]
    self.moc.delete(deleteItem)

    do {
        try self.moc.save()
    } catch {
        print(error)
    }
}

Sie möchten sicherstellen, dass der "Klassenname" auf "CourseItem" gesetzt ist, was der CourseItemzuvor erstellten Klasse entspricht.

Klicken Sie einfach in Ihrer .xcdatamodeIdDatei auf ENTITIES und stellen Sie alles auf Folgendes ein (einschließlich Modul auf "Aktuelles Produktmodul" und Codegen auf "Manuell / Keine"):

Geben Sie hier die Bildbeschreibung ein

Fulvio
quelle