环境变量未传递给子视图

时间:2019-09-26 09:34:35

标签: swift watchkit swiftui

我想开始使用Core Data和SwiftUI,因此使用最新的Xcode 11.1 GM创建了一个新的watchOS项目。

然后,我从一个新的iOS项目(已启用核心数据)中复制了persistentContainersaveContext,以获得核心数据功能。

此后,我修改了HostingController以返回AnyView并在环境中设置变量。

class HostingController: WKHostingController<AnyView> {
    override var body: AnyView {

        let managedObjectContext = (WKExtension.shared().delegate as! ExtensionDelegate).persistentContainer.viewContext


        return AnyView(ContentView().environment(\.managedObjectContext, managedObjectContext))
    }
}

现在,我可以访问ContentView内部的上下文,但不能访问其子视图
但这不是预期的样子吗?据我所知,所有子视图都应从其超级视图继承其环境,对吗?

现在,要在其子视图中访问它,我只需再次设置环境变量,如下所示:

ContentView.swift

NavigationLink(destination: ProjectsView().environment(\.managedObjectContext, managedObjectContext)) {
    HStack {
        Image(systemName: "folder.fill")
        Text("Projects")
    }
}

一旦删除ContentView中的.environment()参数,该应用程序将崩溃,因为没有加载上下文?!

错误消息是Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x804795e0>

ProjectsView.swift

struct ProjectsView: View {
    @Environment(\.managedObjectContext) var managedObjectContext
    [...]
}

但是,那又不对吗?那么,是什么导致了错误呢?

2 个答案:

答案 0 :(得分:1)

我能够通过修复HostingController并确保在构造视图之前设置CoreData堆栈来解决此问题。首先,让我们确保CoreData堆栈已准备就绪。在ExtensionDelegate中:

class ExtensionDelegate: NSObject, WKExtensionDelegate {

    let persistentContainer = NSPersistentContainer(name: "Haha")

    func applicationDidFinishLaunching() {
        persistentContainer.loadPersistentStores(completionHandler: { (storeDescription, error) in
            // handle this
        })
    }
}

此属性为lazy时遇到麻烦,因此我对其进行了明确设置。如果您遇到计时问题,请使用信号量对loadPersistentStores进行同步调用以进行调试,然后找出如何延迟笔尖实例化直到以后调用闭包。

接下来,让我们通过引用常量视图上下文来修复HostingControllerWKHostingController是对象,而不是结构。现在我们有了:

class HostingController: WKHostingController<AnyView> {

    private(set) var context: NSManagedObjectContext!

    override func awake(withContext context: Any?) {
        self.context = (WKExtension.shared().delegate as! ExtensionDelegate).persistentContainer.viewContext
    }

    override var body: AnyView {
        return AnyView(ContentView().environment(\.managedObjectContext, context))
    }
}

现在,所有子视图都应有权访问MOC。现在,以下内容适用于我:

struct ContentView: View {

    @Environment(\.managedObjectContext) var moc: NSManagedObjectContext

    var body: some View {
        VStack {
            Text("\(moc)")
            SubView()
        }
    }
}

struct SubView: View {

    @Environment(\.managedObjectContext) var moc: NSManagedObjectContext

    var body: some View {
        Text("\(moc)")
            .foregroundColor(.red)
    }
}

您应该看到MOC的地址在上面的白色和下面的红色,没有.environment上呼叫SubView

答案 1 :(得分:0)

在每个要访问ManagedObjectContext的视图中,都需要这样声明:

@Environment(\.managedObjectContext) var context: NSManagedObjectContext

您没有在视图上设置它,它会为您传递。并且不要忘记在这些文件中也导入CoreData。