当我在另一个视图中使用 CoreData 保存新数据时如何更新模型的另一个实例
Posted
技术标签:
【中文标题】当我在另一个视图中使用 CoreData 保存新数据时如何更新模型的另一个实例【英文标题】:How to update another instance of a model when I save new data using CoreData in another view 【发布时间】:2021-01-19 18:55:16 【问题描述】:我正在构建一个简单的 Quiz ios 应用程序,它使用 CoreData 来存储测验中使用的问题。
我的主屏幕视图导航到“播放演示测验”、“播放我的测验”和“添加新问题”视图。
struct ContentView: View
var quizViewModel = QuizViewModel(categoryIndex: 1)
var body: some View
NavigationView
VStack
Spacer()
Text("Quizzy").font(.largeTitle).bold()
Spacer()
NavigationLink(
destination: MainQuizView(categoryIndex: 0),
label:
DefaultNavigationLinkLabel(labelText: "Play Demo Quiz")
)
NavigationLink(
destination: MainQuizView(categoryIndex: 1),
label:
DefaultNavigationLinkLabel(labelText: "Play My Quiz").padding()
)
NavigationLink(
destination: AddQuestionView(),
label:
DefaultNavigationLinkLabel(labelText: "Add New Questions")
)
Spacer()
Spacer()
.navigationBarTitle(Text("Quizzy"), displayMode: .inline)
.onAppear()
quizViewModel.deleteAllQuestions()
UserDefaults.standard.set(false, forKey: "launchedBefore")
if(!quizViewModel.checkIfLaunchedBefore())
quizViewModel.savePresetDemoQuestions()
print("First time.")
else
print("Launched before.")
这些视图中的每一个都有自己的 QuizViewModel,其中有一个 QuizModel
class QuizViewModel : ObservableObject
private let categoryIndex: Int
@Published private var quizModel: QuizModel
private(set) var currentQuestion: Question?
init(categoryIndex: Int)
self.categoryIndex = categoryIndex
self.quizModel = QuizModel(categoryIndex: categoryIndex)
currentQuestion = Question()
if(getQuestionCount() != 0)
getCurrentQuestion()
func checkAnswer(answerIndex: Int) -> Bool
quizModel.checkAnswer(answerIndex: answerIndex)
func getCurrentQuestion() -> Bool
do
try currentQuestion = quizModel.getCurrentQuestion()
return true
catch
return false
func getQuestionCount() -> Int
quizModel.getQuestionCount()
func getCurrentQuestionIndex() -> Int
quizModel.getCurrentQuestionIndex()
func getUserScore() -> Int
quizModel.getUserScore()
func restartGame()
quizModel.restartGame(categoryIndex: categoryIndex)
getCurrentQuestion()
func deleteAllQuestions()
quizModel.deleteAllQuestions()
UserDefaults.standard.set(false, forKey: "launchedBefore")
func savePresetDemoQuestions()
self.quizModel.savePresetDemoQuestions()
restartGame()
func saveQuestion(questionText: String, answers: [String], correctAnswerIndex: Int)
self.quizModel.saveQuestion(questionText: questionText, answers: answers, correctAnswerIndex: correctAnswerIndex, categoryIndex: 1)
restartGame()
func saveDummyQuestion()
quizModel.saveDummyQuestion()
restartGame()
// Utility functions
func checkIfLaunchedBefore() -> Bool
let launchedBefore = UserDefaults.standard.bool(forKey: "launchedBefore")
if launchedBefore
return launchedBefore
else
print("First launch, setting UserDefault.")
UserDefaults.standard.set(true, forKey: "launchedBefore")
return false
这是测验模型
struct QuizModel
private var questions: [Question] = []
private let categoryIndex: Int
private var currentQuestionIndex: Int = 0
private let coreDataManager = CoreDataManager()
var userScore = 0;
init(categoryIndex: Int)
self.categoryIndex = categoryIndex
loadQuestions(categoryIndex: categoryIndex)
enum QuizQuestionSetError: Error
case outOfQuestions;
func getCurrentQuestion() throws -> Question
guard currentQuestionIndex < questions.count else
throw QuizQuestionSetError.outOfQuestions
return questions[currentQuestionIndex]
mutating func checkAnswer(answerIndex: Int) -> Bool
var isCorrect: Bool = false
if(answerIndex == questions[currentQuestionIndex].correctAnswerIndex)
userScore += 1
isCorrect = true
currentQuestionIndex += 1
return isCorrect
func getQuestionCount() -> Int
questions.count
func getCurrentQuestionIndex() -> Int
currentQuestionIndex
func getUserScore() -> Int
userScore
mutating func loadQuestions(categoryIndex: Int)
self.questions = coreDataManager.fetchQuestions().filter question in
question.categoryIndex == categoryIndex
.shuffled()
mutating func restartGame(categoryIndex: Int)
loadQuestions(categoryIndex: categoryIndex)
self.currentQuestionIndex = 0
self.userScore = 0
func saveQuestion(questionText: String, answers: [String], correctAnswerIndex: Int, categoryIndex: Int)
coreDataManager.saveQuestion(questionText: questionText, answers: answers, correctAnswerIndex: correctAnswerIndex, categoryIndex: categoryIndex)
mutating func savePresetDemoQuestions()
guard let encodedDemoQuestions = self.readDemoJsonFile(forName: "DemoQuestions") else
return
if let decodedDemoQuestions = self.parse(jsonData: encodedDemoQuestions)
for demoQuestion in decodedDemoQuestions
coreDataManager.saveQuestion(questionText: demoQuestion.questionText,
answers: demoQuestion.answers,
correctAnswerIndex: demoQuestion.correctAnswerIndex,
categoryIndex: demoQuestion.categoryIndex)
print(decodedDemoQuestions.count)
print("Updating questions.....")
self.loadQuestions(categoryIndex: categoryIndex)
// MARK: - Utility Methods
private func readDemoJsonFile(forName name: String) -> Data?
do
if let bundlePath = Bundle.main.path(forResource: name, ofType: "json"),
let jsonData = try String(contentsOfFile: bundlePath).data(using: .utf8)
return jsonData
catch
print(error)
return nil
private func parse(jsonData: Data) -> [QuestionStruct]?
do
let decodedData = try JSONDecoder().decode([QuestionStruct].self, from: jsonData)
return decodedData
catch
print(error)
return nil
func saveDummyQuestion()
saveQuestion(questionText: "AAAAAAAAAAA", answers: ["N","B","C","D"], correctAnswerIndex: 2, categoryIndex: 1)
func deleteAllQuestions()
print("\n\n\n Deleted")
coreDataManager.deleteAllQuestions()
那么,当使用该模型的另一个实例将问题保存在“AddQuestionView”中时,我如何告诉我的实例化“播放我的测验”模型重新加载他的问题? 如果需要,可以在这里访问完整的项目:https://github.com/MateoA/Quizzy
提前非常感谢,对于这个混乱的问题,我们深表歉意。
【问题讨论】:
【参考方案1】:要观察/侦听 CoreData 更改,您需要使用 View
中为 SwiftUI 制作的 @FetchRequest
包装器,或使用包装在 ObservableObject
中的传统 FetchedRequestController(因为您有 CoreData 管理器)
它有很多代码,但CoreData Programming Guide 有一个很好的教程,介绍如何在 TableView 中创建它。最大的不同是,您将更新 ObservableObject 中的变量,而不是更新 TableView。
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
//Update an @Published variable here
Tutorial Combo Video
另外,你应该观察QuizViewModel
并将它传递给appropriately
//In the ContentView
@StateObject var quizViewModel = QuizViewModel(categoryIndex: 1)
//Initialize the Views that need the model like this
AddQuestionView().environmentObject(quizViewModel)
//All Views that need the Model
struct AddQuestionView:View
@EnvironmentObject var quizViewModel = QuizViewModel
//...
如果您查看Apple SwiftUI tutorials,您可能能够最大限度地发挥 SwiftUI 的优势
【讨论】:
以上是关于当我在另一个视图中使用 CoreData 保存新数据时如何更新模型的另一个实例的主要内容,如果未能解决你的问题,请参考以下文章
在 tableViewController 中使用重新加载的 CoreData