CoreData e SwiftUI: il contesto in ambiente non è collegato a un coordinatore di negozi persistente


10

Sto cercando di insegnare a me stesso i dati di base creando un'app per la gestione dei compiti. Il mio codice viene compilato correttamente e l'app funziona correttamente finché non provo ad aggiungere un nuovo compito all'elenco. Ottengo questo errore Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1c25719e8)sulla seguente riga: ForEach(courses, id: \.self) { course in. La console ha anche questo errore: Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x2823cb3a0>.

So molto poco di Core Data e non riesco a capire quale potrebbe essere il problema. Ho impostato entità "Assegnazione" e "Corso" nel modello di dati, dove Corso ha una relazione uno-a-molti con Assegnazione. Ogni incarico verrà classificato in un determinato corso.

Questo è il codice per la vista che aggiunge una nuova assegnazione all'elenco:

    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: Ecco il codice in AppDelegate che configura il contenitore persistente:

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

E il codice in SceneDelegate che configura l'ambiente:

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

Dove si aggiunge il contesto dell'oggetto gestito all'ambiente? Come viene creato quel contesto di oggetto gestito? Sembra che tu non l'abbia collegato con un coordinatore di negozi persistente,
Paulw11

Ho aggiunto il codice in cui aggiungo il moc all'ambiente nel mio post originale per te.
Kevin Olmats,

@KevinOlmats La mia risposta è stata di aiuto?
Fulvio,

Verifica di aver assegnato un contesto tramite ambiente.environment(\.managedObjectContext, viewContext)
onmyway133,

@ onmyway133 questa è la risposta corretta
Kevin Olmats il

Risposte:


8

In realtà non stai salvando il contesto. Dovresti eseguire quanto segue:

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

Inoltre @FetchRequest(...)potresti assomigliare a questo:

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

Puoi modificare la tua CourseItemclasse in modo da gestire sortDescriptorsquanto segue:

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

Quindi modificherai il tuo ForEach(...)come il seguente e puoi anche gestire la cancellazione degli elementi abbastanza facilmente:

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

Una cosa che vuoi assicurare è che "Nome classe" sia impostato su "CourseItem", che corrisponde alla CourseItemclasse che abbiamo creato in precedenza.

Fai semplicemente clic su ENTITÀ nel tuo .xcdatamodeIdfile e imposta tutto quanto segue (incluso Modulo su "Modulo prodotto corrente" e Codegen su "Manuale / Nessuno"):

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.