Migrate from Core Data to SwiftData

- 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>
This commit is contained in:
Trey t
2025-12-10 15:08:05 -06:00
parent 443f4dfc55
commit aaaf04f05e
51 changed files with 926 additions and 962 deletions

View File

@@ -33,7 +33,7 @@ struct VoteMoodIntent: AppIntent {
let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding())
// Add mood entry
PersistenceController.shared.add(mood: mood, forDate: votingDate, entryType: .widget)
DataController.shared.add(mood: mood, forDate: votingDate, entryType: .widget)
// Store last voted date
let dateString = ISO8601DateFormatter().string(from: Calendar.current.startOfDay(for: votingDate))
@@ -63,19 +63,24 @@ struct VoteWidgetProvider: TimelineProvider {
completion(entry)
return
}
let entry = createEntry()
completion(entry)
Task { @MainActor in
let entry = createEntry()
completion(entry)
}
}
func getTimeline(in context: Context, completion: @escaping (Timeline<VoteWidgetEntry>) -> Void) {
let entry = createEntry()
Task { @MainActor in
let entry = createEntry()
// Refresh at midnight
let midnight = Calendar.current.startOfDay(for: Calendar.current.date(byAdding: .day, value: 1, to: Date())!)
let timeline = Timeline(entries: [entry], policy: .after(midnight))
completion(timeline)
// Refresh at midnight
let midnight = Calendar.current.startOfDay(for: Calendar.current.date(byAdding: .day, value: 1, to: Date())!)
let timeline = Timeline(entries: [entry], policy: .after(midnight))
completion(timeline)
}
}
@MainActor
private func createEntry() -> VoteWidgetEntry {
let hasSubscription = GroupUserDefaults.groupDefaults.bool(forKey: UserDefaultsStore.Keys.hasActiveSubscription.rawValue)
@@ -84,7 +89,7 @@ struct VoteWidgetProvider: TimelineProvider {
let dayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: dayStart)!
// Check if user has voted today
let todayEntry = PersistenceController.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first
let todayEntry = DataController.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first
let hasVotedToday = todayEntry != nil && todayEntry?.mood != Mood.missing && todayEntry?.mood != Mood.placeholder
// Get today's mood if voted
@@ -93,7 +98,7 @@ struct VoteWidgetProvider: TimelineProvider {
// Get stats for display after voting
var stats: MoodStats? = nil
if hasVotedToday {
let allEntries = PersistenceController.shared.getData(
let allEntries = DataController.shared.getData(
startDate: Date(timeIntervalSince1970: 0),
endDate: Date(),
includedDays: []
@@ -188,9 +193,10 @@ struct VotingView: View {
}
} else {
// Horizontal layout for medium/large
HStack(spacing: 12) {
HStack(spacing: 4) {
ForEach(moods, id: \.rawValue) { mood in
MoodButton(mood: mood, isCompact: false)
.frame(maxWidth: .infinity)
}
}
}
@@ -224,6 +230,8 @@ struct MoodButton: View {
Text(mood.widgetDisplayName)
.font(.caption2)
.foregroundStyle(.secondary)
.lineLimit(1)
.minimumScaleFactor(0.7)
}
}
}