Reputation: 5354
I have a view named Requests
and this has a view model called RequestViewModel
. This view model takes as parameter a model of type Request
and then it provides several methods to the view. The problem is that I need to load these requests
first from firebase but I do not know where.
Is view model a good place to fetch data because I have this feeling that it is not? I have similar situations like this for other view models; for ex PackageViewModel
and more...
Should I load this data somewhere else for ex in a Service and then pass it to this view model or there are other ways to do this?
class RequestViewModel: ObservableObject {
@Published var request: Request
@Published var requests: [Request] = []
init(request: Request) {
self.request = request
}
func loadRequests() -> Void {
network call here {
self.requests = requests
}
}
}
Upvotes: 0
Views: 2801
Reputation: 1409
Use a dedicated network service.
Search your feelings, you know it to be true.
The problem is, it goes against everything you think you know about MVVM.
Most MVVM devs use view model as a convenient wrapper for networking requests (which have side effects), business logic, and view controls.
The idea behind it is to refactor out view-binding model and associated logic/effects.
And as with all design patterns, some devs would abuse it, twist it, copy and paste it from java ... etc.
As time goes by, devs began to take ViewModel for granted, since it's a norm in Android, it must work in iOS, right?
Any dev worth their salt would ask these questions when they try to port MVVM to SwiftUI:
Is there a binding mechanism in SwiftUI? How does it work?
struct Model: View {...}
, is this a model-view mapping? If so I may not need to declare
a separate ViewModel? (pro only question)
Whoever conforms to ObservableObject can be observed (to trigger view update), this goes beyond the narrow definition of view model (view-related properties). An ObservableObject can be a service or shared resource. E.g.; A networking service. Congratulations, you just took the first step into a much larger world.
Why would I introduce a reference type view model to augment a value type model view for every such value type that conforms to View in a SDK built around value type? Swift is not java?
Now we have enough setup to properly answer your question.
Create a dedicated networking service, e.g.;
final class Resource: ObservableObject { // don't even need inheritance, OOP isn't always the best solution in all situations
@Published var requests = [Requests]()
// publish other resources
func loadRequests() {
/* update requests in callback */
}
}
use it, e.g.;
struct Model: View {
@State var model = MyModel() // can be "view model", because it maps to view
@EnvironmentObject var res: Resource
// or as an alternative, @ObservedObject var res = Resource()
var body: some View {
// access the automatic binding provided by SwiftUI, e.g.; res.requests
// call res.loadRequests() to mutate internal state of a reference type
// note that stateful part of the "view model" is refactored out
// so that `model` can be value type, yet it provides binding via @State
// instead of ObservableObject, which has to be reference type
// this is the brilliance of SwiftUI, it moves heaven and earth so you have
// the safety of immutability from value type, **unlike MVVM**
}
}
Done.
Why do we need ViewModel again? Let me know in the comments.
Upvotes: 4
Reputation: 2549
It really depends. Are you using the data for just this one View? Or are you using it throughout the app?
Fetching data from the ViewModel
is fine. Checkout this repo for an example.
Be aware that data stored an
ObservableObject
doesn't persist. So you'd have to store theViewModel
as a@State
in the parent view. Look here.
Then I suggest having some sort of "store", loaded into your views with .environmentObject()
. Checkout this repo for an example.
Upvotes: 1