CoreData dan SwiftUI: Konteks dalam lingkungan tidak terhubung ke koordinator toko persisten

10

Saya mencoba untuk belajar sendiri Data Inti dengan membangun aplikasi pengelolaan pekerjaan rumah. Kode saya dibuat dengan baik dan aplikasi berjalan dengan baik sampai saya mencoba menambahkan tugas baru ke daftar. Saya mendapatkan kesalahan iniThread 1: EXC_BREAKPOINT (code=1, subcode=0x1c25719e8) pada baris berikut: ForEach(courses, id: \.self) { course in. Konsol ini juga memiliki kesalahan ini: Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x2823cb3a0>.

Saya tahu sedikit tentang Data Inti dan bingung apa masalahnya. Saya telah menetapkan entitas "Penugasan" dan "Kursus" dalam model data, di mana Course memiliki hubungan satu-ke-banyak dengan Penugasan. Setiap tugas akan dikategorikan dalam kursus tertentu.

Ini adalah kode untuk tampilan yang menambahkan tugas baru ke daftar:

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

EDIT: Berikut adalah kode di AppDelegate yang mengatur wadah persisten:

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

Dan kode di SceneDelegate yang mengatur lingkungan:

    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
sumber
Di mana Anda menambahkan konteks objek terkelola ke lingkungan? Bagaimana konteks objek terkelola dibuat? Tampaknya Anda belum menghubungkannya dengan koordinator toko yang gigih,
Paulw11
Saya menambahkan kode tempat saya menambahkan moc ke lingkungan di posting asli saya untuk Anda.
Kevin Olmats
@KevinOlmats Apakah jawaban saya membantu?
fulvio
Pastikan Anda telah menetapkan konteks melalui lingkungan.environment(\.managedObjectContext, viewContext)
onmyway133
@ onmyway133 ini adalah jawaban yang benar
Kevin Olmats

Jawaban:

8

Anda sebenarnya tidak menyimpan konteksnya. Anda harus menjalankan yang berikut:

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

Anda juga @FetchRequest(...)bisa terlihat seperti ini:

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

Anda dapat memodifikasi CourseItemkelas Anda untuk menangani hal sortDescriptors-hal berikut:

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

Kemudian Anda akan memodifikasi ForEach(...)seperti berikut dan juga dapat menangani penghapusan item dengan cukup mudah:

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

Satu hal yang ingin Anda pastikan adalah bahwa "Nama Kelas" diatur ke "CourseItem", yang cocok dengan CourseItemkelas yang kami buat sebelumnya.

Cukup klik ENTITIES dalam .xcdatamodeIdfile Anda dan atur semuanya ke yang berikut (termasuk Modul ke "Modul Produk Saat Ini" dan Codegen ke "Manual / Tidak Ada"):

masukkan deskripsi gambar di sini

fulvio
sumber