I have three views (AddMatchView, TeamPickerView and TeamsOfCountryView). Everything should work like this: from AddTeamView I go to TeamPickerView, there I select the country and go to TeamsOfCountryView, after tapping on the team I need, both views (TeamPickerView and TeamsOfCountryView) should immediately close thanks to the common Bool variable, and the selected team (an object of the Team type) passed to the parent AddMatchView. But after selecting a team, only TeamsOfCountryView is closed, and an error occurs in TeamPickerView: Fatal error: No ObservableObject of type DBService found. A View.environmentObject (_ :) for DBService may be missing as an ancestor of this view.
Everything worked as it should until I decided to create a ViewModel for my view. i.e. if I transfer @State variables from AddMatchView to TeamPickerView, then errors will not stink, but I use properties from @ObservedObject
Main parent view:
struct AddMatchView: View {
@ObservedObject var viewModel = AddMatchViewModel()
@State isPresented = false //This works!
@State team: Team? //This works!
var body: some View {
//...
Button(action: { viewModel.isPresented.toggle() }){
//...
}.fullScreenCover(isPresented: $viewModel.isPresented) { TeamPickerView(team: $viewModel.home, isPresented: $viewModel.isPresented)}
//.fullScreenCover(isPresented: $isPresented) { TeamPickerView(team: $home, isPresented: $isPresented)} THIS WORKS!!
//...
}
ViewModel after using of which the error began to occur:
class AddMatchViewModel: ObservableObject{
@Published var home: Team?
@Published var isPresented = false
//...
}
TeamsPickerView:
struct TeamPickerView: View {
@EnvironmentObject var db: DBService
@Binding var team: Team?
@Binding var isPresented: Bool
@State private var searchText = ""
var body: some View {
NavigationView {
VStack{
SearchBar(text: $searchText)
Form{
//ERROR in below line after selecting team in child TeamsOfCountryView: No ObservableObject of type DBService found. A View.environmentObject(_:) for DBService may be missing as an ancestor of this view.
List (db.countries.filter({ searchText.isEmpty ? true : $0.name.contains(searchText) })) { country in
NavigationLink(destination: TeamsOfCountryView(countryID: country.documentID, team: $team, isPresented: $isPresented)) {
HStack{
Image(uiImage: Flag(countryCode: country.code)!.image(style: .roundedRect)).resizable().scaledToFit().frame(maxWidth: 30, maxHeight: 30)
Text(country.name)
}
}
}
}
}
.navigationBarTitle(Text("Countries"))
.navigationBarItems(trailing: Button(action: {isPresented = false }){
Text("Close")
})
}
}
}
TeamsOfCountryView:
struct TeamsOfCountryView: View {
@EnvironmentObject var db: DBService
@Binding var team: Team?
@Binding var isPresented: Bool
//...
var body: some View {
VStack{
//...
Form{
List (teams.filter({ searchText.isEmpty ? true : $0.name.contains(searchText) })) { team in
Button(action: {
//After that, an error occurs in the parent TeamPickerView
self.team = team
self.isPresented = false
}){
//...
}
}
}
//...
}
}
LITTLE UPDATE: simplified the example as much as possible
class ViewModel: ObservableObject{
@Published var isPresented = false
}
class EnvObj: ObservableObject{
@Published var foo = "test"
}
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
//@State var isPresented = false no error if we use it instead of viewModel.isPresented
var body: some View {
Button("Open Child View A"){
viewModel.isPresented.toggle()
}.fullScreenCover(isPresented: $viewModel.isPresented){ChildViewA(isPresented: $viewModel.isPresented)}
}
}
struct ChildViewA: View {
@EnvironmentObject var envObj: EnvObj
@Binding var isPresented: Bool
@State var openChildB = false
var body: some View {
NavigationView{
VStack{
//ERROR HERE: No ObservableObject of type EnvObj found. A View.environmentObject(_:) for EnvObj may be missing as an ancestor of this view.
Text(envObj.foo)
NavigationLink(destination: ChildViewB(isPresented: $isPresented)){
Text("Open Child View B")
}
}
}
}
}
struct ChildViewB: View {
@EnvironmentObject var envObj: EnvObj
@Binding var isPresented: Bool
var body: some View {
Button("Close Child View A and B"){
isPresented = false
}
}
}