SwiftUI passing data into ViewModel
I'm assuming here, that you have a class which has done a calculation. Furthermore, that calculation its result is required for the ShowViewModel. I'd do something as follows:
class EntryData: ObservableObject {
@Published var calculation = 42
}
struct ShowView: View {
@ObservedObject var showVM: ShowViewModel
var body: some View {
Text("\(showVM.m.daysRequired)")
}
init(entryData: EntryData) {
let month = ShowViewModel.Month(daysRequired: entryData.calculation)
self.showVM = ShowViewModel(month: month)
}
}
class ShowViewModel: ObservableObject {
struct Month {
let daysRequired: Int
}
@Published var test = "something"
@Published var m: Month
init(month: Month) {
self.m = month
}
}
struct ContentView: View {
var body: some View {
ShowView(entryData: EntryData())
}
}
You may try the following:
struct ShowView: View {
@ObservedObject var entry: EntryData
@ObservedObject var showVM: ShowViewModel // declare only (@ObservedObject optionally)
init(entry: EntryData) {
self.entry = entry
self.showVM = ShowViewModel(entry: entry)
}
var body: some View {
Text("\(entrybeg)")
}
}
class ShowViewModel: ObservableObject {
@Published var test = "something"
@Published var m: Month = Month()
private var entry: EntryData
init(entry: EntryData) {
self.entry = entry
}
}
Now when you create your view you need to pass EntryData
to it:
ShowView(entry: EntryData())
SwiftUI doesn't use the View Model pattern, you have to learn something new. SwiftUI uses lightweight data structs (badly named as Views) that are created super fast, tell the system how to create the actual views shown on screen and then are thrown away immediately and if you follow Apple's tutorials they state that the "Views are very cheap, we encourage you to make them your primary encapsulation mechanism" (20:50 Data Essentials) . You can also group related properties into their own custom struct so that a change to any property is detected as a change to the struct itself, this is called value semantics which is a feature of structs you cannot get with objects like view model objects. Define a struct var with @State
in parent View and reference it in child view using @Binding
passing it in using the $ syntax. These property wrappers allow the struct to behave like an object reference. SwiftUI does dependency tracking and if any @State
or @Binding
that is referenced by the body method, body will be called whenever these properties change.
struct ShowViewConfig {
var test = "something"
var m:Month = Month()
// you can even put a method in to update it
mutating func fetch(name:String, ascending: Bool){
}
}
Then create it in your ContentView
like:
struct ContentView {
@State var config = ShowViewConfig()
var body: some View {
...
ShowView(config:$config)
Then use it in your ShowView
like:
struct ShowView: View {
@Binding var config : ShowViewConfig
var body: some View {
Text("\(config.test)")
}
}
You can see this pattern at 8:44 in Data Essentials in SwiftUI WWDC 2020
If you have a model object, i.e. that is being monitored by @ObservableObject
then your job is to convert from the rich data types to simple data types in a View struct somewhere in the hierarchy, using formatters if necessary. You can learn more about this in WWDC 2020 Structure your app for SwiftUI previews @ 11:22. So as you can see if you tried to do this in a View Model object instead of the View structs then that is the wrong approach.
It's also fine to use @ObservableObject
for a loader or fetcher (20:14 Data Essentials).