Migrate journal entries to CoreData for iCloud sync (build 33)

- Add JournalEntry CoreData entity (syncable=YES): id, monthKey, note, moodRaw, rating, completionTime, createdAt
- Rewrite MonthlyCheckInStore: CoreData as primary storage, same public API
- One-time migration from UserDefaults triggered after store loads
- MonthlyCheckInCard: @FetchRequest on JournalEntry — reactive to iCloud sync
- MonthlyCheckInView: onReceive NSManagedObjectContextObjectsDidChange to refresh @State vars on sync
- Stats cards: observe NSManagedObjectContextObjectsDidChange instead of UserDefaults
- ChartsViewModel.completedMonthKeys: remove MonthlyCheckInStore dependency (data-driven)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
alexandrev-tibco
2026-05-26 15:05:53 +02:00
parent 0b01d6f563
commit 270edeb536
8 changed files with 236 additions and 276 deletions
@@ -5,13 +5,6 @@ struct MonthlyCheckInView: View {
@Environment(\.openURL) private var openURL
@EnvironmentObject var accountStore: AccountStore
@StateObject private var viewModel = MonthlyCheckInViewModel()
// Provides the latest snapshot date from CoreData (synced via iCloud) as fallback
// for lastCompletionDate when local MonthlyCheckInStore records are stale/missing.
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Snapshot.date, ascending: false)],
animation: .none
) private var latestSnapshots: FetchedResults<Snapshot>
@State private var referenceDate: Date
@State private var monthlyNote: String
@@ -32,12 +25,7 @@ struct MonthlyCheckInView: View {
}
private var lastCompletionDate: Date? {
// MonthlyCheckInStore lives in UserDefaults (device-local, not iCloud-synced).
// On a secondary device after iCloud sync the latest snapshot from CoreData may be
// newer than the local check-in record. Use whichever is more recent.
let localCompletion = MonthlyCheckInStore.latestCompletionDate()
let latestSnapshotDate = latestSnapshots.first?.date
return [localCompletion, latestSnapshotDate].compactMap { $0 }.max()
MonthlyCheckInStore.latestCompletionDate()
}
private var checkInProgress: Double {
@@ -162,6 +150,11 @@ struct MonthlyCheckInView: View {
starRating = MonthlyCheckInStore.rating(for: referenceDate) ?? 0
selectedMood = MonthlyCheckInStore.mood(for: referenceDate)
}
.onReceive(NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange)) { _ in
monthlyNote = MonthlyCheckInStore.note(for: referenceDate)
starRating = MonthlyCheckInStore.rating(for: referenceDate) ?? 0
selectedMood = MonthlyCheckInStore.mood(for: referenceDate)
}
.onReceive(accountStore.$selectedAccount) { account in
viewModel.selectedAccount = account
viewModel.selectedRange = DateRange.month(containing: referenceDate)