- Replace Core Data with SwiftData for iOS 18+ - Create MoodEntryModel as @Model class replacing MoodEntry entity - Create SharedModelContainer for App Group container sharing - Create DataController with CRUD extensions replacing PersistenceController - Update all views and view models to use MoodEntryModel - Update widget extension to use SwiftData - Remove old Core Data files (Persistence*.swift, .xcdatamodeld) - Add EntryType enum with all entry type cases - Fix widget label truncation with proper spacing and text scaling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
86 lines
2.6 KiB
Swift
86 lines
2.6 KiB
Swift
//
|
|
// DataControllerGET.swift
|
|
// Feels
|
|
//
|
|
// SwiftData READ operations.
|
|
//
|
|
|
|
import SwiftData
|
|
import Foundation
|
|
|
|
extension DataController {
|
|
func getEntry(byDate date: Date) -> MoodEntryModel? {
|
|
let startDate = Calendar.current.startOfDay(for: date)
|
|
let endDate = Calendar.current.date(byAdding: .day, value: 1, to: startDate)!
|
|
|
|
var descriptor = FetchDescriptor<MoodEntryModel>(
|
|
predicate: #Predicate { entry in
|
|
entry.forDate >= startDate && entry.forDate <= endDate
|
|
},
|
|
sortBy: [SortDescriptor(\.forDate, order: .forward)]
|
|
)
|
|
descriptor.fetchLimit = 1
|
|
|
|
return try? modelContext.fetch(descriptor).first
|
|
}
|
|
|
|
func getData(startDate: Date, endDate: Date, includedDays: [Int]) -> [MoodEntryModel] {
|
|
let weekDays = includedDays.isEmpty ? [1, 2, 3, 4, 5, 6, 7] : includedDays
|
|
|
|
let descriptor = FetchDescriptor<MoodEntryModel>(
|
|
predicate: #Predicate { entry in
|
|
entry.forDate >= startDate &&
|
|
entry.forDate <= endDate &&
|
|
weekDays.contains(entry.weekDay)
|
|
},
|
|
sortBy: [SortDescriptor(\.forDate, order: .forward)]
|
|
)
|
|
|
|
return (try? modelContext.fetch(descriptor)) ?? []
|
|
}
|
|
|
|
func splitIntoYearMonth(includedDays: [Int]) -> [Int: [Int: [MoodEntryModel]]] {
|
|
let data = getData(
|
|
startDate: Date(timeIntervalSince1970: 0),
|
|
endDate: Date(),
|
|
includedDays: includedDays
|
|
).sorted { $0.forDate < $1.forDate }
|
|
|
|
guard let earliest = data.first,
|
|
let latest = data.last else { return [:] }
|
|
|
|
let calendar = Calendar.current
|
|
let earliestYear = calendar.component(.year, from: earliest.forDate)
|
|
let latestYear = calendar.component(.year, from: latest.forDate)
|
|
|
|
var result = [Int: [Int: [MoodEntryModel]]]()
|
|
|
|
for year in earliestYear...latestYear {
|
|
var monthData = [Int: [MoodEntryModel]]()
|
|
|
|
for month in 1...12 {
|
|
var components = DateComponents()
|
|
components.year = year
|
|
components.month = month
|
|
components.day = 1
|
|
|
|
guard let startOfMonth = calendar.date(from: components) else { continue }
|
|
|
|
let items = getData(
|
|
startDate: startOfMonth,
|
|
endDate: startOfMonth.endOfMonth,
|
|
includedDays: [1, 2, 3, 4, 5, 6, 7]
|
|
)
|
|
|
|
if !items.isEmpty {
|
|
monthData[month] = items
|
|
}
|
|
}
|
|
|
|
result[year] = monthData
|
|
}
|
|
|
|
return result
|
|
}
|
|
}
|