Home > Software engineering >  Can’t get the sum of ForEach items
Can’t get the sum of ForEach items

Time:09-10

I'm trying to display the total of the Depot Values. One depot can have many stocks.

My Overview Page has a List of Depots and should have a Text with the total value of all depots.

I get the value of one depot as following: (connected in One-To-Many-Relationship)

let sum = depot.aktienArray.map { $0.a_purchValue }.reduce(0,  )

Portfolio View:

struct Portfolio: View {
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Depot.d_name, ascending: true)],
        animation: .default)
    private var depots: FetchedResults<Depot>
    
    // General States
    @State private var showAddDepotView: Bool = false
    // @State private var //left header button
    @State private var isShowingConfirmation: Bool = false
    @State private var isShowingMenu: Bool = false
    @State private var navigateTo: AnyView?
    @State private var isNavActive = false
    @State private var depotToDelete: Depot?
        
    @State var PortVal: Double = 0
    @State var sum: Double = 0
    
    var body: some View {
        
        NavigationView {
            GeometryReader { g in
                VStack(spacing: 0) {
                                        
                    //MARK: DEPOT OVERVIEW
                    VStack {
                        VStack(alignment: .leading) {
                            HStack {
                                Text("Portfolio Value")
                                    .font(.system(size: g.size.width / 22))
                                    .foregroundColor(Color.gray)
                                Spacer()
                                Text("Details")
                                    .foregroundColor(Color.white)
                                    .font(.system(size: g.size.width / 22))
                                Image(systemName: "chevron.right")
                                    .foregroundColor(Color.white)
                                    .padding(.leading, g.size.width / -100)
                            }
                            .padding(.horizontal, g.size.width / 40)
                            .padding(.bottom, g.size.height / -45)
                            .padding(.top, g.size.height / 100)
                            
                            HStack(alignment: .top) {
                                Text("\(PortVal as NSNumber, formatter: formatter) €")
                                    .bold()
                                    .foregroundColor(Color.blue)
                                    .minimumScaleFactor(0.4)
                                    .font(.system(size: g.size.width / 10))
                                    .lineLimit(1)
                                Text(" 22,3 %")
                                    .bold()
                                    .foregroundColor(Color.green)
                                    .font(.system(size: g.size.width / 20))
                            }
                            .padding(.horizontal, g.size.width / 40)
                            .padding(.bottom, g.size.height / 800)
                            
                            HStack {
                                VStack(alignment: .leading) {
                                    Text("Liquidität")
                                        .font(.system(size: g.size.width / 22))
                                        .foregroundColor(Color.gray)
                                        .padding(.bottom, g.size.height / -75)
                                    Text("339.830,87 €")
                                        .bold()
                                        .foregroundColor(Color.blue)
                                        .minimumScaleFactor(0.4)
                                        .font(.system(size: g.size.width / 14))
                                        .lineLimit(1)
                                }
                                Spacer()
                                VStack(alignment: .center) {
                                    Text("Yield")
                                        .font(.system(size: g.size.width / 22))
                                        .foregroundColor(Color.gray)
                                        .padding(.bottom, g.size.height / -75)
                                    Text("5,80 %")
                                        .bold()
                                        .foregroundColor(Color.blue)
                                        .font(.system(size: g.size.width / 14))
                                }
                                Spacer()
                            }
                            .padding(.leading, g.size.width / 40)
                            .padding(.bottom, g.size.height / 120)
                        }
                        .frame(height: UIScreen.main.bounds.height / 5.5)
                        .frame(maxWidth: .infinity)
                        .background(RoundedRectangle(cornerRadius: 15).fill(Color.white.opacity(0.1)))
                    }
                    .padding(.horizontal, g.size.width / 30)
                    .padding(.vertical, g.size.height / 65)
                    .padding(.bottom, g.size.height / 150)
                    //
                    
                    //MARK: DEPOT LIST
                    // DEPOT LIST TITLE
                    VStack(spacing: 0) {
                        Divider()
                        HStack {
                            Text("Depots")
                                .bold()
                                .font(.system(size: g.size.width / 12))
                                .foregroundColor(.white)
                            Spacer()
                        }
                        .padding(.horizontal, g.size.width / 30)
                        .padding(.top, g.size.width / 40)
                        .padding(.bottom, g.size.width / 100)
                        Divider()
                            .overlay(.white)
                            .padding(.horizontal, g.size.width / 30)
                    }
                    
                    
                    
                    //DEPOT LIST
                    VStack(alignment: .leading) {
                        ScrollView(showsIndicators: false) {
                            Color.clear
                                .padding(.bottom, g.size.height / -200)
                            ForEach(depots) { depot in
                                
                                let sum = depot.aktienArray.map { $0.a_purchValue }.reduce(0,  )

                                ZStack {
                                    NavigationLink(destination: DepotDetail(depot: depot)) {
                                        VStack(alignment: .leading, spacing: 6) {
                                            HStack {
                                                VStack (alignment: .leading){
                                                    HStack (alignment: .bottom) {
                                                        Text(depot.d_name ?? "")
                                                            .font(.system(size: 22))
                                                            .foregroundColor(Color.GrayD)
                                                            .bold()
                                                        Text("Depot-Nr.: \(depot.d_nr as NSNumber, formatter: formatter2)")
                                                            .foregroundColor(.gray)
                                                            .font(.system(size: 16))
                                                        Spacer()
                                                    }
                                                    Text("Eröffnet: \(depot.d_createDate ?? Date(), formatter: formatterD)")
                                                        .font(.system(size: 15))
                                                        .foregroundColor(.GrayD)
                                                }
                                            }.padding(.bottom, -0.5)
                                                .padding(.top, 40)
                                            HStack {
                                                VStack (alignment: .leading) {
                                                    Text("Depot Value")
                                                        .font(.system(size: 18))
                                                        .foregroundColor(Color.gray)
                                                    if sum == 0 {
                                                    Text("0,00 €")
                                                            .bold()
                                                            .foregroundColor(Color.GrayD)
                                                            .minimumScaleFactor(0.32)
                                                            .font(.system(size: g.size.width / 4))
                                                            .lineLimit(1)
                                                            .padding(.bottom, -5)
                                                    } else {
                                                    Text("\(sum as NSNumber, formatter: formatter) €")
                                                        .bold()
                                                        .foregroundColor(Color.GrayD)
                                                        .minimumScaleFactor(0.32)
                                                        .font(.system(size: g.size.width / 4))
                                                        .lineLimit(1)
                                                        .padding(.bottom, -5)
                                                    }
                                                    Text("Performance")
                                                        .font(.system(size: 18))
                                                        .foregroundColor(Color.gray)
                                                    HStack {
                                                        Text("360,00 €")
                                                            .bold()
                                                            .foregroundColor(Color.green)
                                                            .font(.system(size: 25))
                                                            .lineLimit(1)
                                                        Text(" 3,6 %")
                                                            .bold()
                                                            .foregroundColor(Color.green)
                                                            .font(.system(size: 18))
                                                    }
                                                }
                                                Spacer()
                                                VStack(alignment: .leading) {
                                                    Text("Yield")
                                                        .font(.system(size: 18))
                                                        .foregroundColor(Color.gray)
                                                    Text("5,80 %")
                                                        .bold()
                                                        .foregroundColor(Color.GrayD)
                                                        .font(.system(size: 25))
                                                        Spacer()
                                                    Text("FSA")
                                                        .font(.system(size: 18))
                                                        .foregroundColor(Color.gray)
                                                    Text("\(depot.d_fsa as NSNumber, formatter: formatter) €")
                                                        .bold()
                                                        .foregroundColor(Color.GrayD)
                                                        .font(.system(size: 25))
                                                }
                                            }
                                            Spacer()
                                            Spacer()
                                            Spacer()
                                        }
                                        .padding(.horizontal, g.size.width / 40)
                                        .frame(height: UIScreen.main.bounds.height / 4.7)
                                        .frame(maxWidth: .infinity)
                                        .background(RoundedRectangle(cornerRadius: 15).fill(Color.white))
                                    }.buttonStyle(FlatLinkStyle())
                                    Menu {
                                        Button {
                                            self.navigateTo = AnyView(EditDepot(depot: depot))
                                            self.isNavActive = true
                                        } label: {
                                            Label("Bearbeiten", systemImage: "pencil")
                                        }
                                        Button(role: .destructive) {
                                            depotToDelete = depot
                                            isShowingConfirmation = true
                                        } label: {
                                            Label("Löschen", systemImage: "trash")
                                        }
                                    } label: {
                                        Image(systemName: "ellipsis.circle").foregroundColor(.GrayD)
                                    }
                                    .padding(.bottom, g.size.height / 5.75)
                                    .padding(.leading, g.size.width / 1.25)
                                    .confirmationDialog("Depot", isPresented: $isShowingConfirmation, titleVisibility: .visible) {
                                        if let depot = depotToDelete {
                                            Text("Depot \"\(depot.d_name ?? "")\" wirklich löschen?")
                                        }
                                        Button(role: .destructive) {
                                            if let depot = depotToDelete {
                                                deleteDepot(depot: depot)
                                            }
                                        } label: {
                                            if let depot = depotToDelete {
                                                Text("\"\(depot.d_name ?? "")\" wirklich löschen?")
                                            }
                                        }
                                    }
                                    .background(
                                        NavigationLink(destination: self.navigateTo, isActive: $isNavActive) {
                                            EmptyView()
                                        }
                                    )
                                }
                                .onAppear {
                                    PortVal = 0
                                    PortVal   sum
                                }
//                                .onChange(of: sum, perform: { _ in
//                                    PortVal = 0
//                                    PortVal  = sum
//
//                                })
                            }
                            
                            
                            
                            // DEPOT ADD BUTTON
                            VStack {
                                HStack {
                                    Button {
                                        showAddDepotView.toggle()
                                    } label: {
                                        HStack {
                                            Image(systemName: "plus")
                                            Text("Depot hinzufügen")
                                                .font(.headline)
                                        }
                                        .foregroundColor(.white)
                                    }
                                }
                                .frame(height: UIScreen.main.bounds.height / 20)
                                .frame(maxWidth: .infinity)
                                .background(RoundedRectangle(cornerRadius: 15).fill(Color.white.opacity(0.1)))
                            }
                            .padding(.top, g.size.height / 50)
                        }
                    }
                    .padding(.horizontal, g.size.width / 30)
                    
                }
                .background(Color.GrayD.ignoresSafeArea())
                .fullScreenCover(isPresented: $showAddDepotView, content: AddDepot.init)
                .preferredColorScheme(.dark)
            }
            .navigationTitle("")
            .navigationBarHidden(true)
        }
    }
    
    private func deleteDepot(depot: Depot) {
        withAnimation {
            viewContext.delete(depot)
            do {
                try viewContext.save()
            } catch {
                print(error)
            }
        }
    }
}

