Home > front end >  How to initialize a View by observing @StateObject from other View
How to initialize a View by observing @StateObject from other View

Time:09-04

I have 2 Views inside a TabView (each view is in a different view file):

  1. For adding employees (AddEmployeeView.swift)
  2. For displaying employees (AllEmployeesView.swift)

and I also have a View Model in EmployeeDB.swift . I want to use the same object ( @StateObject var viewModel = EmployeeViewModel() ) from AddEmployeeView.swift to access and display viewModel 's info in AllEmployeesView.swift. I tried using @ObservedObject to do so which is working, but when I initialize this view (AllEmployeesView())in TabView this error ( Missing argument for parameter 'viewModel' in call ) pops up. How can I access the updated data like AddEmployeeView 's viewModel.allEmployees from the AllEmployeesView View?

ContentView.swift

struct ContentView: View {
    var body: some View {
        TabView() {
            AddEmployeeView()
                .tabItem {
                    Text("Add")
                }
            
            AllEmployeesView() //Missing argument for parameter 'viewModel' in call
                .tabItem {
                    Text("All Employee")
                }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

EmployeeDB.swift

class EmployeeViewModel: ObservableObject {
    
    var allEmployees = [Employee]()
    @Published var name : String = ""
    @Published var id : String = ""
    
    struct Employee: Identifiable {
        let name : String
        let id: String
    }
    
    func save() {
        let newEmployee = Employee(name: name, id: id)
        allEmployees.append(newEmployee)
    }
    
    func getEmployees() -> [Employee] {
        return allEmployees
    }
    
}

AddEmployeeView.swift

struct AddEmployeeView: View {
    
    @StateObject var viewModel = EmployeeViewModel()
    
    var body: some View {
        VStack {
            TextField("Name", text: $viewModel.name)
            TextField("Id", text: $viewModel.id)
            Button ("Save") {
                viewModel.save()
            }
        }
        .background(.gray)
        .padding()
    }
}

struct AddEmployeeView_Previews: PreviewProvider {
    static var previews: some View {
        AddEmployeeView()
    }
}

AllEmployeesView.swift

struct AllEmployeesView: View {
    
    @ObservedObject var viewModel: EmployeeViewModel
    
    var body: some View {
    
        List {
            let allEmployees = viewModel.allEmployees
            ForEach(allEmployees) { employee in
                Text(employee.name)
            }
        }
    }
}

struct AllEmployeesView_Previews: PreviewProvider {
    static var previews: some View {
        AllEmployeesView(viewModel: EmployeeViewModel())
    }
}

CodePudding user response:

You should have one source of truth for your viewModel. Create your viewModel as a @StateObject in ContentView and pass it to AddEmployeeView and AllEmployeesView.

In order to do that, declare:

@ObservedObject var viewModel: EmployeeViewModel

in both AddEmployeeView and AllEmployeesView. Then pass in viewModel from ContentView like this:

AddEmployeeView(viewModel: viewModel)

and

AllEmployeesView(viewModel: viewModel)

That way, all three of your views will be working from the same viewModel which keeps everything in sync.

  • Related