【问题标题】:CoreData and SwiftUI: Context in environment is not connected to a persistent store coordinatorCoreData 和 SwiftUI:环境中的上下文未连接到持久存储协调器
【发布时间】:2020-03-28 16:40:24
【问题描述】:

我正在尝试通过构建一个家庭作业管理应用程序自学 Core Data。我的代码构建良好,应用程序运行良好,直到我尝试向列表中添加新分配。我在以下行收到此错误Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1c25719e8)ForEach(courses, id: \.self) { course in。控制台也有这个错误:Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x2823cb3a0>.

我对 Core Data 知之甚少,不知道问题可能出在哪里。我在数据模型中设置了“作业”和“课程”实体,其中课程与作业具有一对多的关系。每个作业都将归类在特定的课程下。

这是向列表添加新赋值的视图代码:

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

编辑:这是 AppDelegate 中设置持久容器的代码:

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

以及设置环境的 SceneDelegate 中的代码:

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

【问题讨论】:

  • 您在哪里将托管对象上下文添加到环境中?该托管对象上下文是如何创建的?看来您还没有将其与持久存储协调器连接起来,
  • 我在原始帖子中为您添加了将 moc 添加到环境中的代码。
  • @KevinOlmats 我的回答有帮助吗?
  • 检查你是否通过环境.environment(\.managedObjectContext, viewContext)分配了上下文
  • @onmyway133 这是正确答案

标签: swift core-data swiftui


【解决方案1】:

您实际上并没有保存上下文。您应该执行以下操作:

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

您的@FetchRequest(...) 也可能如下所示:

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

您可以修改CourseItem 类来处理sortDescriptors,如下所示:

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

然后你可以像下面这样修改你的ForEach(...),并且也可以很容易地处理项目的删除:

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

您要确保将“类名”设置为“CourseItem”,这与我们之前创建的CourseItem 类匹配。

只需单击 .xcdatamodeId 文件中的 ENTITIES 并将所有内容设置为以下内容(包括 Module 到“当前产品模块”和 Codegen到“手动/无”):

【讨论】:

    【解决方案2】:

    像 moc 这样的环境值只会自动传递给层次结构中的其他视图。因此,如果您显示不属于您的视图层次结构的工作表或任何内容,您将失去环境,并且您需要将 moc 传递给新的层次结构,就像您为 ContentView 所做的那样。检查此代码 sn -p:

    .sheet(isPresented: self.$showSheet) {
                SheetView()
                    .environment(\.managedObjectContext, self.moc)
            }
    

    【讨论】:

    • 一些后续问题:什么被认为是视图层次结构的一部分?我应该如何在视图之间导航以保持相同的层次结构?
    • @Jeff 简单来说,如果视图属于同一个 NavigationView,它们就是同一个层次结构的一部分。因此,每当您使用 NavigationLink 在视图之间移动时,您都处于相同的层次结构中。展示一张工作表(如上面的代码)会创建一个新的层次结构,从而创建一个新的环境,这就是为什么必须将环境变量传递到新的层次结构中。
    猜你喜欢
    • 2020-07-08
    • 1970-01-01
    • 1970-01-01
    • 2013-04-08
    • 1970-01-01
    • 2014-02-15
    • 2020-07-13
    • 1970-01-01
    • 2011-01-17
    相关资源
    最近更新 更多