Files
Reflect/Shared/Protocols/ChartDataBuildable.swift
Trey t 0442eab1f8 Rebrand entire project from Feels to Reflect
Complete rename across all bundle IDs, App Groups, CloudKit containers,
StoreKit product IDs, data store filenames, URL schemes, logger subsystems,
Swift identifiers, user-facing strings (7 languages), file names, directory
names, Xcode project, schemes, assets, and documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:47:16 -06:00

114 lines
3.8 KiB
Swift

//
// ChartDataBuildable.swift
// Reflect
//
// Created by Trey Tartt on 1/17/22.
//
import SwiftUI
typealias Year = Int
typealias Month = Int
protocol ChartDataBuildable {
associatedtype ChartType: ChartViewItemBuildable
// [Year: [Month: [View]]
func buildGridData(withData data: [MoodEntryModel]) -> [Year: [Month: [ChartType]]]
}
extension ChartDataBuildable {
public func buildGridData(withData data: [MoodEntryModel]) -> [Year: [Month: [ChartType]]] {
guard let earliestEntry = data.first,
let lastEntry = data.last else { return [:] }
let calendar = Calendar.current
// Pre-group entries by year/month/day in a single pass - O(n) instead of O(n*m)
// Key: "year-month-day" -> entry
var entriesByDate = [String: MoodEntryModel]()
for entry in data {
let components = calendar.dateComponents([.year, .month, .day], from: entry.forDate)
let key = "\(components.year!)-\(components.month!)-\(components.day!)"
entriesByDate[key] = entry
}
let earliestYear = calendar.component(.year, from: earliestEntry.forDate)
let latestYear = calendar.component(.year, from: lastEntry.forDate)
// Cache expensive lookups
let moodTint: MoodTintable.Type = UserDefaultsStore.moodTintable()
let shape = UserDefaultsStore.getCustomBGShape()
var returnData = [Int: [Int: [ChartType]]]()
for year in earliestYear...latestYear {
var allMonths = [Int: [ChartType]]()
for month in 1...12 {
var components = DateComponents()
components.month = month
components.year = year
guard let startDateOfMonth = calendar.date(from: components) else { continue }
allMonths[month] = createViewFor(
year: year,
month: month,
forMonth: startDateOfMonth,
entriesByDate: entriesByDate,
moodTint: moodTint,
shape: shape,
calendar: calendar
)
}
returnData[year] = allMonths
}
return returnData
}
private func createViewFor(
year: Int,
month: Int,
forMonth monthDate: Date,
entriesByDate: [String: MoodEntryModel],
moodTint: MoodTintable.Type,
shape: BGShape,
calendar: Calendar
) -> [ChartType] {
var filledOutArray = [ChartType]()
let range = calendar.range(of: .day, in: .month, for: monthDate)!
let numDays = range.count
for day in 1...numDays {
let key = "\(year)-\(month)-\(day)"
if let item = entriesByDate[key] {
// O(1) dictionary lookup instead of O(n) filter
let view = ChartType(color: moodTint.color(forMood: item.mood),
weekDay: Int(item.weekDay),
shape: shape)
filledOutArray.append(view)
} else {
let thisDate = calendar.date(bySetting: .day, value: day, of: monthDate)!
let view = ChartType(color: Mood.placeholder.color,
weekDay: calendar.component(.weekday, from: thisDate),
shape: shape)
filledOutArray.append(view)
}
}
for _ in filledOutArray.count...32 {
let view = ChartType(color: Mood.placeholder.color,
weekDay: 2,
shape: shape)
filledOutArray.append(view)
}
return filledOutArray
}
}
struct DayChartViewChartBuilder: ChartDataBuildable {
typealias ChartType = DayChartView
}