I've simplified my code down so it's easier to see what i'm trying to do, I am building a feed of ratings that are sourced from firebase documents of whomever I am "following"
All that I want to do is whenever firebase says there is a new update to one of my reviews, change that Identifiable, as apposed to having to re-load every review from every person i'm following.
Here's my view:
import SwiftUI
import Firebase
struct Rating: Identifiable {
var id: String
var review: String
var likes: Int
}
struct Home: View {
@ObservedObject var feedViewModel = FeedViewModel()
var body: some View {
VStack{
ForEach(self.feedViewModel.allRatings, id: \.id){ rating in
Text(rating.review)
Text("\(rating.likes)")
}
}
}
}
Here are my functions for FeedViewModel:
class FeedViewModel: ObservableObject {
@Published var following = ["qedXpEcaRLhIa6zWjfJC", "1nDyDT4bIa7LBEaYGjHG", "4c9ZSPTQm2zZqztNlVUp", "YlvnziMdW8VfibEyCUws"]
@Published var allRatings = [Rating]()
init() {
for doc in following {
// For each person (aka doc) I am following we need to load all of it's ratings and connect the listeners
getRatings(doc: doc)
initializeListener(doc: doc)
}
}
func getRatings(doc: String) {
db.collection("ratings").document(doc).collection("public").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
// We have to use += because we are loading lots of different documents into the allRatings array, allRatings turns into our feed of reviews.
self.allRatings += querySnapshot!.documents.map { queryDocumentSnapshot -> Rating in
let data = queryDocumentSnapshot.data()
let id = ("\(doc)-\(queryDocumentSnapshot.documentID)")
let review = data["review"] as? String ?? ""
let likes = data["likes"] as? Int ?? 0
return Rating(id: id, review: review, likes: likes)
}
}
}
func initializeListener(doc: String){
db.collection("ratings").document(doc).collection("public")
.addSnapshotListener { (querySnapshot, error) in
guard let snapshot = querySnapshot else {
print("Error listening for channel updates")
return
}
snapshot.documentChanges.forEach { change in
for document in snapshot.documents{
let data = document.data()
let id = ("\(doc)-\(document.documentID)")
let review = data["review"] as? String ?? ""
let likes = data["likes"] as? Int ?? 0
// I thought swiftui Identifiables would be smart enough to not add another identifiable if there is already an existing one with the same id, but I was wrong, this just duplicates the rating
// self.allRatings.append(Rating(id: id, review: review, likes: likes))
// This is my current solution, to overwrite the Rating in the allRatings array, however I can not figure out how to get the index of the changed rating
// "0" should be replaced with whatever Rating index is being changed
self.allRatings[0] = Rating(id: id, review: review, likes: likes)
}
}
}
}
}
All I want is to make the "likes" of each rating live and update whenever someone likes a rating. It seems simple but I am relatively new to swiftui so I might be completely off on how I am doing this. Any help is greatly appreciated!
After a week of trying to figure this out I finally got it, hopefully this helps somebody, if you want to overwrite an identifiable in your array find the index by using your id. In my case my ID is the firebaseCollectionID + firebaseDocumentID, so this makes it always 100% unique AND it allows me to reference it from a snapshot listener...
Then overwrite it by doing: