Merge pull request 'Fix lyrics wiped on schema reset' (#8) from fix/lyrics-data-loss into main

This commit was merged in pull request #8.
This commit is contained in:
2026-04-13 10:27:39 -05:00
3 changed files with 24 additions and 13 deletions

View File

@@ -10,7 +10,7 @@ private enum CloudPreviewContainer {
let configuration = ModelConfiguration(isStoredInMemoryOnly: true) let configuration = ModelConfiguration(isStoredInMemoryOnly: true)
return try! ModelContainer( return try! ModelContainer(
for: ReviewCard.self, CourseReviewCard.self, UserProgress.self, for: ReviewCard.self, CourseReviewCard.self, UserProgress.self,
TestResult.self, DailyLog.self, TestResult.self, DailyLog.self, SavedSong.self,
configurations: configuration configurations: configuration
) )
}() }()
@@ -67,13 +67,13 @@ struct ConjugaApp: App {
"cloud", "cloud",
schema: Schema([ schema: Schema([
ReviewCard.self, CourseReviewCard.self, UserProgress.self, ReviewCard.self, CourseReviewCard.self, UserProgress.self,
TestResult.self, DailyLog.self, TestResult.self, DailyLog.self, SavedSong.self,
]), ]),
cloudKitDatabase: .private("iCloud.com.conjuga.app") cloudKitDatabase: .private("iCloud.com.conjuga.app")
) )
cloudContainer = try ModelContainer( cloudContainer = try ModelContainer(
for: ReviewCard.self, CourseReviewCard.self, UserProgress.self, for: ReviewCard.self, CourseReviewCard.self, UserProgress.self,
TestResult.self, DailyLog.self, TestResult.self, DailyLog.self, SavedSong.self,
configurations: cloudConfig configurations: cloudConfig
) )
} catch { } catch {
@@ -200,7 +200,6 @@ struct ConjugaApp: App {
schema: Schema([ schema: Schema([
Verb.self, VerbForm.self, IrregularSpan.self, Verb.self, VerbForm.self, IrregularSpan.self,
TenseGuide.self, CourseDeck.self, VocabCard.self, TenseGuide.self, CourseDeck.self, VocabCard.self,
SavedSong.self,
]), ]),
url: url, url: url,
cloudKitDatabase: .none cloudKitDatabase: .none
@@ -208,7 +207,6 @@ struct ConjugaApp: App {
return try ModelContainer( return try ModelContainer(
for: Verb.self, VerbForm.self, IrregularSpan.self, for: Verb.self, VerbForm.self, IrregularSpan.self,
TenseGuide.self, CourseDeck.self, VocabCard.self, TenseGuide.self, CourseDeck.self, VocabCard.self,
SavedSong.self,
configurations: localConfig configurations: localConfig
) )
} }
@@ -237,7 +235,7 @@ struct ConjugaApp: App {
/// Clears accumulated stale schema metadata from previous container configurations. /// Clears accumulated stale schema metadata from previous container configurations.
/// Bump the version number to force another reset if the schema changes again. /// Bump the version number to force another reset if the schema changes again.
private static func performOneTimeLocalStoreResetIfNeeded(at url: URL) { 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 key = "localStoreResetVersion"
let defaults = UserDefaults.standard let defaults = UserDefaults.standard

View File

@@ -7,9 +7,11 @@ struct LyricsConfirmationView: View {
let result: LyricsSearchResult let result: LyricsSearchResult
let onSave: () -> Void let onSave: () -> Void
@Environment(\.modelContext) private var modelContext @Environment(\.cloudModelContextProvider) private var cloudModelContextProvider
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
private var cloudModelContext: ModelContext { cloudModelContextProvider() }
@State private var translatedEN = "" @State private var translatedEN = ""
@State private var isTranslating = true @State private var isTranslating = true
@State private var translationError = false @State private var translationError = false
@@ -207,8 +209,8 @@ struct LyricsConfirmationView: View {
albumArtURL: result.albumArtURL ?? "", albumArtURL: result.albumArtURL ?? "",
appleMusicURL: result.appleMusicURL ?? "" appleMusicURL: result.appleMusicURL ?? ""
) )
modelContext.insert(song) cloudModelContext.insert(song)
try? modelContext.save() try? cloudModelContext.save()
onSave() onSave()
} }
} }

View File

@@ -3,9 +3,12 @@ import SharedModels
import SwiftData import SwiftData
struct LyricsLibraryView: View { 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 @State private var showingSearch = false
private var cloudModelContext: ModelContext { cloudModelContextProvider() }
var body: some View { var body: some View {
Group { Group {
if songs.isEmpty { if songs.isEmpty {
@@ -42,18 +45,26 @@ struct LyricsLibraryView: View {
NavigationStack { NavigationStack {
LyricsSearchView { LyricsSearchView {
showingSearch = false showingSearch = false
loadSongs()
} }
} }
} }
.onAppear(perform: loadSongs)
} }
@Environment(\.modelContext) private var modelContext private func loadSongs() {
let descriptor = FetchDescriptor<SavedSong>(
sortBy: [SortDescriptor(\SavedSong.savedDate, order: .reverse)]
)
songs = (try? cloudModelContext.fetch(descriptor)) ?? []
}
private func deleteSongs(at offsets: IndexSet) { private func deleteSongs(at offsets: IndexSet) {
for index in offsets { for index in offsets {
modelContext.delete(songs[index]) cloudModelContext.delete(songs[index])
} }
try? modelContext.save() try? cloudModelContext.save()
loadSongs()
} }
} }