我是 Swift 语言的新手。我有一个应用程序,其中包含用于解析和解码 JSON 的代码,然后将其保存到 Core Data 和一些用于获取特定数据的 Core Data 函数。我不知道如何将其更改为 SwiftData。 有人有想法吗? 特别是
parseAndStoreQuizJSONData
函数和 delete
函数。
请问该如何处理?
class QuizDataViewModel: ObservableObject {
// MARK: Quiz for Board funcs
@Published var selectedBoardQuizQuestions : [QuizQuestion] = []
@Published var selectedQuizSystem: QuizSystem?
func numberOfQuestionsForBoardFor(month: Int, part: Int, year: Int) -> Int {
guard let quizQuestions = selectedQuizSystem?.quizQuestions?.allObjects as? [QuizQuestion] else { return 0 }
return quizQuestions.filter {
let (questionPart, questionMonth, questionYear, _) = extractDataFromId(id: Int($0.id))
return questionYear == year && questionPart == part && questionMonth == month
}.count
}
func selectQuestionsForBoardQuiz(_ numberOfQuestions: Int, for month: Int, part: Int, year: Int) {
guard let quizQuestions = selectedQuizSystem?.quizQuestions?.allObjects as? [QuizQuestion] else { return }
let relevantQuestions = quizQuestions.filter {
let (questionPart, questionMonth, questionYear, _) = extractDataFromId(id: Int($0.id))
return questionYear == year && questionPart == part && questionMonth == month
}
if numberOfQuestions >= relevantQuestions.count {
// If we are asking for all questions (or more), sort them by id
selectedBoardQuizQuestions = relevantQuestions.sorted(by: { $0.id < $1.id })
} else {
// If we are asking for a subset of questions, select randomly
selectedBoardQuizQuestions = Array(relevantQuestions.shuffled().prefix(numberOfQuestions))
}
}
func extractDataFromId(id: Int) -> (part: Int, month: Int, year: Int, questionId: Int) {
let idString = String(id)
let part = Int(idString.prefix(1))
let month = Int(idString.dropFirst(1).prefix(2))
let year = Int(idString.dropFirst(3).prefix(4))
let questionId = Int(idString.dropFirst(7))
return (part ?? 0, month ?? 0, year ?? 0, questionId ?? 0)
}
func uniqueYearsFromQuestions(in system: QuizSystem) -> [Int] {
guard let quizQuestions = system.quizQuestions?.allObjects as? [QuizQuestion] else { return [] }
let years = Set(quizQuestions.map { extractDataFromId(id: Int($0.id)).year })
return Array(years).sorted(by: >) // sort in descending order
}
func uniquePartsForYear(year: Int, in system: QuizSystem) -> [Int] {
guard let quizQuestions = system.quizQuestions?.allObjects as? [QuizQuestion] else { return [] }
let parts = Set(quizQuestions.filter {
let extractedYear = extractDataFromId(id: Int($0.id)).year
return extractedYear == year
}.map {
let extractedPart = extractDataFromId(id: Int($0.id)).part
return extractedPart
})
let uniqueParts = Array(parts).sorted() // sort in ascending order
return uniqueParts
}
func uniqueMonthsForPart(part: Int, year: Int, in system: QuizSystem) -> [Int] {
guard let quizQuestions = system.quizQuestions?.allObjects as? [QuizQuestion] else { return [] }
let months = Set(quizQuestions.filter {
let (questionPart, _, questionYear, _) = extractDataFromId(id: Int($0.id))
return questionYear == year && questionPart == part
}.map {
let extractedMonth = extractDataFromId(id: Int($0.id)).month
return extractedMonth
})
let uniqueMonths = Array(months).sorted() // sort in ascending order
return uniqueMonths
}
func fetchQuizSystemForId(systemID: Int64, branchID: Int64) {
let context = container.viewContext
let fetchRequest: NSFetchRequest<QuizSystem> = QuizSystem.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "id == %d AND quizBranch.id == %d", systemID, branchID)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
do {
let fetchedSystems = try context.fetch(fetchRequest)
self.selectedQuizSystem = fetchedSystems.first // update selectedQuizSystem
} catch {
print("Failed to fetch QuizSystem: \(error)")
}
}
// ---------------------------------------------
// MARK: Func for saving any data to coredata
func saveChanges() {
let context = container.viewContext
if context.hasChanges {
do {
try context.save()
// Toggle the refreshFlag to force a UI update
self.refreshFlag.toggle()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
// ---------------------------------------------
// MARK: Func for QuizRandoms
@Published var quizSystems = [QuizSystem]()
@Published var quizQuestions: [QuizQuestion] = []
@Published var selectedSystemName: String?
@Published var currentSystemIndex = 0
@Published var quizTimeRemaining = 900
func fetchQuizSystems(for branchID: Int64, excludingSystemIDs: [Int64] = []) {
let context = container.viewContext
context.perform {
let fetchQuizSystemRequest: NSFetchRequest<QuizSystem> = QuizSystem.fetchRequest()
var predicates = [NSPredicate(format: "quizBranch.id == %d", branchID)]
for id in excludingSystemIDs {
let excludePredicate = NSPredicate(format: "id != %d", id)
predicates.append(excludePredicate)
}
fetchQuizSystemRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
fetchQuizSystemRequest.sortDescriptors = [NSSortDescriptor(keyPath: \QuizSystem.id, ascending: true)]
do {
let fetchedQuizSystems = try context.fetch(fetchQuizSystemRequest)
self.quizSystems = fetchedQuizSystems
} catch {
print("Failed to fetch QuizSystem: \(error)")
}
}
}
func fetchQuizQuestions(for systemID: Int64, and branchID: Int64) {
let context = container.viewContext
let fetchRequest: NSFetchRequest<QuizQuestion> = QuizQuestion.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "quizSystem.id == %d AND quizSystem.quizBranch.id == %d", systemID, branchID)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
do {
self.quizQuestions = try context.fetch(fetchRequest)
} catch {
print("Failed to fetch QuizQuestion objects: \(error)")
}
}
func selectRandomQuestions(_ count: Int) {
guard quizQuestions.count >= count else {
print("Not enough questions to select from.")
return
}
quizQuestions.shuffle()
quizQuestions = Array(quizQuestions.prefix(count))
}
// -------------------------------------------
// MARK: Func for Downloading and updating data from server
@AppStorage("selectedBranchName") var selectedBranchName: String?
let urlSession = URLSession.shared
let container: NSPersistentContainer
init(container: NSPersistentContainer) {
self.container = container
self.parseAndStoreQuizJSONData()
}
func fetchAndDecodeQuizJSONData(from url: URL, using urlSession: URLSession = URLSession.shared, completion: @escaping (Result<QuizBranchTemp, Error>) -> Void) {
let task = urlSession.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(error))
print("Something went wrong: \(error)")
return
}
guard let data = data else {
completion(.failure(NSError(domain: "", code: -1, userInfo: nil)))
return
}
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(QuizBranchTemp.self, from: data)
completion(.success(decodedData))
} catch {
completion(.failure(error))
}
}
task.resume()
}
func parseAndStoreQuizJSONData() {
let context = container.newBackgroundContext()
context.perform {
guard let mainURL = URL(string: "https://example.com/name.json") else {
print("Invalid URL")
return
}
var request = URLRequest(url: mainURL)
request.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("Failed to fetch main Quiz JSON: \(error)")
return
}
guard let httpResponse = response as? HTTPURLResponse else {
print("Invalid response")
return
}
guard httpResponse.statusCode == 200 else {
print("HTTP Status Code: \(httpResponse.statusCode)")
return
}
guard let data = data else {
print("No data returned from main Quiz JSON")
return
}
let decoder = JSONDecoder()
do {
let quizMainData = try decoder.decode(QuizMainData.self, from: data)
let quizStoredVersion = UserDefaults.standard.string(forKey: "quizMainDataVersion") ?? ""
print("Fetched quizMainDataVersion: \(quizMainData.quizMainDataVersion)")
print("Stored quizMainDataVersion: \(quizStoredVersion)")
if quizMainData.quizMainDataVersion == quizStoredVersion {
print("quizMainDataVersion is the same as the quizStored version.")
return
}
var parsedQuizBranchIDs = Set<Int64>()
var parsedQuizSystemIDs = Set<Int64>()
var parsedQuizQuestionIDs = Set<Int64>()
let group = DispatchGroup()
for quizBranchInfo in quizMainData.quizMainDataContent {
print("Processing quizBranch: \(quizBranchInfo.branchName)")
let lastVersion = UserDefaults.standard.string(forKey: "\(quizBranchInfo.branchName)Version") ?? ""
// Add this branch's ID to parsedBranchIDs
parsedQuizBranchIDs.insert(Int64(quizBranchInfo.id))
if quizBranchInfo.version == lastVersion {
print("\(quizBranchInfo.branchName) version is the same as the current version. Aborting...")
continue
}
guard let quizBranchURL = URL(string: quizBranchInfo.url) else {
print("Invalid URL for branch: \(quizBranchInfo.branchName)")
continue
}
group.enter()
self.fetchAndDecodeQuizJSONData(from: quizBranchURL, using: self.urlSession) { result in
defer { group.leave() }
switch result {
case .success(let quizBranch):
do {
let fetchQuizBranchRequest: NSFetchRequest<QuizBranch> = QuizBranch.fetchRequest()
fetchQuizBranchRequest.predicate = NSPredicate(format: "id == %d", quizBranch.id)
let fetchedQuizBranches = try context.fetch(fetchQuizBranchRequest)
let QuizBranchEntity = fetchedQuizBranches.first ?? QuizBranch(context: context)
QuizBranchEntity.id = Int64(quizBranch.id)
QuizBranchEntity.branchName = quizBranch.branchName
for quizSystem in quizBranch.quizSystems {
let fetchQuizSystemRequest: NSFetchRequest<QuizSystem> = QuizSystem.fetchRequest()
fetchQuizSystemRequest.predicate = NSPredicate(format: "id == %d AND quizBranch.id == %d", quizSystem.id, quizBranch.id)
let fetchedQuizSystems = try context.fetch(fetchQuizSystemRequest)
let quizSystemEntity = fetchedQuizSystems.first ?? QuizSystem(context: context)
quizSystemEntity.id = Int64(quizSystem.id)
quizSystemEntity.systemName = quizSystem.systemName
quizSystemEntity.systemSubheadline = quizSystem.systemSubheadline
quizSystemEntity.systemImage = quizSystem.systemImage
quizSystemEntity.quizBranch = QuizBranchEntity
parsedQuizSystemIDs.insert(quizSystemEntity.id)
for quizQuestion in quizSystem.quizQuestions {
let fetchQuizQuestionRequest: NSFetchRequest<QuizQuestion> = QuizQuestion.fetchRequest()
fetchQuizQuestionRequest.predicate = NSPredicate(format: "id == %d AND quizSystem.id == %d AND quizSystem.quizBranch.id == %d", quizQuestion.id, quizSystem.id, quizBranch.id)
let fetchedQuizQuestions = try context.fetch(fetchQuizQuestionRequest)
let quizQuestionEntity = fetchedQuizQuestions.first ?? QuizQuestion(context: context)
quizQuestionEntity.id = Int64(quizQuestion.id)
quizQuestionEntity.questionNumber = Int64(quizQuestion.questionNumber)
quizQuestionEntity.questionContent = quizQuestion.questionContent
quizQuestionEntity.choiceA = quizQuestion.choiceA
quizQuestionEntity.choiceB = quizQuestion.choiceB
quizQuestionEntity.choiceC = quizQuestion.choiceC
quizQuestionEntity.choiceD = quizQuestion.choiceD
quizQuestionEntity.choiceE = quizQuestion.choiceE
quizQuestionEntity.correctAnswerLetter = quizQuestion.correctAnswerLetter
quizQuestionEntity.explanation = quizQuestion.explanation
quizQuestionEntity.isSolvedWrong = quizQuestion.isSolvedWrong
quizQuestionEntity.isFlagged = quizQuestion.isFlagged
quizQuestionEntity.isNeedReview = quizQuestion.isNeedReview
quizQuestionEntity.quizSystem = quizSystemEntity
parsedQuizQuestionIDs.insert(quizQuestionEntity.id)
}
}
do {
try context.save()
print("Saved new version (\(String(describing: quizBranchInfo.version))) of \(quizBranchInfo.branchName) to Quiz CoreData")
} catch {
print("Failed to save Quiz Core Data: \(error)")
}
} catch {
print("Failed to fetch and parse Quiz data: \(error)")
}
UserDefaults.standard.set(quizBranchInfo.version, forKey: "\(quizBranchInfo.branchName)Version")
print("Set \(quizBranchInfo.branchName) version to \(quizBranchInfo.version)")
case .failure(let error):
print("Failed to decode Quiz JSON for Quiz branch \(quizBranchInfo.branchName): \(error)")
}
}
}
group.notify(queue: .main) {
print("Parsed Quiz branch IDs: \(parsedQuizBranchIDs)")
// Now it's safe to delete
do {
self.deleteEntitiesNotInParsedIDs(context: context, entityName: "QuizBranch", parsedIDs: parsedQuizBranchIDs)
self.deleteEntitiesNotInParsedIDs(context: context, entityName: "QuizSystem", parsedIDs: parsedQuizSystemIDs)
self.deleteEntitiesNotInParsedIDs(context: context, entityName: "QuizQuestion", parsedIDs: parsedQuizQuestionIDs)
try context.save()
UserDefaults.standard.set(quizMainData.quizMainDataVersion, forKey: "quizMainDataVersion")
} catch {
print("Failed to decode main Quiz JSON: \(error)")
}
}
} catch {
print("Failed to decode main Quiz JSON: \(error)")
}
}.resume()
}
}
func deleteEntitiesNotInParsedIDs(context: NSManagedObjectContext, entityName: String, parsedIDs: Set<Int64>) {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: entityName)
do {
let fetchedEntities = try context.fetch(fetchRequest)
for object in fetchedEntities {
if let entity = object as? NSManagedObject, let entityId = entity.value(forKey: "id") as? Int64 {
if !parsedIDs.contains(entityId) {
context.delete(entity)
}
}
}
} catch {
print("Failed to fetch \(entityName)s: \(error)")
}
}
// ----------------------------------------------------------------
// MARK: Func for Trainging center
@Published var refreshFlag: Bool = false
func fetchIsFlaggedQuestionsBySystem(for branchID: Int64) -> ([String: [QuizQuestion]], [QuizSystem]) {
var isFlaggedQuizQuestionBySystem = [String: [QuizQuestion]]()
var isFlaggedQuizSystemsArray = [QuizSystem]()
// Fetch systems for the selected branch
self.fetchQuizSystems(for: branchID)
for system in self.quizSystems {
// Fetch flagged questions for the system
let flaggedQuestions = fetchIsFlaggedQuestions(for: system.id)
// Only add the system to the dictionary if it has flagged questions
if !flaggedQuestions.isEmpty {
isFlaggedQuizQuestionBySystem[system.systemName!] = flaggedQuestions
isFlaggedQuizSystemsArray.append(system)
}
}
return (isFlaggedQuizQuestionBySystem, isFlaggedQuizSystemsArray)
}
func fetchIsFlaggedQuestions(for systemID: Int64) -> [QuizQuestion] {
let context = container.viewContext
let fetchRequest: NSFetchRequest<QuizQuestion> = QuizQuestion.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "quizSystem.id == %d AND isFlagged == %d", systemID, 1)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
do {
let flaggedQuestions = try context.fetch(fetchRequest)
return flaggedQuestions
} catch {
print("Failed to fetch flagged QuizQuestion objects: \(error)")
}
return []
}
func fetchIsSolvedWrongQuestionsBySystem(for branchID: Int64) -> ([String: [QuizQuestion]], [QuizSystem]) {
var isSolvedWrongQuizQuestionBySystem = [String: [QuizQuestion]]()
var isSolvedWrongQuizSystemsArray = [QuizSystem]()
// Fetch systems for the selected branch
self.fetchQuizSystems(for: branchID)
for system in self.quizSystems {
// Fetch flagged questions for the system
let isSolvedWrongQuestions = fetchIsSolvedWrongQuestions(for: system.id)
// Only add the system to the dictionary if it has flagged questions
if !isSolvedWrongQuestions.isEmpty {
isSolvedWrongQuizQuestionBySystem[system.systemName!] = isSolvedWrongQuestions
isSolvedWrongQuizSystemsArray.append(system)
}
}
return (isSolvedWrongQuizQuestionBySystem, isSolvedWrongQuizSystemsArray)
}
func fetchIsSolvedWrongQuestions(for systemID: Int64) -> [QuizQuestion] {
let context = container.viewContext
let fetchRequest: NSFetchRequest<QuizQuestion> = QuizQuestion.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "quizSystem.id == %d AND isSolvedWrong == %d", systemID, 1)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
do {
let isSolvedWrongQuestions = try context.fetch(fetchRequest)
return isSolvedWrongQuestions
} catch {
print("Failed to fetch isSolvedWrong QuizQuestion objects: \(error)")
}
return []
}
func fetchIsNeedReviewQuestionsBySystem(for branchID: Int64) -> ([String: [QuizQuestion]], [QuizSystem]) {
var isNeedReviewQuizQuestionBySystem = [String: [QuizQuestion]]()
var isNeedReviewQuizSystemsArray = [QuizSystem]()
// Fetch systems for the selected branch
self.fetchQuizSystems(for: branchID)
for system in self.quizSystems {
// Fetch flagged questions for the system
let isNeedReviewQuestions = fetchIsNeedReviewQuestions(for: system.id)
// Only add the system to the dictionary if it has flagged questions
if !isNeedReviewQuestions.isEmpty {
isNeedReviewQuizQuestionBySystem[system.systemName!] = isNeedReviewQuestions
isNeedReviewQuizSystemsArray.append(system)
}
}
return (isNeedReviewQuizQuestionBySystem, isNeedReviewQuizSystemsArray)
}
func fetchIsNeedReviewQuestions(for systemID: Int64) -> [QuizQuestion] {
let context = container.viewContext
let fetchRequest: NSFetchRequest<QuizQuestion> = QuizQuestion.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "quizSystem.id == %d AND isNeedReview == %d", systemID, 1)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
do {
let isNeedReviewQuestions = try context.fetch(fetchRequest)
return isNeedReviewQuestions
} catch {
print("Failed to fetch isNeedReview QuizQuestion objects: \(error)")
}
return []
}
}
我想将解码后的 JSON 保存到 SwiftData。