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:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
import Intents
|
||||
import CoreData
|
||||
import SwiftData
|
||||
|
||||
class WatchTimelineView: Identifiable {
|
||||
let id = UUID()
|
||||
@@ -28,7 +28,7 @@ class WatchTimelineView: Identifiable {
|
||||
}
|
||||
|
||||
struct TimeLineCreator {
|
||||
static func createViews(daysBack: Int) -> [WatchTimelineView] {
|
||||
@MainActor static func createViews(daysBack: Int) -> [WatchTimelineView] {
|
||||
var timeLineView = [WatchTimelineView]()
|
||||
|
||||
let latestDayToShow = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: UserDefaultsStore.getOnboarding())
|
||||
@@ -42,7 +42,7 @@ struct TimeLineCreator {
|
||||
let moodTint: MoodTintable.Type = UserDefaultsStore.moodTintable()
|
||||
let moodImages: MoodImagable.Type = UserDefaultsStore.moodMoodImagable()
|
||||
|
||||
if let todayEntry = PersistenceController.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first {
|
||||
if let todayEntry = DataController.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first {
|
||||
timeLineView.append(WatchTimelineView(image: moodImages.icon(forMood: todayEntry.mood),
|
||||
graphic: moodImages.icon(forMood: todayEntry.mood),
|
||||
date: dayStart,
|
||||
@@ -99,7 +99,7 @@ struct Provider: IntentTimelineProvider {
|
||||
timeLineViews: TimeLineCreator.createSampleViews(count: 10))
|
||||
}
|
||||
|
||||
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
|
||||
@MainActor func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
|
||||
// Use sample data for widget picker preview, real data otherwise
|
||||
let timeLineViews: [WatchTimelineView]
|
||||
if context.isPreview {
|
||||
@@ -165,9 +165,6 @@ struct FeelsWidgetEntryView : View {
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
}.onReceive(NotificationCenter.default.publisher(for: .NSPersistentStoreRemoteChange)) { _ in
|
||||
// make sure you don't call this too often
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,10 +291,6 @@ struct FeelsGraphicWidgetEntryView : View {
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
SmallGraphicWidgetView(entry: entry)
|
||||
.onReceive(NotificationCenter.default.publisher(for: .NSPersistentStoreRemoteChange)) { _ in
|
||||
// make sure you don't call this too often
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user