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.