iosswiftswiftuistepper

How to calculate total with Stepper onIncrement and onDecrement


I have the simple add function which can calculate the total . It working fine . I want to update grand total when + and - is clicked form stepper . Not sure How can I pass the total variable into stepper ..

Here is the modal ..

import Foundation

// MARK: - Welcome
struct ProductData: Codable, Hashable {
    let carts: [Cart]
}

// MARK: - Cart
struct Cart: Codable, Hashable {
    let id: Int
    let products: [Product]
}

// MARK: - Product
struct Product: Codable, Hashable, Identifiable {
    let id: Int
    let title: String
    var price, quantity, total: Int
    let discountPercentage: Double
    let discountedPrice: Int
    let thumbnail: String
}

Here is my class ..

import SwiftUI

class Order: ObservableObject {

    @Published var products = [Product]()
    @Published private(set) var productTotal: Int = 0

    func add(item: Product) {
        if let index = products.firstIndex(where: {$0.id == item.id }) {
            products[index].quantity += 1
            productTotal += item.quantity * item.price

        } else {
            products.append(item)
            productTotal += item.quantity * item.price
        }
    }

    func remove(item: Product) {
        if let index = products.firstIndex(of: item) {
            products.remove(at: index)
            productTotal -= item.quantity * item.price
        }
    }
}

Be more specific.. Here is the Stepper code ..

 VStack {
    HStack {
         Stepper {
           Text("\(Image(systemName: "multiply"))\(item.quantity)")
                      }
                   onIncrement: {
                        item.quantity += 1
                              }
                                onDecrement: {
                                    if item.quantity > 1 {
                                        item.quantity -= 1
                                    }
                                }
                 Image(systemName: "trash")
              .foregroundColor(Color(hue: 1.0, saturation:0.89, brightness: 0.835))
                                    .onTapGesture {
                                    order.remove(item: item)
                                    }
                                }
                            }

Here is the view code for calculating total..

             HStack {
                Text("Your cart total is :")
                        .bold()
                    Spacer()
                    Text("£\(order.productTotal).00")
                        .bold()
                }
                .padding()

Here is complete my view code ..

import SwiftUI

struct OrderView: View {
    @EnvironmentObject var order: Order

    var body: some View {
        NavigationStack {
            List {
                Section {
                    ForEach.init($order.products) { $item in
                        Section {
                            HStack {
                                Text("Description")
                                    .foregroundColor(.blue)
                                    .bold()
                                Spacer()
                                Text("Action")
                                    .foregroundColor(.blue)
                                    .bold()
                            }
                        }

                        HStack {
                            if let url = URL(string: item.thumbnail) {
                                ProductAsyncImageView(url: url)
                                    .frame(width: 90, height: 90)
                                    .padding(.bottom, -10)
                                    .clipShape(Circle())
                            }
                            Spacer()

                            VStack {
                                HStack {
                                    Stepper {
                                        Text("\(Image(systemName: "multiply"))\(item.quantity)")

                                    }
                                onIncrement: {
                                    item.quantity += 1
                                  // increment function 

                                }
                                onDecrement: {
                                    if item.quantity > 1 {
                                        item.quantity -= 1
                                    // decrement function
                                    }
                                }
                                Image(systemName: "trash")
                                    .foregroundColor(Color(hue: 1.0, saturation: 0.89, brightness: 0.835))
                                    .onTapGesture {
                                    order.remove(item: item)
                                    }
                                }
                            }
                            Spacer()
                        }

                        VStack {
                            HStack(spacing: 20) {
                                Text("Name: \(item.title)")
                                    .font(.subheadline)
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                    .bold()
                            }
                            HStack {
                                Text("Price: £\(item.price)")
                                    .font(.subheadline)
                                    .bold()
                                    .frame(maxWidth: .infinity, alignment: .leading)
                                Text("Sub Total: £\(item.quantity * item.price)")
                                    .font(.subheadline)
                                    .bold()
                                    .frame(maxWidth: .infinity, alignment: .leading)
                            }
                        }
                    }
                    HStack {
                        Text("Your cart total is :")
                            .bold()
                        Spacer()
                        Text("£\(order.productTotal).00")
                            .bold()
                    }
                    .padding()
                }
                Section {
                    NavigationLink("Place Order") {
                    }
                }
            }
            .navigationTitle("Order")
        }
    }
}

Solution

  • The simplest way is probably to add a function to your model - Also, an Int probably isn't a good choice for representing currency. NSDecimalNumber is best, but for simplicity we can use a Double.

    class Order: ObservableObject {
    
        @Published var products = [Product]()
        @Published private(set) var productTotal: Double = 0
    
        func add(item: Product) {
            if let index = products.firstIndex(where: {$0.id == item.id }) {
                products[index].quantity += 1
      
            } else {
                products.append(item)
            }
            self.calculateTotal()
        }
    
        func remove(item: Product) {
            if let index = products.firstIndex(of: item) {
                products.remove(at: index)
                self.calculateTotal()
          
            }
        }
    
        func calculateTotal() {
            self.productTotal = self.products.reduce(0.0, { total, item in 
                total+Double(item.quantity)*item.price
            }
        }
    }
    

    You can also use this function when you increment or decrement an item quantity:

    Stepper {
        Text("\(Image(systemName: "multiply"))\(item.quantity)")
    }
    onIncrement: {
        item.quantity += 1
        self.order.calculateTotal()
    }
    
    onDecrement: {
        if item.quantity > 1 {
            item.quantity -= 1
            self.order.calculateTotal()
        }
    }