Initial commit: Conjuga Spanish conjugation app

Includes SwiftData dual-store architecture (local reference + CloudKit user data),
JSON-based data seeding, 20 tense guides, 20 grammar notes, SRS review system,
course vocabulary, and widget support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-04-09 20:58:33 -05:00
commit 4b467ec136
95 changed files with 82599 additions and 0 deletions
@@ -0,0 +1,74 @@
import Foundation
import SwiftData
struct ReferenceStore {
let context: ModelContext
func fetchGuides() -> [TenseGuide] {
let descriptor = FetchDescriptor<TenseGuide>(sortBy: [SortDescriptor(\TenseGuide.tenseId)])
return (try? context.fetch(descriptor)) ?? []
}
func fetchGuideMap() -> [String: TenseGuide] {
Dictionary(uniqueKeysWithValues: fetchGuides().map { ($0.tenseId, $0) })
}
func fetchVerbs() -> [Verb] {
let descriptor = FetchDescriptor<Verb>(sortBy: [SortDescriptor(\Verb.infinitive)])
return (try? context.fetch(descriptor)) ?? []
}
func fetchVerbs(selectedLevel: String) -> [Verb] {
fetchVerbs().filter { VerbLevelGroup.matches($0.level, selectedLevel: selectedLevel) }
}
func allowedVerbIDs(selectedLevel: String) -> Set<Int> {
Set(fetchVerbs(selectedLevel: selectedLevel).map(\.id))
}
func fetchVerb(id: Int) -> Verb? {
let descriptor = FetchDescriptor<Verb>(predicate: #Predicate<Verb> { $0.id == id })
return (try? context.fetch(descriptor))?.first
}
func fetchVerbForms(verbId: Int) -> [VerbForm] {
let descriptor = FetchDescriptor<VerbForm>(
predicate: #Predicate<VerbForm> { $0.verbId == verbId },
sortBy: [SortDescriptor(\VerbForm.tenseId), SortDescriptor(\VerbForm.personIndex)]
)
return (try? context.fetch(descriptor)) ?? []
}
func fetchForms(verbId: Int, tenseId: String) -> [VerbForm] {
let descriptor = FetchDescriptor<VerbForm>(
predicate: #Predicate<VerbForm> { form in
form.verbId == verbId && form.tenseId == tenseId
},
sortBy: [SortDescriptor(\VerbForm.personIndex)]
)
return (try? context.fetch(descriptor)) ?? []
}
func fetchForm(verbId: Int, tenseId: String, personIndex: Int) -> VerbForm? {
let descriptor = FetchDescriptor<VerbForm>(
predicate: #Predicate<VerbForm> { form in
form.verbId == verbId &&
form.tenseId == tenseId &&
form.personIndex == personIndex
}
)
return (try? context.fetch(descriptor))?.first
}
func fetchSpans(verbId: Int, tenseId: String, personIndex: Int) -> [IrregularSpan] {
let descriptor = FetchDescriptor<IrregularSpan>(
predicate: #Predicate<IrregularSpan> { span in
span.verbId == verbId &&
span.tenseId == tenseId &&
span.personIndex == personIndex
},
sortBy: [SortDescriptor(\IrregularSpan.start)]
)
return (try? context.fetch(descriptor)) ?? []
}
}