如何将解析后的 JSON 保存到 CoreData 迁移到 SwiftData [关闭]

问题描述 投票:0回答:0

我是 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。

ios core-data-migration swift-data
© www.soinside.com 2019 - 2024. All rights reserved.