I am working on a SwiftUI Project using MVVM. I have the following files for a marketplace that has listings.
ListingRepository.swift - Connecting to Firebase Firestore Listing.swift - Listing Model File MarketplaceViewModel - Marketplace View Model MarketplaceView - List view of listings for the marketplace
Originally, I was making my repository file the EnvironmentObject which worked. While researching I am realizing it makes more sense to make the ViewModel the EnvironmentObject. However, I am having trouble making an EnvironmentObject. Xcode is giving me the following error in my MarketplaceView.swift file when I try and access marketplaceViewModel and I can't understand why?
SwiftUI:0: Fatal error: No ObservableObject of type MarketplaceViewModel found. A View.environmentObject(_:) for MarketplaceViewModel may be missing as an ancestor of this view.
Here are the files in a simplified form.
App File
@main struct Global_Seafood_ExchangeApp: App { @StateObject private var authSession = AuthSession() @StateObject private var marketplaceViewModel = MarketplaceViewModel(listingRepository: ListingRepository())
var body: some Scene { WindowGroup { ContentView() .environmentObject(marketplaceViewModel) .environmentObject(authSession) } } }
ListingRepository.swift
class ListingRepository: ObservableObject { let db = Firestore.firestore() @Published var listings = [Listing]() init() { startSnapshotListener() } func startSnapshotListener() { db.collection(FirestoreCollection.listings).addSnapshotListener { (querySnapshot, error) in if let error = error { print("Error getting documents: \(error)") } else { guard let documents = querySnapshot?.documents else { print("No Listings.") return } self.listings = documents.compactMap { listing in do { return try listing.data(as: Listing.self) } catch { print(error) } return nil } } } } } Listing.swift
struct Listing: Codable, Identifiable { @DocumentID var id: String? var title: String? } MarketplaceModelView.swift
class MarketplaceViewModel: ObservableObject { var listingRepository: ListingRepository @Published var listingRowViewModels = [ListingRowViewModel]() private var cancellables = Set<AnyCancellable>() init(listingRepository: ListingRepository) { self.listingRepository = listingRepository self.startCombine() } func startCombine() { listingRepository .$listings .receive(on: RunLoop.main) .map { listings in listings.map { listing in ListingRowViewModel(listing: listing) } } .assign(to: \.listingRowViewModels, on: self) .store(in: &cancellables) } } MarketplaceView.swift
struct MarketplaceView: View { @EnvironmentObject var marketplaceViewModel: MarketplaceViewModel var body: some View { // ERROR IS HERE Text(self.marketplaceViewModel.listingRowViewModels[1].listing.title) } } ListingRowViewModel.swift
class ListingRowViewModel: ObservableObject { var id: String = "" @Published var listing: Listing private var cancellables = Set<AnyCancellable>() init(listing: Listing) { self.listing = listing $listing .receive(on: RunLoop.main) .compactMap { listing in listing.id } .assign(to: \.id, on: self) .store(in: &cancellables) } } ContentView.swift
struct ContentView: View { @EnvironmentObject var authSession: AuthSession @EnvironmentObject var marketplaceViewModel: MarketplaceViewModel var body: some View { Group{ if (authSession.currentUser != nil) { TabView { MarketplaceView() .tabItem { Image(systemName: "shippingbox") Text("Marketplace") }.tag(0) // MarketplaceView AccountView(user: testUser1) .tabItem { Image(systemName: "person") Text("Account") }.tag(2) // AccountView } // TabView .accentColor(.white) } else if (authSession.currentUser == nil) { AuthView() } }// Group .onAppear(perform: authenticationListener) } // MARK: ++++++++++++++++++++++++++++++++++++++ Methods ++++++++++++++++++++++++++++++++++++++ func authenticationListener() { // Setup Authentication Listener authSession.listen() } } Any help would be greatly appreciated.