I have a little issue, I'm fetching data from Firebase, from Firestore database, but the view is only updated when I'm selecting one of the category. The view has 4 categories which can contains items. I'll post a short video at the end of this questions to make it clear what's my problem.
So, this is my ViewModel :
class HomeViewModell : ObservableObject {
@Published var productType : ProductType = .Wearable
@Published var products = [Product]()
@Published var filteredProducts : [Product] = []
var searchCancellable: AnyCancellable?
init() {
getData()
filteredProductByType()
}
func getData() {
FirebaseManager.shared.firestore.collection("products").addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("No documents")
return
}
DispatchQueue.main.async {
self.products = documents.map { d in
return Product(id: d.documentID, type: ProductType(rawValue: d["type"] as? String ?? "") ?? .Laptops
, title: d["title"] as? String ?? "",
subtitle: d["subtitle"] as? String ?? "",
price: d["price"] as? String ?? "", productImage: d["productImage"] as? String ?? ""
)
}
}
}
}
func filteredProductByType() {
DispatchQueue.global(qos: .userInteractive).async {
let results = self.products
//Since it will require more memory so were using lazy to perform more
.lazy
.filter { product in
return product.type == self.productType
}
// Limiting results..
.prefix(4)
DispatchQueue.main.async {
self.filteredProducts = results.compactMap({ product in
return product
})
}
}
}
}
These are my structs :
struct Product : Identifiable, Hashable {
var id = UUID().uuidString
var type : ProductType
var title : String
var subtitle : String
var description : String = ""
var price : String
var productImage : String = ""
var quantity : Int = 1
}
enum ProductType : String, CaseIterable {
case Wearable = "Wearable"
case Laptops = "Laptops"
case Phones = "Phones"
case Tablets = "Tablets"
}
And this is my view
struct Homee: View {
@Namespace var animation : Namespace.ID
@EnvironmentObject var sharedData : SharedDataViewModel
@ObservedObject var homeData : HomeViewModel = HomeViewModel()
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing : 15) {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing : 18) {
ForEach(ProductType.allCases, id: \.self) { type in
productTypeView(type: type)
}
}
.padding(.horizontal, 25)
}
.padding(.top, 28 )
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing : 25) {
ForEach(homeData.filteredProducts) { product in
ProductCardView(product: product)
}
}
.padding(.horizontal, 25)
.padding(.bottom)
.padding(.top, 80)
}
.padding(.top, 30)
}
.padding(.vertical)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color("HomeBG"))
.onChange(of: homeData.productType) { newValue in
homeData.filteredProductByType()
}
}
@ViewBuilder
func productTypeView(type : ProductType) -> some View {
Button {
withAnimation {
homeData.productType = type
}
} label: {
Text(type.rawValue)
.font(.custom(customFont, size: 15))
.fontWeight(.semibold)
.padding(.bottom, 10)
.overlay(
ZStack {
if homeData.productType == type {
Capsule()
.fill(Color("Purple"))
.matchedGeometryEffect(id: "PRODUCTTAB", in: animation)
.frame(height : 2)
} else {
Capsule()
.fill(Color.clear)
.frame(height : 2)
}
}
.padding(.horizontal, -5)
, alignment: .bottom
)
}
}
@ViewBuilder
func ProductCardView(product : Product) -> some View {
VStack(spacing: 10) {
//Addid matched geometry effect
ZStack {
if sharedData.showDetailProduct {
Image(product.productImage)
.resizable()
.aspectRatio(contentMode: .fit)
}
else {
Image(product.productImage)
.resizable()
.aspectRatio(contentMode: .fit)
.matchedGeometryEffect(id: "\(product.id)IMAGE", in: animation)
}
}
.frame(width: getRect().width / 2.5, height: getRect().width / 2.5)
.offset(y: -80)
.padding(.bottom, -80
Text(product.title)
.font(.custom(customFont, size: 18))
.fontWeight(.semibold)
.padding(.top)
Text(product.subtitle)
.font(.custom(customFont, size: 14))
.fontWeight(.semibold)
.foregroundColor(.gray)
Text(product.price)
.font(.custom(customFont, size: 16))
.fontWeight(.bold)
.foregroundColor(Color("Purple"))
.padding(.top, 5)
}
.padding(.horizontal,20)
.padding(.bottom, 22)
.background(
Color.white.cornerRadius(25)
)
}
}
Anyone know what can be the issue ?
So, only when a category is selected, my items appears.