0

I am developing a finance management app, and I want to display a bar chart for each spending category. The goal is to show the user a visual representation of their transactions within a specific time period, such as today, this week, this month, or this year. What is the best way to create a bar chart that effectively displays transaction data for each selected time frame?

I have tried to filter the date for each category to see the day and week and month and year of each transactions in the selected category but it dose not display as I want it to. so when the user clicks on day I want the chart to display in hours, to show what time the user did a transaction throughout the day. and when the user clicks on week, I want the chart to change and display the data in days for 7 days. and so on for the rest of them how do I do that? and how do I change make it say on the chart on the x axis that the name of the day or the time. something like this in the images: enter image description here

enter image description here enter image description here


import Foundation
import SwiftUI
import Charts


// for each dateCategory there can be one or more Category and for each Category there can be one or more FinancialTransaction

struct DateCategory {
    let date: Date
    var categories: [Category]
}

let predefinedCategories = ["Groceries", "Eat out", "School", "Petroul", "Car", "Transportation", "Rent", "Home Improvements","Internet","Cell Phone","Electricity","Water","Gas","Cleaning and Home Maintenance Services","Pet Care and Grooming Services","Garden Services","Health Insurance","Doctor Visits","Medecen//","Gym","Personal Care Products","Makup//","Childcare","Pet Food and Supplies","Personal Hygiene Products","Haircuts","Beauty Services","Clothing","Accessories","Jewelry and Watches","Gifts and Special Occasions","Entertainment","Banking","Financial Services and Fees"]

class Category: Identifiable {
    var id = UUID()
    var categoryIndex: Int
    var transactions: [FinancialTransaction]
    var progressbar: Bool
    let date: Date
    var limite: Int
    var usageCount: Int = 0 // New property

    var totalTransactions: Int {
        Int(transactions.reduce(0) { $0 + $1.amount })
    }
    
    required init(categoryIndex: Int, transactions: [FinancialTransaction], progressbar: Bool, limit: Int, date: Date) {
        self.categoryIndex = categoryIndex
        self.transactions = transactions
        self.progressbar = progressbar
        self.limite = limit
        self.date = date
    }
    
    func addTransaction(_ transaction: FinancialTransaction) {
        self.transactions.append(transaction)
        self.usageCount += 1
    }
}
    
    
    struct FinancialTransaction: Identifiable {
        var id = UUID()
        let date: Date
        let amount: Double
    }
    
    
    struct FilteredTransaction {
        let date: Date
        let amount: Int
    }
    
enum TimeFrame: String, CaseIterable {
    case hour
    case day
    case week
    case month
    case year
}



struct MinimalReproducibleExample: View {
    @State private var categories: [Category] = [
        Category(categoryIndex: 0, transactions: [
            FinancialTransaction(date: Calendar.current.date(byAdding: .day, value: -7, to: Date())!, amount: 45.0),
            FinancialTransaction(date: Calendar.current.date(byAdding: .day, value: -1, to: Date())!, amount: 23.0),
            FinancialTransaction(date: Calendar.current.date(byAdding: .month, value: -1, to: Date())!, amount: 200.0),
            FinancialTransaction(date: Calendar.current.date(byAdding: .day, value: -14, to: Date())!, amount: 125.0)
        ], progressbar: true, limit: 500, date: Calendar.current.date(byAdding: .day, value: -3, to: Date())!),

        Category(categoryIndex: 1, transactions: [
            FinancialTransaction(date: Calendar.current.date(byAdding: .day, value: -4, to: Date())!, amount: 78.0),
            FinancialTransaction(date: Calendar.current.date(byAdding: .day, value: -10, to: Date())!, amount: 135.0),
            FinancialTransaction(date: Calendar.current.date(byAdding: .month, value: -2, to: Date())!, amount: 245.0),
            FinancialTransaction(date: Calendar.current.date(byAdding: .day, value: -21, to: Date())!, amount: 67.0)
        ], progressbar: false, limit: 600, date: Calendar.current.date(byAdding: .day, value: -1, to: Date())!)
    ]


    @State private var selectedCategory: Category?
    @State private var selectedTimeFrame: TimeFrame = .day
    
