I am trying to populate a view when a button is tapped on a different view. When I used my mock data, everything worked fine. However, when I switch to calling my firestore db data, nothing shows up. I'm still a few months into learning swiftui but I called on db the same way in a different part of my project which worked well. Here is an example of the code I am using.
import SwiftUI
import FirebaseFirestore
import FirebaseFirestoreSwift
struct RewardsView: View {
// Dummy Data
// var rewardItems: [Reward] = [
// .init(RewardText: "10% Off Next Haircut", RewardRedeemed: false, rewardPhoto: "crown_kings_main_photo", costOfReward: 10),
// .init(RewardText: "10% Off Extra Service", RewardRedeemed: true, rewardPhoto: "crown_kings_main_photo", costOfReward: 8),
// .init(RewardText: "20% Next Haircut", RewardRedeemed: false, rewardPhoto: "crown_kings_main_photo", costOfReward: 15),
// .init(RewardText: "30% off Next Haircut", RewardRedeemed: false, rewardPhoto: "crown_kings_main_photo", costOfReward: 20),
// .init(RewardText: "25% Off Next Haircut", RewardRedeemed: true, rewardPhoto: "crown_kings_main_photo", costOfReward: 18),
// .init(RewardText: "45% Off Next Haircut", RewardRedeemed: true, rewardPhoto: "crown_kings_main_photo", costOfReward: 30)
// ]
@EnvironmentObject private var rewardsViewModel: RewardViewModel
@EnvironmentObject private var loyaltyViewModel: LoyaltyViewModel
@State var rewards: [Reward] = []
func getRewards() async throws -> [Reward]{
let db = Firestore.firestore()
let snapshot = try await db.collection("redeemRewards").getDocuments()
let rewards = snapshot.documents.compactMap{try? $0.data(as: Reward.self)}
return rewards
}
var body: some View {
NavigationStack{
ScrollView {
LazyVGrid (columns: [
GridItem(.flexible()),
GridItem(.flexible()),
], spacing: 16, content:{
ForEach(rewardsViewModel.rewards, id: \.self) { reward in
RewardsCardView(rewardPhoto:reward.rewardPhoto, rewardText: reward.rewardText, rewardRedeemed: reward.rewardRedeemed, costOfReward: reward.costOfReward)
.environmentObject(rewardsViewModel)
.environmentObject(loyaltyViewModel)
}
})
}
.onAppear {
Task{
do {
let newRewards = try await getRewards()
rewards.append(contentsOf: newRewards)
} catch {
print("Error Fetching rewards: \(error.localizedDescription)")
}
}
}
.navigationTitle("Rewards")
}
}
}
//#Preview {
// RewardsView()
//}
struct Reward: Identifiable, Codable,Hashable {
@DocumentID var id: String?
let rewardText: String
let rewardRedeemed: Bool
let rewardPhoto: String
let costOfReward: CGFloat
let timestamp: Timestamp
let userId: String
let remainingPoints: CGFloat
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: Reward, rhs: Reward) -> Bool {
lhs.id == rhs.id
}
}
Am I possibly misunderstanding how a view is constructed at runtime?
Thanks for the help, and consideration.
As @lorem ipsum
commented, there would be many places that can be improved but the main reason your code doesn't work is that you're trying to display viewModel.rewards
but the fetched data is stored in rewards
in the view.
So, try replace
ForEach(rewardsViewModel.rewards, id: \.self) { reward in
with
ForEach(rewards, id: \.self) { reward in