Environment variable not passed to Subviews

本秂侑毒 提交于 2019-12-11 19:46:24

问题


I want to get started with Core Data & SwiftUI and therefore created a new watchOS project using the latest Xcode 11.1 GM.

Then, I copied both persistentContainer & saveContext from a fresh iOS project (with Core Data enabled), to gain Core Data capabilities.

After that I modified the HostingController to return AnyView and set the variable in the environment.

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

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


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

Now I can access the context inside the ContentView, but not in its sub views.
But thats not how it is intended to be? As far as I know, all sub views should inherit its environment from its super views, right?

Right now, to access it inside its sub views I simply set the environment variables again, like this:

ContentView.swift

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

Once I remove the .environment() parameter inside ContentView, the App will crash, because there is no context loaded?!

The error message is Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x804795e0>.

ProjectsView.swift

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

But again, that can't be right? So, whats causing the error here?


回答1:


I was able to solve this by fixing up HostingController and guaranteeing the CoreData stack was setup before view construction. First, let's make sure the CoreData stack is ready to go. In ExtensionDelegate:

class ExtensionDelegate: NSObject, WKExtensionDelegate {

    let persistentContainer = NSPersistentContainer(name: "Haha")

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

I had trouble when this property was lazy so I set it up explicitly. If you run into timing issues, make loadPersistentStores a synchronous call with a semaphore to debug and then figure out how to delay nib instantiation until the closure is called later.

Next, let's fix HostingController, by making a reference to a constant view context. WKHostingController is an object, not a struct. So now we have:

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

Now, any subviews should have access to the MOC. The following now works for me:

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

You should see the address of the MOC in white above and in red below, without calling .environment on the SubView.




回答2:


In each view where you want to access your managedObjectContext you need to declare it like this:

@Environment(\.managedObjectContext) var context: NSManagedObjectContext

You don't set it on views, it gets passed around for you. And don't forget to import CoreData as well in those files.



来源:https://stackoverflow.com/questions/58113776/environment-variable-not-passed-to-subviews

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!