Reputation: 328
I'm developing an iOS app using swiftUI and Combine framework and also MVVM. I'm want to handle the login API request in a separate class called LoginService, which is used in the LoginViewModel. Now I want to know how should I publish and observe the attributes between view and ViewModel. I mean ViewModel is an ObservableObject and is being observed in the View, But since I'm handling the network request in a Service class how should LoginService notify LoginViewModel and LoginView that the data is received and the View should be updated?
import Foundation
import Combine
class LoginViewModel: ObservableObject {
@Published var user = UserModel()
@Published var LoginStatus: Bool = false
@Published var LoginMessage: String = ""
var service = LoginService()
func Login(With email: String, And password: String) -> Bool {
service.validateLogin(email: email, password: password)
return false
}
}
This is the Code for LoginViewModel. How should LoginService change the values for LoginStatus, LoginMessage and user when data is received from the server to notify the View? I'm saying this because as far as I know you can Observe the ObservableObjects only in the View(SwiftUI).
Upvotes: 4
Views: 1365
Reputation: 1409
You've established a one-way binding, i.e; model -> view
Now it doesn't matter how your service updates model, whenever model changes, view updates.
There are several ways for your service to update model.
Usual URLSession, update LoginStatus and LoginMessage in callback as usual.
Via Combine publishers, e.g.; URLSessionDataTaskPublisher, and do update in its sink closure.
Upvotes: 0
Reputation: 4450
Okay so I take your example and I would do the following:
I assumed your service returns true
or false
import Foundation
import Combine
class LoginViewModel: ObservableObject {
@Published var LoginStatus: Bool = false
@Published var LoginMessage: String = ""
var service = LoginService()
func Login(_ email: String, _ password: String) -> Bool {
self.LoginStatus = service.validateLogin(email: email, password: password)
return self.LoginStatus
}
}
In your view:
import SwiftUI
struct ContentView : View {
@ObservedObject var model = LoginViewModel()
var body: some View {
VStack {
Button(action: {
_ = self.model.Login("TestUser", "TestPassword")
}, label: {
Text("Login")
})
Text(self.model.LoginStatus ? "Logged In" : "Not Logged in")
}
}
}
It should be something around that.
I removed UserModel
because you shouldn't nest models.
I hope this helps.
EDIT 1:
To validate something automatically you can use onApear()
on your View
Or listen to changes with olnrecieve()
to update the UI or the State
import SwiftUI
struct ContentView : View {
@ObservedObject var model = LoginViewModel()
var body: some View {
VStack {
Button(action: {
_ = self.model.Login("TestUser", "TestPassword")
}, label: {
Text("Login")
})
Text(self.model.LoginStatus ? "Logged In" : "Not Logged in")
}.onAppear {
// call a function that gets something from your server
// and modifies your state
self.model.validate()
}.onReceive(self.model.$LoginMessage, perform: { message in
// here you can update your state or your ui
// according the LoginMessage... this gets called
// whenever LoginMessage changes in your model
})
}
}
Upvotes: 1