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) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user