SwiftUI Modal Environment

Viewed 1401

I don't really understand how the environment works when using modals. It looks like the environment is separated for modal views.

I made a quick example using core data and saving an object to Core Data. If I do not pass the environment, then the object does not get saved and I get the following error when trying to save the object: The operation couldn’t be completed. (Foundation._GenericObjCError error 0.)

If I pass the managed object context in the modal's environment it works. I commented out the line that makes it work.

Can anyone explain why does this happen, please?


    @FetchRequest(fetchRequest: ToDoItem.fetchAllItems()) var items
    @Environment(\.managedObjectContext) var managedObjectContext
    @State var showAddModal = false


    var body: some View {

        VStack {

            List(items, id: \.name) { item in
                Text(item.name)
            }

            Button(action: {
                self.showAddModal.toggle()
            }) {
                Text("Add item")
            }.sheet(isPresented: $showAddModal) {
                ModalView()
//                    .environment(\.managedObjectContext, self.managedObjectContext)
//                It works if the managed object context is passed in the modal's environment
            }
        }
    }
}

struct ModalView: View {

    @State var toDoItemName: String = ""
    @Environment(\.presentationMode) var presentationMode
    @Environment(\.managedObjectContext) var managedObjectContext

    var body: some View {

        VStack {
            TextField("Item name", text: $toDoItemName)
            Button(action: {
                let toDoItem = ToDoItem(context: self.managedObjectContext)
                toDoItem.name = self.toDoItemName
                do {
                    try self.managedObjectContext.save()
                } catch {
                    print(error.localizedDescription)
                }
                self.presentationMode.wrappedValue.dismiss()
            }) {
                Text("Add")
            }
        }
    }
}```
4 Answers

It should pass through automatically but doesn't.

"if view A presents view B as a sheet then they don’t automatically share environment data, and we need to send it in by hand. Apple has described this sheet situation as a bug that they want to fix, so I’m hopeful this will change in a future update to SwiftUI." - Paul Hudson - Hacking With Swift

I don't know his sources, but he seems to know what he is talking about normally.

When you create the first view in SwiftUI, the framework generates an Environment for it. SwiftUI uses Environment to store different system settings like the color scheme, managedObjectContext and app-specific stuff like default font. All system settings and app-specific stuff except our custom EnvironmentObjects are pre-defined environment values(here, you can check the full collection of environmental values). One important thing to remember is that each view inherits Environment from its parent.

So with the following line of code in your ModalView, you are getting the managedObjectContext from the Environment of your ModalView that was inherited from the view that presents it.

@Environment(\.managedObjectContext) var managedObjectContext

Also, when you work with Coredata, you should pay attention to contexts, especially if you use multiple contexts.

Tip: If you go to our root view and set all the values that you might need, you will propagate them through your entire app (as values injected in Environment).

References:

If I can tag on to this:

when I do add the .environment(\.managedObjectContext, self.managedObjectContext) My ObservableObject class that I create in the child (modal) will not work correctly. All vars in the class reset to their initial value each time a call is made to the class's properties or methods.

when you pass view with .sheet, environment objects are not passed so you need to specified your environment object.

Try to open with ModalView() with NavigationLink and everything will work.

Related