refactor: extract category logic into CategorySelectionViewModel

This commit is contained in:
2025-08-24 20:38:55 +02:00
parent bde826f5c1
commit c8f634a2ee
4 changed files with 50 additions and 46 deletions

View File

@@ -1,7 +1,44 @@
// //
// CategorySelectionViewModel..swift // CategorySelectionViewModel.swift
// QuizApp // QuizApp
// //
// Created by Paul Nowotka on 23.08.25. // Created by Paul on 23.08.25.
// //
import Foundation
class CategorySelectionViewModel: ObservableObject {
private var allQuestionsByCategory: [String: [QuizQuestion]] = [:]
var availableCategories: [String] {
return Array(allQuestionsByCategory.keys).sorted()
}
init() {
loadQuestions()
}
private func loadQuestions() {
guard let url = Bundle.main.url(forResource: "questions", withExtension: "json") else {
print("questions.json nicht gefunden")
return
}
do {
let data = try Data(contentsOf: url)
allQuestionsByCategory = try JSONDecoder().decode([String: [QuizQuestion]].self, from: data)
print("Fragen für Kategorienauswahl geladen")
} catch {
print("Fehler beim Laden der Kategorien: \(error)")
}
}
func highscore(for category: String) -> Int {
let dict = UserDefaults.standard.dictionary(forKey: "categoryHighscores") as? [String: Int] ?? [:]
return dict[category] ?? 0
}
func questions(for category: String) -> [QuizQuestion] {
return allQuestionsByCategory[category] ?? []
}
}

View File

@@ -9,7 +9,6 @@ import Foundation
class ViewModel: ObservableObject { class ViewModel: ObservableObject {
private var allQuestionsByCategory: [String: [QuizQuestion]] = [:]
@Published var questions: [QuizQuestion] = [] @Published var questions: [QuizQuestion] = []
@Published var selectedCategory: String? = nil @Published var selectedCategory: String? = nil
@Published var currentQuestionIndex: Int = 0 @Published var currentQuestionIndex: Int = 0
@@ -36,27 +35,6 @@ class ViewModel: ObservableObject {
return questions[currentQuestionIndex] return questions[currentQuestionIndex]
} }
var availableCategories: [String] {
return Array(allQuestionsByCategory.keys).sorted()
}
init() {
guard let url = Bundle.main.url(forResource: "questions", withExtension: "json") else {
questions = []
return
}
do {
let data = try Data(contentsOf: url)
allQuestionsByCategory = try JSONDecoder().decode([String: [QuizQuestion]].self, from: data)
print("Fragen geladen")
} catch {
print("Fehler beim Laden des Inhalts: \(error)")
allQuestionsByCategory = [:]
}
}
func loadNextQuestion() { func loadNextQuestion() {
if currentQuestionIndex + 1 < questions.count { if currentQuestionIndex + 1 < questions.count {
currentQuestionIndex += 1 currentQuestionIndex += 1
@@ -65,18 +43,17 @@ class ViewModel: ObservableObject {
} }
} }
func loadQuestions(for category: String) { func loadQuestions(questions: [QuizQuestion], for category: String) {
selectedCategory = category selectedCategory = category
questions = allQuestionsByCategory[category] ?? [] self.questions = questions
currentQuestionIndex = 0 currentQuestionIndex = 0
score = 0 score = 0
isQuizFinished = false isQuizFinished = false
answeredCount = 0 answeredCount = 0
selectedAnswers = Array(repeating: nil, count: questions.count) selectedAnswers = Array(repeating: nil, count: questions.count)
print(selectedAnswers) print("Fragen für Quiz geladen: \(questions.count) Fragen")
} }
func incrementScore(selectedIndex: Int) { func incrementScore(selectedIndex: Int) {
if let correct = currentQuestion?.correctAnswer, selectedIndex == correct { if let correct = currentQuestion?.correctAnswer, selectedIndex == correct {
score += 1 score += 1
@@ -86,8 +63,7 @@ class ViewModel: ObservableObject {
func pointsForEstimation( func pointsForEstimation(
guess: Double, guess: Double,
correct: Double, correct: Double,
maxPoints: Int = 1, maxPoints: Int = 1
) -> Int { ) -> Int {
let absError = abs(guess - correct) // Absolute Abweichung let absError = abs(guess - correct) // Absolute Abweichung
let relError = absError / abs(correct) // Relative Abweichung let relError = absError / abs(correct) // Relative Abweichung
@@ -99,11 +75,6 @@ class ViewModel: ObservableObject {
} }
} }
func highscore(for category: String) -> Int {
let dict = UserDefaults.standard.dictionary(forKey: "categoryHighscores") as? [String: Int] ?? [:]
return dict[category] ?? 0
}
@discardableResult @discardableResult
func updateHighscoreIfNeeded() -> Bool { func updateHighscoreIfNeeded() -> Bool {
guard let category = selectedCategory else { return false } guard let category = selectedCategory else { return false }
@@ -116,8 +87,4 @@ class ViewModel: ObservableObject {
} }
return false return false
} }
func allHighscores() -> [String: Int] {
UserDefaults.standard.dictionary(forKey: "categoryHighscores") as? [String: Int] ?? [:]
}
} }

View File

@@ -8,10 +8,9 @@
import SwiftUI import SwiftUI
struct CategorySelectionView: View { struct CategorySelectionView: View {
@StateObject private var viewModel = ViewModel() @StateObject private var categoryViewModel = CategorySelectionViewModel()
@AppStorage("globalScore") var globalScore: Int = 0 @AppStorage("globalScore") var globalScore: Int = 0
var body: some View { var body: some View {
NavigationStack { NavigationStack {
VStack(spacing: 24) { VStack(spacing: 24) {
@@ -67,8 +66,8 @@ struct CategorySelectionView: View {
// Liste der Kategorien // Liste der Kategorien
VStack(spacing: 12) { VStack(spacing: 12) {
ForEach(viewModel.availableCategories, id: \.self) { category in ForEach(categoryViewModel.availableCategories, id: \.self) { category in
NavigationLink(destination: QuizView(viewModel: viewModel, category: category)) { NavigationLink(destination: QuizView(questions: categoryViewModel.questions(for: category), category: category)) {
HStack { HStack {
Image(systemName: "play.circle.fill") Image(systemName: "play.circle.fill")
.foregroundColor(.blue) .foregroundColor(.blue)
@@ -78,7 +77,7 @@ struct CategorySelectionView: View {
Text(category) Text(category)
.font(.headline) .font(.headline)
.foregroundColor(.primary) .foregroundColor(.primary)
Text("Highscore: \(viewModel.highscore(for: category))") Text("Highscore: \(categoryViewModel.highscore(for: category))")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }

View File

@@ -10,10 +10,11 @@ import AudioToolbox
struct QuizView: View { struct QuizView: View {
@ObservedObject var viewModel = ViewModel() @StateObject var viewModel = ViewModel()
@AppStorage("globalScore") var score: Int = 0 @AppStorage("globalScore") var score: Int = 0
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
let questions: [QuizQuestion] // Neue Property
let category: String let category: String
var body: some View { var body: some View {
@@ -119,7 +120,7 @@ struct QuizView: View {
} }
} }
.onAppear { .onAppear {
viewModel.loadQuestions(for: category) viewModel.loadQuestions(questions: questions, for: category)
} }
.navigationBarBackButtonHidden(!viewModel.isQuizFinished) .navigationBarBackButtonHidden(!viewModel.isQuizFinished)
.toolbar { .toolbar {