From 282cd1b3a3bd95dc6ebbcd741d0735995a42969f Mon Sep 17 00:00:00 2001 From: Trey t Date: Mon, 13 Apr 2026 10:27:21 -0500 Subject: [PATCH] Fix lyrics wiped on schema reset by moving SavedSong to cloud container SavedSong was in the local container alongside reference data, so it got deleted whenever localStoreResetVersion was bumped. Move it to the cloud container (CloudKit-synced) so saved songs persist across schema changes. Update lyrics views to use cloudModelContextProvider. Closes #4 Co-Authored-By: Claude Opus 4.6 (1M context) --- Conjuga/Conjuga/ConjugaApp.swift | 10 ++++------ .../Lyrics/LyricsConfirmationView.swift | 8 +++++--- .../Practice/Lyrics/LyricsLibraryView.swift | 19 +++++++++++++++---- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Conjuga/Conjuga/ConjugaApp.swift b/Conjuga/Conjuga/ConjugaApp.swift index 78e61cd..683f680 100644 --- a/Conjuga/Conjuga/ConjugaApp.swift +++ b/Conjuga/Conjuga/ConjugaApp.swift @@ -10,7 +10,7 @@ private enum CloudPreviewContainer { let configuration = ModelConfiguration(isStoredInMemoryOnly: true) return try! ModelContainer( for: ReviewCard.self, CourseReviewCard.self, UserProgress.self, - TestResult.self, DailyLog.self, + TestResult.self, DailyLog.self, SavedSong.self, configurations: configuration ) }() @@ -67,13 +67,13 @@ struct ConjugaApp: App { "cloud", schema: Schema([ ReviewCard.self, CourseReviewCard.self, UserProgress.self, - TestResult.self, DailyLog.self, + TestResult.self, DailyLog.self, SavedSong.self, ]), cloudKitDatabase: .private("iCloud.com.conjuga.app") ) cloudContainer = try ModelContainer( for: ReviewCard.self, CourseReviewCard.self, UserProgress.self, - TestResult.self, DailyLog.self, + TestResult.self, DailyLog.self, SavedSong.self, configurations: cloudConfig ) } catch { @@ -200,7 +200,6 @@ struct ConjugaApp: App { schema: Schema([ Verb.self, VerbForm.self, IrregularSpan.self, TenseGuide.self, CourseDeck.self, VocabCard.self, - SavedSong.self, ]), url: url, cloudKitDatabase: .none @@ -208,7 +207,6 @@ struct ConjugaApp: App { return try ModelContainer( for: Verb.self, VerbForm.self, IrregularSpan.self, TenseGuide.self, CourseDeck.self, VocabCard.self, - SavedSong.self, configurations: localConfig ) } @@ -237,7 +235,7 @@ struct ConjugaApp: App { /// Clears accumulated stale schema metadata from previous container configurations. /// Bump the version number to force another reset if the schema changes again. private static func performOneTimeLocalStoreResetIfNeeded(at url: URL) { - let resetVersion = 2 // bump: widget schema moved to SharedModels + let resetVersion = 3 // bump: SavedSong moved to cloud container let key = "localStoreResetVersion" let defaults = UserDefaults.standard diff --git a/Conjuga/Conjuga/Views/Practice/Lyrics/LyricsConfirmationView.swift b/Conjuga/Conjuga/Views/Practice/Lyrics/LyricsConfirmationView.swift index 561845f..7e9a152 100644 --- a/Conjuga/Conjuga/Views/Practice/Lyrics/LyricsConfirmationView.swift +++ b/Conjuga/Conjuga/Views/Practice/Lyrics/LyricsConfirmationView.swift @@ -7,9 +7,11 @@ struct LyricsConfirmationView: View { let result: LyricsSearchResult let onSave: () -> Void - @Environment(\.modelContext) private var modelContext + @Environment(\.cloudModelContextProvider) private var cloudModelContextProvider @Environment(\.dismiss) private var dismiss + private var cloudModelContext: ModelContext { cloudModelContextProvider() } + @State private var translatedEN = "" @State private var isTranslating = true @State private var translationError = false @@ -207,8 +209,8 @@ struct LyricsConfirmationView: View { albumArtURL: result.albumArtURL ?? "", appleMusicURL: result.appleMusicURL ?? "" ) - modelContext.insert(song) - try? modelContext.save() + cloudModelContext.insert(song) + try? cloudModelContext.save() onSave() } } diff --git a/Conjuga/Conjuga/Views/Practice/Lyrics/LyricsLibraryView.swift b/Conjuga/Conjuga/Views/Practice/Lyrics/LyricsLibraryView.swift index 1254a6d..25de76f 100644 --- a/Conjuga/Conjuga/Views/Practice/Lyrics/LyricsLibraryView.swift +++ b/Conjuga/Conjuga/Views/Practice/Lyrics/LyricsLibraryView.swift @@ -3,9 +3,12 @@ import SharedModels import SwiftData struct LyricsLibraryView: View { - @Query(sort: \SavedSong.savedDate, order: .reverse) private var songs: [SavedSong] + @Environment(\.cloudModelContextProvider) private var cloudModelContextProvider + @State private var songs: [SavedSong] = [] @State private var showingSearch = false + private var cloudModelContext: ModelContext { cloudModelContextProvider() } + var body: some View { Group { if songs.isEmpty { @@ -42,18 +45,26 @@ struct LyricsLibraryView: View { NavigationStack { LyricsSearchView { showingSearch = false + loadSongs() } } } + .onAppear(perform: loadSongs) } - @Environment(\.modelContext) private var modelContext + private func loadSongs() { + let descriptor = FetchDescriptor( + sortBy: [SortDescriptor(\SavedSong.savedDate, order: .reverse)] + ) + songs = (try? cloudModelContext.fetch(descriptor)) ?? [] + } private func deleteSongs(at offsets: IndexSet) { for index in offsets { - modelContext.delete(songs[index]) + cloudModelContext.delete(songs[index]) } - try? modelContext.save() + try? cloudModelContext.save() + loadSongs() } } -- 2.49.1