swiftswiftuiswiftui-environment

How do I pass environment object between sibling views?


I'm trying to pass some data between sibling views.

I got a VerseDetailView with a button:

@StateObject var favoritesViewModel = FavoritesViewModel()
...
Button("Add to favorite") {
 favoritesViewModel.add(verse: verse)
}

My FavoritesViewModel looks like this:

class FavoritesViewModel: ObservableObject {
    
    @Published var favoriteVerses: [Verse] = []
    
    func add(verse: Verse) {
        favoriteVerses.append(verse)
    }
    
}

How would I pass favoriteVerses to display in a completely different subview?

My main app file looks like this:

var body: some Scene {
        WindowGroup {
            TabView {
                NavigationView {
                    BookView() // VerseDetailView is a child of this view
                }
                .tabItem {
                    Image(systemName: "book")
                    Text("Books")
                       
                }
                NavigationView {
                    FavoritesView() // I want to get the array of favoriteVerses here
                }
                
                .tabItem {
                    Image(systemName: "bookmark")
                    Text("Favorites")
                }
            }
        }
    }

My FavoritesView looks like this:

struct FavoritesView: View {
    @EnvironmentObject var favoritesViewModel: FavoritesViewModel 
     // is this correct?
    // I get a hread 1: "Fatal error: No ObservableObject of type FavoritesViewModel found" error
    
    var body: some View {
        
            List {
                Section(header: Text("Favorite verses")) {
                    ForEach(favoritesViewModel.favoriteVerses) { verse in
                        Text(verse.verse)
                    }
                }
                
            }
    }
    
}

I tried adding this at the top of the main app file:

var favoritesViewModel = FavoritesViewModel()
...
NavigationView { 
  FavoritesView().environmentObject(favoritesViewModel)
}

But that doesn't work either


Solution

  • Add it for common top view (in your case it is TabView), like

    TabView {
        NavigationView {
            BookView() // VerseDetailView is a child of this view
        }
        .tabItem {
            Image(systemName: "book")
            Text("Books")
               
        }
        NavigationView {
            FavoritesView() // I want to get the array of favoriteVerses here
        }
        
        .tabItem {
            Image(systemName: "bookmark")
            Text("Favorites")
        }
    }
    .environmentObject(favoritesViewModel)   // << here !!