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>
114 lines
3.8 KiB
Swift
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
|
|
}
|