Recommended method of passing NSManagedObjectContext to @ObservedObject/@StateObject

I have a view that is pulling the context from the environment using the latest SwiftUI CoreData template (i.e. there is a preview/in-memory context vs the persistent context).

I also have an @ObservableObject class that is fetching objects based on the predicates passed in (think dynamic filtering). A random element from these fetched results are then displayed back in the view (i.e. I do not need this ObservableObject class to be a view itself).

However, there is an interesting "issue" where I cannot instantiate my @ObservedObject because the property initializers are run before "self" is available and I need to pass it the NSManagedObjectContext.

The only way I can think to get around this is to create the ObservableObject class outside of the view and pass it in the view's initializer. However, this isn't completely desirable as I would prefer this data be completely private to the view as other views do not need to know about its existence.

I also need it to be an ObserableObject so that the filters can change and it be reflected back in the view observing it.

Am I using the wrong tool or thinking about this wrong?

Code Block swift
class Filter: ObservableObject {
@Published var someObjects: [Objects] = []
/* Need to instantiate with the context so objects can be fetched */
private let context: NSManagedObjectContext
}
struct ContentView: View {
    @Environment(\.managedObjectContext) var context
/* Cannot initialize here as context isn't available */
    @ObservedObject var filter = Filter(context: context)
}


Accepted Reply

You may have already tried, but you can create a SubContentView including all the parts depending on filter.
Code Block
struct SubContentView: View {
@ObservedObject var filter: Filter
var body: some View {
Text("Hello, World!")
//All the parts depending on `filter`.
//...
}
}
struct ContentView: View {
@Environment(\.managedObjectContext) var context
var body: some View {
SubContentView(filter: Filter(context: context))
}
}


Not sure if there are some better ways.

Replies

You may have already tried, but you can create a SubContentView including all the parts depending on filter.
Code Block
struct SubContentView: View {
@ObservedObject var filter: Filter
var body: some View {
Text("Hello, World!")
//All the parts depending on `filter`.
//...
}
}
struct ContentView: View {
@Environment(\.managedObjectContext) var context
var body: some View {
SubContentView(filter: Filter(context: context))
}
}


Not sure if there are some better ways.
hi,

how about making private let context: NSManagedObjectContext be, instead, private var context: NSManagedObjectContext? = nil, writing @ObservedObject var filter = Filter() in the ContentView, and then adding an .onAppear() modifier in the body of the ContentView that does something like

Code Block
var body: some View {
List(filter.someObjects) { object in
// whatever you display here for an object
}
.onAppear(filter.setContext(context))
}

where setContext() is a method on Filter that sets the context and does whatever fetch you need. that should cause the ContentView to update.

hope that helps,
DMG
I really like this approach as it seems to be the cleanest. I wonder if this is an area SwiftUI can improve upon by flattening the need for an internal subview or if it is by design