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()
}
}