MealStatsSetup.swift 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import CoreData
  2. import Foundation
  3. /// Represents statistical data about meal macronutrients for a specific day
  4. struct MealStats: Identifiable {
  5. let id = UUID()
  6. /// The date representing this time period
  7. let date: Date
  8. /// Total carbohydrates in grams
  9. let carbs: Double
  10. /// Total fat in grams
  11. let fat: Double
  12. /// Total protein in grams
  13. let protein: Double
  14. }
  15. extension Stat.StateModel {
  16. /// Initiates the process of fetching and processing meal statistics
  17. /// - Parameter duration: The time period to fetch records for
  18. func setupMealStats(for duration: Duration) {
  19. Task {
  20. let stats = await fetchMealStats(for: duration)
  21. await MainActor.run {
  22. self.mealStats = stats
  23. }
  24. }
  25. }
  26. /// Fetches and processes meal statistics for a specific duration
  27. /// - Parameter duration: The time period to fetch records for (Today, 24h, 7 Days, 30 Days, or All)
  28. /// - Returns: Array of MealStats containing daily meal statistics, sorted by date
  29. private func fetchMealStats(for duration: Duration) async -> [MealStats] {
  30. let now = Date()
  31. let calendar = Calendar.current
  32. // Determine start date based on selected duration
  33. // For Today and 24h, we show 3 days of data for better context
  34. // For other durations, we fetch the respective time period
  35. let startDate: Date
  36. switch duration {
  37. case .Today:
  38. startDate = calendar.date(byAdding: .day, value: -2, to: calendar.startOfDay(for: now))!
  39. case .Day:
  40. startDate = calendar.date(byAdding: .day, value: -2, to: calendar.startOfDay(for: now))!
  41. case .Week:
  42. startDate = calendar.date(byAdding: .day, value: -7, to: calendar.startOfDay(for: now))!
  43. case .Month:
  44. startDate = calendar.date(byAdding: .month, value: -1, to: calendar.startOfDay(for: now))!
  45. case .Total:
  46. startDate = calendar.date(byAdding: .month, value: -3, to: calendar.startOfDay(for: now))!
  47. }
  48. // Fetch CarbEntryStored entries from Core Data
  49. let results = await CoreDataStack.shared.fetchEntitiesAsync(
  50. ofType: CarbEntryStored.self,
  51. onContext: mealTaskContext,
  52. predicate: NSPredicate(format: "date >= %@", startDate as NSDate),
  53. key: "date",
  54. ascending: false,
  55. batchSize: 100
  56. )
  57. return await mealTaskContext.perform {
  58. // Safely unwrap the fetched results, return empty array if nil
  59. guard let fetchedResults = results as? [CarbEntryStored] else { return [] }
  60. // Group entries by day using calendar's startOfDay
  61. // This ensures all entries within the same day are grouped together
  62. // regardless of their specific time
  63. let groupedEntries = Dictionary(grouping: fetchedResults) { entry in
  64. calendar.startOfDay(for: entry.date ?? Date())
  65. }
  66. // Create array of all dates in the range
  67. // This ensures we have entries for every day in the range,
  68. // even if there are no meal entries for some days
  69. var dates: [Date] = []
  70. var currentDate = startDate
  71. while currentDate <= now {
  72. dates.append(calendar.startOfDay(for: currentDate))
  73. currentDate = calendar.date(byAdding: .day, value: 1, to: currentDate)!
  74. }
  75. // Calculate statistics for each day
  76. // For days without entries, all values will be 0
  77. return dates.map { date in
  78. let entries = groupedEntries[date, default: []]
  79. // Sum up macronutrients for the day
  80. // Each reduce operation calculates the total for one macronutrient
  81. let carbsTotal = entries.reduce(0.0) { $0 + $1.carbs } // Total carbs in grams
  82. let fatTotal = entries.reduce(0.0) { $0 + $1.fat } // Total fat in grams
  83. let proteinTotal = entries.reduce(0.0) { $0 + $1.protein } // Total protein in grams
  84. return MealStats(
  85. date: date,
  86. carbs: carbsTotal,
  87. fat: fatTotal,
  88. protein: proteinTotal
  89. )
  90. }.sorted { $0.date < $1.date } // Sort results by date in ascending order
  91. }
  92. }
  93. }