I am working with Swift Combine in a SwiftUI App. I've had success using it until recently. I am attempting to use a ViewModel to set value when using combine but it does not appear to be getting updated. What's odd is I have another ViewModel that is working. I am using Firebase as a database.
ListingRepository
This file starts a listener from Firestore and sets an @Published
property with a array of listings.
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
}
}
}
}
}
Working ViewModel
This is the ViewModel that is working. I am using Combine here to map over the listings and create ListRowViewModel
s. The code works and I can populate the rows successfully.
class MarketplaceViewModel: ObservableObject {
@Published var listingRepository = ListingRepository()
@Published var listingRowViewModels = [ListingRowViewModel]()
private var cancellables = Set<AnyCancellable>()
init() {
listingRepository
.$listings
.receive(on: RunLoop.main)
.map { listings in
listings.map { listing in
ListingRowViewModel(listing: listing)
}
}
.assign(to: \.listingRowViewModels, on: self)
.store(in: &cancellables)
}
}
ViewModel Not Working
This ViewModel is for an account view. I started simply by using the count() operator to try and capture the number of listings and then display this on the AccountView
. When I build and run the view is not updated.
class AccountViewModel: ObservableObject {
@Published var listingRepository = ListingRepository()
@Published var numberOfListings: Int = 0
@
private var cancellables = Set<AnyCancellable>()
init() {
listingRepository
.$listings
.receive(on: RunLoop.main)
.count()
.assign(to: \.numberOfListings, on: self)
.store(in: &cancellables)
}
}
Upon further inspection it appears the publisher is not being called. I used a sink to try and print the result of the count and nothing gets printed.
class AccountViewModel: ObservableObject {
@Published var listingRepository = ListingRepository()
@Published var numberOfListings: Int = 0
@
private var cancellables = Set<AnyCancellable>()
init() {
listingRepository
.$listings
.receive(on: RunLoop.main)
.count()
.sink {
print("Number Of Listings: \($0)")
}
.store(in: &cancellables)
}
}
MarketplaceViewModel
and AccountViewModel
are used in MarketplaceView
and AccountView`.
MarketplaceView
struct MarketplaceView: View {
@ObservedObject var marketplaceViewModel = MarketplaceViewModel()
var body: some View {
Text(marketPlaceViewModel.listingRowViewModels[1].listing.name)
}
}
AccountView
struct AccountView: View {
@ObservedObject var accountViewModel = AccountViewModel()
var body: some View {
Text(String(accountViewModel.numberOfListings) }
}
}
These two views are presented in ContentView
struct ContentView: View {
var body: some View {
Group{
TabView {
MarketplaceView()
.tabItem {
Image(systemName: "shippingbox")
Text("Marketplace")
}.tag(0) // MarketplaceView
AccountView(user: testUser1)
.tabItem {
Image(systemName: "person")
Text("Account")
}.tag(1) // AccountView
} // TabView
.accentColor(.white)
}// Group
}
}
I'm very puzzled why this is not working. Any help would be greatly appreciated.