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() {
}
}
CodePudding user response:
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.
CodePudding user response:
You need to instantiate the StateObject
like this:
_addBudgetVM = StateObject(wrappedValue: AddBudgetViewModel(context: viewContext))
Although Apple recommends not doing this.
CodePudding user response:
You could initialize the view model without the parameter and inside the view model declare de environment
struct ContentView: View {
@StateObject var addBudgetVM: AddBudgetViewModel = AddBudgetViewModel()
var body: some View {
// some code here
}
}
The viewModel:
class AddBudgetViewModel: ObservableObject {
@Published var name: String = ""
@Environment(\.managedObjectContext) var viewContext
func save() {
}
}