    var body: some View {
        NavigationView {
            VStack {
                // MARK: ALL categorys chart hear:
                //I want to create a bar chart that displays the total transactions for all categories over time, such as a week, a month, and a year
                
                
                Chart{
                    ForEach(categories) { category in
                        BarMark(
                    x: .value("Shape Type", category.date, unit: .day),
                    y: .value("Total Count", category.totalTransactions)
                    )
                    }
                }
                .chartXAxis {
                    AxisMarks (values: .stride (by: .day)) { value in
                AxisGridLine()
                AxisTick()
                AxisValueLabel (
                format: .dateTime.month(.narrow)
                )
                }
                }
                
                List(categories) { category in
                    Button(action: {
                        self.selectedCategory = category
                    }) {
                        Text(predefinedCategories[category.categoryIndex])
                    }
                }
                
                if let category = selectedCategory {
                    VStack {
                        
                        // MARK: selected Category Chart hear:
                        // I want to create a bar chart for each selected category that displays when transactions occurred during a day, week, month, or year. The chart should show the amount or total amount of transactions that occurred during a week. However, this should only be for the selected category.
                        let filteredTransactionschart = getFilteredTransactions(for: category, timeFrame: selectedTimeFrame)
                               
                               Chart{
                                   ForEach(filteredTransactionschart, id: \.id) { transaction in
                                       BarMark(
                                           x: .value("Transaction Date", transaction.date, unit: .day),
                                           y: .value("Transaction Amount", transaction.amount)
                                       )
                                   }
                               }
                               .chartXAxis {
                                   AxisMarks(values: .stride(by: .month)) { value in
                                       AxisGridLine()
                                       AxisTick()
                                       AxisValueLabel(format: .dateTime.month(.narrow))
                                   }
                               }
                        HStack {
                            ForEach(TimeFrame.allCases, id: \.self) { timeFrame in
                                Button(action: {
                                    self.selectedTimeFrame = timeFrame
                                }) {
                                    Text(timeFrame.rawValue.capitalized)
                                        .padding()
                                        .background(selectedTimeFrame == timeFrame ? Color.blue : Color.gray)
                                        .foregroundColor(.white)
                                        .cornerRadius(8)
                                }
                            }
                        }
                        .padding(.bottom)

                        List(filteredTransactions(for: category, in: selectedTimeFrame)) { transaction in
                            Text("Date: \(transaction.date), Amount: \(transaction.amount)")
                        }
                    }
                }
            }
            .navigationTitle("Categories")
        }
    }

    
    
    func createDate(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) -> Date {
        var components = DateComponents()
        components.year = year
        components.month = month
        components.day = day
        components.hour = hour
        components.minute = minute
        components.second = second
        
        let calendar = Calendar.current
        return calendar.date(from: components)!
    }
    
    
    //
    // MARK: filter Chart Transactions
    //

    func getFilteredTransactions(for category: Category, timeFrame: TimeFrame) -> [FinancialTransaction] {
        let currentDate = Date()
        let calendar = Calendar.current

        let filteredTransactions: [FinancialTransaction]

        switch timeFrame {
        case .hour:
            let hourAgoDate = calendar.date(byAdding: .hour, value: -1, to: currentDate)!
            filteredTransactions = category.transactions.filter { $0.date >= hourAgoDate }
        case .day:
            let dayAgoDate = calendar.date(byAdding: .day, value: -1, to: currentDate)!
            filteredTransactions = category.transactions.filter { $0.date >= dayAgoDate }
        case .week:
            let weekAgoDate = calendar.date(byAdding: .weekOfYear, value: -1, to: currentDate)!
            filteredTransactions = category.transactions.filter { $0.date >= weekAgoDate }
        case .month:
            let monthAgoDate = calendar.date(byAdding: .month, value: -1, to: currentDate)!
            filteredTransactions = category.transactions.filter { $0.date >= monthAgoDate }
        case .year:
            let yearAgoDate = calendar.date(byAdding: .year, value: -1, to: currentDate)!
            filteredTransactions = category.transactions.filter { $0.date >= yearAgoDate }
        }

        return filteredTransactions
    }
//
// MARK: filteredTransactions for list
//
func filteredTransactions(for category: Category, in timeFrame: TimeFrame) -> [FinancialTransaction] {
    let now = Date()
    let calendar = Calendar.current

    let filteredTransactions = category.transactions.filter { transaction in
        let components: DateComponents
        switch timeFrame {
        case .hour:
            components = calendar.dateComponents([.hour], from: transaction.date, to: now)
            return components.hour! < 1
        case .day:
            components = calendar.dateComponents([.day], from: transaction.date, to: now)
            return components.day! < 1
        case .week:
            components = calendar.dateComponents([.weekOfYear], from: transaction.date, to: now)
            return components.weekOfYear! < 1
        case .month:
            components = calendar.dateComponents([.month], from: transaction.date, to: now)
            return components.month! < 1
        case .year:
            components = calendar.dateComponents([.year], from: transaction.date, to: now)
            return components.year! < 1
     
        }
    }

    return filteredTransactions
}



    
    
    
    
}


struct MinimalReproducibleExample_Previews: PreviewProvider {
    static var previews: some View {
        MinimalReproducibleExample()
    }
}









  • 3
    Can you include a [mre]? What you've including doesn't build or run – jnpdx May 08 '23 at 18:42
  • Hi, thank you for reaching out. I appreciate you helping me make a Minimal, Reproducible Example. I have updated it to be as minimal as possible. I am having trouble implementing charts into my project, and I hope the issue is clear – Farhad Maleki May 09 '23 at 17:15
  • I hope you can help me with it @jnpdx – Farhad Maleki May 10 '23 at 16:41
  • Your question asks about making a bar chart, but there doesn't seem to be any attempt at making a chart, yet in your question you say "it doesn't even display the chart". So, I'm not sure exactly what the question is here. – jnpdx May 10 '23 at 21:18
  • Dear @jnpdx I've been diligently working on refining the code you've seen previously and have made significant updates. However, I've encountered a new challenge where the chart does not respond as intended. For clarity, I have updated the text above to better articulate the issues I'm experiencing. Your expertise and guidance would be greatly appreciated as I navigate this problem. – Farhad Maleki May 13 '23 at 15:18
  • Sorry -- the scope of this is beyond what I have time to look at. Good luck! – jnpdx May 13 '23 at 15:25

0 Answers0