0

i'm new in SwiftUI. So, I am doing my practice and create some kind of cashier app and here I been stuck for a while. In ProductPageView I can increase and decrease the number of item. Then, when I go to DetailOrderView (like a cart view), I can also increase and decrease the quantity number. The print shows the quantity number correctly. But if I go back to ProductPageView, the label doesn't update it self.

Take a look at this screenshots:

First, I add 2 items.

enter image description here

Then I go to DetailOrderView, the quantity number is the same.:

enter image description here

Then I add 2 items in DetailOrderView (so it's now 4 items) and going back to ProductPageView, notice how the total price has increased but the quantity doesn't change. I feel like I want to "refresh" or "reload data" on this page.

enter image description here

How can I fix this and update the Text when I press the back button?

Here's my code:

Main

import SwiftUI

@main
struct CashierApp: App {
    @StateObject var productOder = ProductPresenter()

var body: some Scene {
    WindowGroup {
        MainPageView()
            .environmentObject(productOder)
    }
}

}

Product Page View import SwiftUI

struct ProductPageView: View {
    @EnvironmentObject var productOrder: ProductPresenter


var body: some View {
    VStack {
        ScrollView {
            VStack {
                ForEach(productOrder.cartItems, id: \.item.idItem) { element in
                    ProductRow(cartItems: CartItems(item: element.item, quantity: element.quantity))
                }
            }
        }
        TotalPaymentRow()
    }
}

}

Detail Order View or Cart View import SwiftUI

struct DetailOrderView: View {
    @EnvironmentObject var productOrder: ProductPresenter
    var arrayOrdered: [CartItems] = []


var body: some View {
    
    ZStack {
        ScrollView {
            VStack {
                ForEach(arrayOrdered, id: \.item.idItem) { element in
                    ProductRow(cartItems: CartItems(item: element.item, quantity: element.quantity))
                }
            }
            .padding(.top, 10)
        }
        CustomModalGrandTotal()
    }
    .navigationTitle("Cart")
    .navigationBarTitleDisplayMode(.inline)
}

}

The Presenter

import SwiftUI
import Combine

class ProductPresenter: ObservableObject {
     @Published var cartItems: [CartItems] = []
     var totalQuantity = 0

     // Increment product
     func increment(cartItem: inout CartItems) {
         let index: Int = cartItems.firstIndex(where: {$0.item == cartItem.item}) ?? -1
         cartItems[index].quantity += 1
         totalQuantity += 1
     }

     // Decrement product
     func decrement(cartItem: inout CartItems) {
         let index: Int = cartItems.firstIndex(where: {$0.item == cartItem.item}) ?? -1
         if cartItems[index].quantity > 0 {
             cartItems[index].quantity -= 1
             totalQuantity -= 1
         }
     }
}

Product Row

import SwiftUI

struct ProductRow: View {
    @State var cartItems: CartItems
    @EnvironmentObject var productOrder: ProductPresenter

var body: some View {
    
    HStack(alignment: .center) {
        Image(cartItems.item.image ?? "")
        VStack(alignment: .leading, spacing: 8) {
            Text(cartItems.item.name ?? "")
            Text(getPrice(value: cartItems.item.price ?? 0))
        }
        Spacer()
        VStack {
            Spacer()
            HStack{
                Button(action: {
                    if cartItems.quantity > 0 {
                        cartItems.quantity -= 1
                        productOrder.decrement(cartItem: &cartItems)
                        productOrder.calculateTotalPrice()
                    }
                }, label: {
                    imageMinusPlus(name: "minus")
                })
                Text("\(cartItems.quantity)") // This Text should be updated.
                Button(action: {
                    cartItems.quantity += 1
                    productOrder.increment(cartItem: &cartItems)
                    productOrder.calculateTotalPrice()
                }, label: {
                    imageMinusPlus(name: "plus")
                })
            }
        }
    }
}
}

PS: I deleted the styling to make the code shorter.

Thankyou in advance

Aldo Sugiarto
  • 197
  • 3
  • 20
  • As far as I understood your CarItems copied and you work with copy in each view. Move that model array into ProductPresenter and make it observable, and use it from one that place. – Asperi Mar 12 '21 at 06:10
  • `totalQuantity` should be @Published – jnpdx Mar 12 '21 at 06:12
  • @jnpdx , `totalQuantity` is not the one that display the quantity, it's to count the total quantity and check whether there's an item or not in the cart. the one that displaying the each product quantity is `cartItems.quantity`. But thanks for answering, I tried this too but it didn't work – Aldo Sugiarto Mar 12 '21 at 07:21
  • @Asperi , yes I am using the same row but I filtered it with more than 0 quantity to show in the cart view. when I + or - that product in cart view and go back to previous page (product page), it's not updating the Text. While actually on the background, the print value is correct – Aldo Sugiarto Mar 12 '21 at 07:23

2 Answers2

4

Besides passing "ProductPresenter" instance around as "environment" object (which you are handling correctly) you need to declare your properties as @Published, as in this case for "totalQuantity", which now updates the total overall quantity.

class ProductPresenter: ObservableObject {
   @Published var cartItems: [CartItems] = [
      CartItems(item: CartItems.Item(image: "system.car", name: "My item", price: 10, idItem: UUID()), quantity: 3)
   ]
   @Published var totalQuantity = 0 
}

But this doesnt solve this line:

Text("\(cartItems.quantity)") // This Text should be updated.

for following reasons:

  • your model (CartItems) has to be a class type and conform to ObservableObject protocol
  • your property has to be set using @Published.
  • 1
    yes my `CartItems` is a struct model which consist of `var item: Item` and `var quantity: Int`. Is this the one that has to be a class? How can I change it to class since this is a struct model? – Aldo Sugiarto Mar 15 '21 at 03:39
  • Basically you have to have a hold of a class object that can do it for you. Here I made a demo app making this work for you. https://github.com/svoip/ProductAdd – Sardorbek Ruzmatov Mar 16 '21 at 14:32
  • 1
    man it totally work, appreciate your demo app bro. you saved my day. – Aldo Sugiarto Mar 19 '21 at 04:38
0

I think productOrder need to be defined as ObservableObject with @Published properties and int this case it need to be class not a structure, for example:

import SwiftUI
import Combine

final class ProductOder: ObservableObject {
    @Published var cartItems: [CartItem] = []
    
}