I have a call to Firestore that grabs some data and stores to
@published var userJournals
In another view, I go to call the variable and it's empty. I checked the data in the initial pull from firestore and the data shows and is mapped successfully. Wondering what I'm doing wrong on the other view.
View 1
class JournalDashLogic: ObservableObject {
@Published var userJournals = [UserJournalEntry]()
@Published var userJournalIDs = [String]()
func grabUserJournals(journalID: String) {
//grab current user
guard let uid = FirebaseManager.shared.auth.currentUser?.uid else {
return
}
FirebaseManager.shared.firestore
.collection("users")
.document(uid)
.collection("userJournalEntrys")
.document(journalID)
.collection("items")
.addSnapshotListener { (snapshot, err) in
guard let documents = snapshot?.documents else {
print("no documents present")
return
}
//map to journal entry struct
self.userJournals = documents.map { (querySnapshot) -> UserJournalEntry in
let data = querySnapshot.data()
let dateCreated = data["dateCreated"] as? String ?? ""
let dayOfWeek = data["dayOfWeek"] as? String ?? ""
let mealCalories = data["mealCalories"] as? Int ?? 0
let mealCarbs = data["mealCarbs"] as? Int ?? 0
let mealFat = data["mealFat"] as? Int ?? 0
let mealName = data["mealName"] as? String ?? ""
let mealProtein = data["mealProtein"] as? Int ?? 0
let MealServing = data["MealServing"] as? Int ?? 0
let mealSaved = data["saved"] as? Bool ?? false
let mealTiming = data["timeCreated"] as? String ?? ""
let entry = UserJournalEntry(
id: UUID().uuidString, mealName: mealName, mealFat: mealFat, mealCarbs: mealCarbs,
mealProtein: mealProtein, mealCalories: mealCalories, MealServing: MealServing,
mealSaved: mealSaved, mealTiming: mealTiming, dayOfWeek: dayOfWeek,
totalCalories: "100", dateCreated: dateCreated)
return entry
}
}
}
}
View 2
.onAppear {
for id in jm.userJournalIDs {
jm.grabUserJournals(journalID: id)
}
}
sheet presented from View 2
.sheet(isPresented: $showAllJournals) {
SavedJournalsList(savedJournalIDs: jm.userJournalIDs, savedJournals: jm.userJournals)
.transition(transition)
}
View 3
struct SavedJournalsList: View {
@State var savedJournalIDs: [String]
@State var savedJournals: [UserJournalEntry]
var body: some View {
VStack(alignment: .leading) {
ForEach(savedJournals, id: \.self) { entry in
HStack {
Text(entry.dateCreated).bold()
Text("Total Calories: 3200")
.padding(.leading, 15)
}
.frame(maxWidth: .infinity)
.multilineTextAlignment(.center)
HStack {
Text("200 Carbs")
.foregroundColor(.gray)
Text("250 Protein")
.foregroundColor(.gray)
Text("100 Fat")
.foregroundColor(.gray)
}
.padding(.all, 5)
.frame(maxWidth: .infinity)
}
.frame(width: 300, height: 100)
.background(.white)
.cornerRadius(15)
.shadow(color: Color("LighterGray"), radius: 5, x: 0, y: 8)
}
}
}
CodePudding user response:
You can try this function which i have created for my project
func lisentDocument<T:Codable>(docPath: String, model: T.Type, completion:@escaping(T?,String?) -> Void) -> ListenerRegistration? {
let docRef = db.document(docPath)
var listner: ListenerRegistration? = nil
listner = docRef.addSnapshotListener { response, error in
if error != nil {
completion(nil, error?.localizedDescription)
print(listner)
}
do {
let data = try response?.data(as: model)
completion(data, nil)
} catch {
completion(nil, FBError.someThingWentWrong.message)
}
}
return listner
}
How to use
lisentDocument(docPath: "Doc Path", model: [UserJournalEntry].self,
completion: { data, error in
self.userJournals = data
})
If you have Object as an response use can use model as UserJournalEntry.self
CodePudding user response:
A couple of notes:
MealServing
is in uppercase, I think it should probably be lowercase:mealServing
.No need to map data manually, use Codable instead. See the official docs
you're using a view model, so no need to use
@State
properties on your vies. Use the view models instead.You're iterating over the IDs in your view model and it seems like you're trying to set up a snapshot listener for each of those documents. This is inefficient, and you should instead set up a listener on the collection (or a query), and then assign the result of this to the published property
userJournals
. The documentation has an example that shows how to do this here. If you're interested in the code sample, check out this repo