SwiftUI : I get lost when I try to pass EnvironmentObject to another View when using MVVM

Viewed 105

I had done and read all most every post that I could find about this behavior :

No ObservableObject of type BaseViewModel found. A View.environmentObject(_:) for BaseViewModel may be missing as an ancestor of this view

However, I still not be able to get it done. I think the way I use "@EnvironmentObject var vm : BaseViewModel" in ContentView is wrong. But when I tried "@EnvironmentObject var vm = BaseViewModel()", the BaseViewModel() will ask for the payer so it will be BaseViewModel(payer: ). Here is my code

struct Payer: Identifiable {
    var id = UUID().uuidString
    var name : String

    var offset : CGFloat = 0
}

and In my ViewModel file,

class BaseViewModel: ObservableObject {
    
//* The problem * //
    @Published var payer: Payer 
//* The problem * //
    
    @Published var payers = [
        Payer( name: "AA"),
        Payer( name: "BB"),
        Payer( name: "CC"),
    ]
    
    init(payer: Payer) {
        self.payer = payer
    }
}

lastly in my ContentView

struct ContentView: View {
    @EnvironmentObject var vm : BaseViewModel
        var body: some View {
            BaseView()
                .environmentObject(self.vm)
        }
    }
2 Answers

You need to inject it in place where you create ContentView, like

@main
struct YourApp: App {

    var body: some Scene {
        WindowGroup {
            ContentView()
              .environmentObject(BaseViewModel(player: Payer( name: "AA"))
        }
    }
}

and

struct ContentView: View {
    @EnvironmentObject var vm : BaseViewModel
        var body: some View {
            BaseView()
                //.environmentObject(self.vm)  << not needed here !!
        }
    }

Alternate: you can create

struct ContentView: View {
    @StateObject var vm = BaseViewModel(player: Payer( name: "AA") // << create !!
    var body: some View {
        BaseView()
            .environmentObject(self.vm)  << inject !!
    }
}

You have to inject your dataModel from the root of you app, that’s from Main root struct.

import SwiftUI

struct Payer: Identifiable {
    var id = UUID().uuidString
    var name : String = "ABCD"
    
    var offset : CGFloat = 0
}

class BaseViewModel: ObservableObject {
    
    //* The problem * //
    @Published var payer: Payer
    //* The problem * //
    
    @Published var payers = [
        Payer( name: "AA"),
        Payer( name: "BaB"),
        Payer( name: "CC"),
    ]
    
    init(payer: Payer) {
        
        self.payer = payer
    }
}

struct ContentView: View {
    @EnvironmentObject var vm : BaseViewModel
    var body: some View {
        Text(vm.payer.name)
    }
}

@Main-:

import SwiftUI

@main
struct WaveViewApp: App {
    
    @StateObject var dataController:BaseViewModel
   
    init() {
        let payer = Payer()
        let model = BaseViewModel(payer:payer)
        _dataController = StateObject(wrappedValue: model)
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(dataController)
        }
    }
}
Related