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:
MealServingis 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
@Stateproperties 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
