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

enter image description here

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.

enter image description here

It's also fine to use @ObservableObject for a loader or fetcher (20:14 Data Essentials).

enter image description here

Tags:

Swiftui