Passing a managed object context to a view in SwiftUI is easy enough using an @Environment variable. But getting that same context to the model and view model, not so much. Here is what I tried:
I created a property called context in the view model. In the view, I pass the managed object context lazily. The problem is that now I'm getting an error when I call a view model's method from the view - "Cannot use mutating getter on immutable value: 'self' is immutable". The method worked before I added the context, so why did it stop working? And, more importantly, how do I make it work?!
Model:
struct Model {
//use fetch request to get users
func checkLogin(username: String, password: String) {
for user in users {
if username == user.userEmail && password == user.password {
return true
}
}
return false
}
}
View Model:
class ViewModel {
var context: NSManagedObjectContext
private var model = Model()
init(context: NSManagedObjectContext) {
self.context = context
}
func checkLogin(username: String, password: String) -> Bool {
model.checklogin(username: username, password: password)
}
}
And finally, the View:
struct LoginView: View {
@Environment(\.managedObjectContext) var moc
lazy var viewModel = ViewModel(context: moc)
//Login form
Button(action: {
if self.viewModel.checkLogin(username: self.email, password: self.password) {
//ERROR: Cannot use mutating getter on immutable value: 'self' is immutable
//allow login
}
}) {
Text("login")
}
}
You cannot use lazy var
in View, because it is immutable. Here is possible approach to solve your case
class ViewModel {
var context: NSManagedObjectContext?
private var model = Model()
init(context: NSManagedObjectContext? = nil) {
self.context = context
}
func checkLogin(username: String, password: String) -> Bool {
return model.checkLogin(username: username, password: password)
}
}
struct LoginView: View {
@Environment(\.managedObjectContext) var moc
private let viewModel = ViewModel() // no context yet here, so just default
//Login form
var body: some View {
Button(action: {
if self.viewModel.checkLogin(username: self.email, password: self.password) {
//allow login
}
}) {
Text("login")
}
.onAppear {
self.viewModel.context = moc // << set up context here
}
}
}