DepotDetail View:

import SwiftUI

struct DepotDetail: View {
    @Environment(\.managedObjectContext) private var viewContext
    @Environment(\.presentationMode) var presentationMode
    
    // CoreData States
    @StateObject var depot: Depot
    @State private var aktieToDelete: AktieKauf?
    @State private var a_name: String = ""
    @State private var a_industry: String = ""
    @State private var a_segment: String = ""
    @State private var a_shares: Double = 0
    @State private var a_purchPrice: Double = 0
    @State private var a_purchValue: Double = 0
    @State private var a_expDividend: Double = 0
    @State private var a_fees: Double = 0
    @State private var a_ertrag: Double = 0
    @State private var a_purchDate: Date = Date()
    
    // General States
    @State private var showAddStockView: Bool = false
    @State private var isShowingConfirmation: Bool = false
    @State private var navigateTo: AnyView?
    @State private var isNavActive = false
    
    var body: some View {
       
        GeometryReader { g in
            VStack(spacing: 0) {
                
                Text("\(PortfolioValue() as NSNumber, formatter: formatter) €")
                // Image(systemName: "ellipsis")
                
                //MARK: BESTAND
                VStack(alignment: .leading) {
                    ScrollView(showsIndicators: false) {
                        Color.clear
                            .padding(.bottom, g.size.height / -200)
                        ForEach(depot.aktienArray) { aktie in
                            ZStack {
                                //NavigationLink(destination: StockDetail(depot: depot)) {
                                VStack(alignment: .leading, spacing: 6) {
                                        HStack {
                                            VStack (alignment: .leading){
                                                HStack (alignment: .bottom) {
                                                    Text(aktie.unwrappedName)
                                                        .font(.system(size: 22))
                                                        .foregroundColor(Color.GrayD)
                                                        .bold()
                                                    Text("\(aktie.a_purchValue)")
                                                        .foregroundColor(.black)
                                                    Text(aktie.a_industry ?? "").foregroundColor(.black)
                                                    Spacer()
                                                }
                                                Text("Gekauft: \(aktie.a_purchDate ?? Date(), style: .date)")
                                                    
                                                    .foregroundColor(.GrayD)
                                            }
                                        }
                                        .padding(.bottom, -0.5)
                                        .padding(.top, 40)
                                        HStack {
                                            VStack (alignment: .leading) {
                                                Text("Depot Value")
                                                    
                                                    .foregroundColor(Color.gray)
                                                Text("360.000,00 €")
                                                    .bold()
                                                    
                                                    
                                                    .font(.system(size: g.size.width / 4))
                                                    .lineLimit(1)
                                            }
                                            Spacer()
                                            VStack(alignment: .center) {
                                                Text("Yield")
                                                    .font(.system(size: 18))
                                                    .foregroundColor(Color.gray)
                                                Text("5,80 %")
                                                    .bold()
                                                    .foregroundColor(Color.GrayD)
                                                    
                                            }
                                        }
                                        HStack {
                                            VStack(alignment: .leading) {
                                                Text("Performance")
                                                    .font(.system(size: 18))
                                                    
                                                HStack {
                                                    Text("360,00 €")
                                                        .bold()
                                                        
                                                        .font(.system(size: 25))
                                                        .lineLimit(1)
                                                    Text(" 3,6 %")
                                                        .bold()
                                                        .foregroundColor(Color.green)
                                                        
                                                }
                                            }
                                        }
                                        Spacer()
                                        Spacer()
                                        Spacer()
                                    }
                                    .padding(.horizontal, g.size.width / 40)
                                    .frame(height: UIScreen.main.bounds.height / 4.7)
                                    .frame(maxWidth: .infinity)
                                    .background(RoundedRectangle(cornerRadius: 15).fill(Color.white))
                                //}
                                //.buttonStyle(FlatLinkStyle())
                                Menu {
                                    Button {
                                        self.navigateTo = AnyView(EditDepot(depot: depot))
                                        self.isNavActive = true
                                    } label: {
                                        Label("Bearbeiten", systemImage: "pencil")
                                    }
                                    Button(role: .destructive) {
                                        aktieToDelete = aktie
                                        isShowingConfirmation = true
                                    } label: {
                                        Label("Löschen", systemImage: "trash")
                                    }
                                } label: {
                                    Image(systemName: "ellipsis.circle").foregroundColor(.GrayD)
                                }
                                .padding(.bottom, g.size.height / 5.75)
                                .padding(.leading, g.size.width / 1.25)
                                .confirmationDialog("Aktie", isPresented: $isShowingConfirmation, titleVisibility: .visible) {
                                    if let aktie = aktieToDelete {
                                        Text("Aktie \"\(aktie.a_name ?? "?")\" wirklich löschen?")
                                    }
                                    Button(role: .destructive) {
                                        if let aktie = aktieToDelete {
                                            deleteStock(aktieKauf: aktie)
                                        }
                                    } label: {
                                        if let aktie = aktieToDelete {
                                            Text("\"\(aktie.a_name ?? "?")\" wirklich löschen?")
                                        }
                                    }
                                }
                                //.background(
                                    //NavigationLink(destination: self.navigateTo, //isActive: $isNavActive) {
                                //        EmptyView()
                                //    }
                                //)
                            }
                        }
                    }
                }
                .padding(.horizontal, g.size.width / 30)
                //
                
                Spacer()
                
            }
            .background(Color.GrayD.ignoresSafeArea())
            .fullScreenCover(isPresented: $showAddStockView) {
                AddStock(depot: depot)
            }
            .preferredColorScheme(.dark)
        }
        .navigationBarTitle("")
        .navigationBarHidden(true)
    
    }
    
