1

I am trying to inject the managedObjectContext in ViewModel and for some weird reason it is throwing a weird error.

struct ContentView: View {
   
    @Environment(\.managedObjectContext) var viewContext
    @StateObject var addBudgetVM: AddBudgetViewModel
    
    init() {
        // THIS LINE CAUSES ISSUES 
        addBudgetVM = AddBudgetViewModel(context: viewContext)
    }
    
    var body: some View {
       // some code here
    }
    
}

Cannot assign to property: 'addBudgetVM' is a get-only property

Here is the implementation of AddBudgetViewModel

import Foundation
import CoreData

class AddBudgetViewModel: ObservableObject {
    
    @Published var name: String = ""
    var context: NSManagedObjectContext
    
    init(context: NSManagedObjectContext) {
        self.context = context
    }
    
    func save() {
        
    }
    
}
9
  • 1
    see this answer: stackoverflow.com/questions/62635914/… Commented Nov 7, 2021 at 2:10
  • @workingdog But I need to pass managedObjectContext Commented Nov 7, 2021 at 2:13
  • 1
    You could make the context optional on your view model and set it on onAppear Commented Nov 7, 2021 at 3:22
  • Or inject the view model into the view Commented Nov 7, 2021 at 8:09
  • I agree with @JoakimDanielson. You should inject the view model from the superview Commented Nov 7, 2021 at 9:48

1 Answer 1

1

I find the best approach is to inject the view model from the superview. This nicely separates the view, which consumes the view model, from the creation of the view model. It improves testability as it makes it easy to inject a mock view model.

Something like:


struct ParentView: View {
      @Environment(\.managedObjectContext) var viewContext

      var body: some View {
          //...
          AddBudgetView(viewModel: AddBudgetViewModel(context: viewContext))
          //...
      }
}


struct AddBudgetView: View {

      viewModel: AddBudgetViewModel 

      var body: some View {
          // ...
      }
}

class AddBudgetViewModel: ObservableObject {

    private (set) var context: NSManagedObjectContext

    init(context: NSManagedObjectContext) {
    
        self.context = context
    }

   func save() {
    
   }
}

I probably wouldn't even have Core Data code in the view model. I would put all of that in a main model and pass the main model instance to the view model initialiser instead of the managed object context. The view model can then call methods on the main model to create and save items. This way your view model is isolated from the storage implementation detail and you could make changes to your store, such as replacing Core Data, without having to touch view models all over the place.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.