0

I'm trying to inject my core data viewcontext into several view models, so I don't need to pass one big view model throughout the whole app.

I'm using the SwiftUI lifecycle, so the NSManagedObjectContext is generated here:

@main
struct Core_Data_VM_TestApp: App {
    let persistenceController = PersistenceController.shared

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
        }
    }
}

Following along this answer I didn't succeed, also after playing along with different initializers.

What I want to do is something like this, which isn't working ('Cannot use instance member 'viewContext' within property initializer; property initializers run before 'self' is available')

Content View

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @StateObject var itemVM = ItemViewModel(viewContext: viewContext)

(...)
    

Viewmodel:

class ItemViewModel: ObservableObject {
    
    var viewContext: NSManagedObjectContext
    
    init(viewContext: NSManagedObjectContext) {
        self.viewContext = viewContext
    }


}

Thanks for any help!

ScottM
  • 7,108
  • 1
  • 25
  • 42
ronaparte
  • 70
  • 7

2 Answers2

2

Just get it from your controller. The variable in the ViewModel will look something like

var viewContext: NSManagedObjectContext = PersistenceController.shared.container.viewContext

There is no need to pass it in the init.

Also, as a best practice if several anything are using something you should create an object for that something and put the common code there.

All the ViewModels can reference this single object. It will make changing things and debugging a million times easier as you app grows.

lorem ipsum
  • 21,175
  • 5
  • 24
  • 48
  • var viewContext: NSManagedObjectContext = PersistenceController.shared.container.viewContext worked great - thank you, also for pointing out the idea about referencing a single object – ronaparte Nov 09 '21 at 14:34
-1

One approach is to not instantiate the @StateObject in the declaration, but in the view's init method. Note that the StateObject struct that wraps the itemVM property is stored as _itemVM:

struct ContentView: View {
  @Environment(\.managedObjectContext) private var viewContext
  @StateObject var itemVM: ItemViewModel

  init() {
    _itemVM = StateObject(wrappedValue: ItemViewModel(viewContext: viewContext))
  }
  // ...
}
ScottM
  • 7,108
  • 1
  • 25
  • 42