    private func addStock() {
        withAnimation {
            let newAktie = AktieKauf(context: viewContext)
            newAktie.a_name = a_name
            newAktie.a_industry = a_industry
            newAktie.a_segment = a_segment
            newAktie.a_shares = a_shares
            newAktie.a_purchPrice = a_purchPrice
            newAktie.a_purchValue = a_purchValue
            newAktie.a_expDividend = a_expDividend
            newAktie.a_fees = a_fees
            newAktie.a_ertrag = a_ertrag
            newAktie.a_purchDate = a_purchDate
            
            depot.addToAktieKaufRel(newAktie)
            PersistenceController.shared.saveContext()
        }
    }
    
    private func deleteStock(aktieKauf: AktieKauf) {
        withAnimation {
            viewContext.delete(aktieKauf)
            do {
                try viewContext.save()
            } catch {
                print(error)
            }
        }
    }
    
    func deleteAktie(at offsets: IndexSet) {
        withAnimation {
            for index in offsets {
                let aktie = depot.aktienArray[index]
                viewContext.delete(aktie)
                PersistenceController.shared.saveContext()
            }
        }
    }
    
    private func PortfolioValue() -> Double {
        var portfolioValue: Double = 0
        for item in depot.aktienArray {
            portfolioValue  = item.a_purchValue
        }
        return portfolioValue
    }
    
    
}

CodePudding user response:

With SwiftUI you want to create a ViewModel that represents something that's the formatted version of your model. Something that's as easy as possible to map to views. You don't want to be doing calculation and logic within a View's body.

So for example:

extension Depot {
   func sumOfStuff() -> Double {
       aktienArray.map(\.a_purchValue).reduce(0,  )
   }
}

That would be your model, then your view model might be that value stringified for presentation.

extension DepotViewModel {
    var presentableSumOfStuff: String {
       if depot.sumOfStuff() == 0 { return "0" } else { ... }

The problem is this "normal swift code" inside your ViewBuilder block:

ForEach(depots) { depot in
   let sum = depot.aktienArray.map { $0.a_purchValue }.reduce(0,  ) // This should not be here
   ZStack {

Within a ViewBuilder context, you can't put normal swift code, it should just be a list of Views.

If you set up your ViewModel right, then you don't have complex logic in the view like instead of this:

if sum == 0 {
    Text("0,00 €")
        .bold()
        .foregroundColor(Color.GrayD)
        .minimumScaleFactor(0.32)
        .font(.system(size: g.size.width / 4))
        .lineLimit(1)
        .padding(.bottom, -5)
} else {
    Text("\(sum as NSNumber, formatter: formatter) €")
        .bold()
        .foregroundColor(Color.GrayD)
        .minimumScaleFactor(0.32)
        .font(.system(size: g.size.width / 4))
        .lineLimit(1)
        .padding(.bottom, -5)
}

You would have

    Text($0.presentableSumOfStuff)
        .bold()
        .foregroundColor(Color.GrayD)
        .minimumScaleFactor(0.32)
        .font(.system(size: g.size.width / 4))
        .lineLimit(1)
        .padding(.bottom, -5)
  • Related