Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Question Stash and Session Manager): Add question stash and fetch strategy, added a Session Manager. #63

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions SwiftUIQuizz.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
185BB8162835202700BB689A /* correct.json in Resources */ = {isa = PBXBuildFile; fileRef = 185BB8142835202700BB689A /* correct.json */; };
18DF400E282A8402006B7254 /* MultipleChoiceButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18DF400D282A8402006B7254 /* MultipleChoiceButton.swift */; };
23A73D02283686C700C611D4 /* Quicksand.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 23A73D01283686C700C611D4 /* Quicksand.ttf */; };
3D12614C283D237A0079E14B /* Manager.AnswerTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D12614B283D237A0079E14B /* Manager.AnswerTracker.swift */; };
3DFB3603283FA6560045D062 /* Manager.QuestionStash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DFB3602283FA6560045D062 /* Manager.QuestionStash.swift */; };
3DFB3605283FB4580045D062 /* Manager.SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DFB3604283FB4580045D062 /* Manager.SessionManager.swift */; };
3DFD23EE28295C180074AB54 /* Manager.SaveSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DFD23ED28295C180074AB54 /* Manager.SaveSystem.swift */; };
5F0B738C283BACCE008EE6DC /* DesignSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0B738B283BACCE008EE6DC /* DesignSystem.swift */; };
5F0B738E283BAD6E008EE6DC /* DesignSystem.Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0B738D283BAD6E008EE6DC /* DesignSystem.Color.swift */; };
Expand Down Expand Up @@ -48,7 +49,8 @@
185BB8142835202700BB689A /* correct.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = correct.json; sourceTree = "<group>"; };
18DF400D282A8402006B7254 /* MultipleChoiceButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipleChoiceButton.swift; sourceTree = "<group>"; };
23A73D01283686C700C611D4 /* Quicksand.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Quicksand.ttf; sourceTree = "<group>"; };
3D12614B283D237A0079E14B /* Manager.AnswerTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manager.AnswerTracker.swift; sourceTree = "<group>"; };
3DFB3602283FA6560045D062 /* Manager.QuestionStash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manager.QuestionStash.swift; sourceTree = "<group>"; };
3DFB3604283FB4580045D062 /* Manager.SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manager.SessionManager.swift; sourceTree = "<group>"; };
3DFD23ED28295C180074AB54 /* Manager.SaveSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manager.SaveSystem.swift; sourceTree = "<group>"; };
5F0B738B283BACCE008EE6DC /* DesignSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DesignSystem.swift; sourceTree = "<group>"; };
5F0B738D283BAD6E008EE6DC /* DesignSystem.Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DesignSystem.Color.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -135,7 +137,8 @@
5F3AC736282408250041CBBC /* Manager.API.swift */,
3DFD23ED28295C180074AB54 /* Manager.SaveSystem.swift */,
1830AF87283E4A5800D4EA1A /* Manager.SFX.swift */,
3D12614B283D237A0079E14B /* Manager.AnswerTracker.swift */,
3DFB3602283FA6560045D062 /* Manager.QuestionStash.swift */,
3DFB3604283FB4580045D062 /* Manager.SessionManager.swift */,
);
path = Manager;
sourceTree = "<group>";
Expand Down Expand Up @@ -319,6 +322,7 @@
files = (
5F3AC737282408250041CBBC /* Manager.API.swift in Sources */,
5FDC436D2829371100A4F011 /* LaunchView.swift in Sources */,
3DFB3603283FA6560045D062 /* Manager.QuestionStash.swift in Sources */,
5FE9134A2822A76E00C0CDE0 /* QuestionView.swift in Sources */,
3DFD23EE28295C180074AB54 /* Manager.SaveSystem.swift in Sources */,
5F0B738C283BACCE008EE6DC /* DesignSystem.swift in Sources */,
Expand All @@ -334,8 +338,8 @@
18DF400E282A8402006B7254 /* MultipleChoiceButton.swift in Sources */,
5F0B738E283BAD6E008EE6DC /* DesignSystem.Color.swift in Sources */,
5F9C58ED2833D0B100070A37 /* ConclusionView.swift in Sources */,
3DFB3605283FB4580045D062 /* Manager.SessionManager.swift in Sources */,
5F0B7390283BC476008EE6DC /* Data.swift in Sources */,
3D12614C283D237A0079E14B /* Manager.AnswerTracker.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
30 changes: 29 additions & 1 deletion SwiftUIQuizz/Manager/Manager.API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ extension Manager {
return url
}

mutating func fetchQuestions(
mutating private func simpleFetch(
category: QuestionCategory,
difficulty: Difficulty,
answerType: AnswerTypes,
Expand All @@ -120,6 +120,34 @@ extension Manager {
return questions
}

mutating func fetchQuestions(
category: QuestionCategory,
difficulty: Difficulty,
answerType: AnswerTypes,
amount: Int = 10
) async throws -> [Question] {
do {
var returnArray: [Question] = []
while returnArray.count < amount {
let questionArr = try await simpleFetch(category: category,
difficulty: difficulty,
answerType: answerType,
amount: amount*2)
for question in questionArr {
if !QuestionStash.shared.isRepeatedQuestion(question: question.question) {
returnArray.append(question)
}
if returnArray.count >= amount {
break
}
}
}
return returnArray
} catch {
throw error
}
}

static func parseResponse(questions: [Manager.API.QuestionAPI]) -> [Question] {
return questions.map { question in
return Question(
Expand Down
2 changes: 1 addition & 1 deletion SwiftUIQuizz/Manager/Manager.AnswerTracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extension Manager {
self.wrongAnswers = 0
}

func startQuiz(questionAmount: Int = 0) {
func resetTracker(questionAmount: Int = 0) {
self.questionAmount = questionAmount
self.correctAnswers = 0
self.wrongAnswers = 0
Expand Down
58 changes: 58 additions & 0 deletions SwiftUIQuizz/Manager/Manager.QuestionStash.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// Manager.QuestionStash.swift
// SwiftUIQuizz
//
// Created by Henrique Finger Zimerman on 26/05/22.
//

import Foundation

extension Manager {
class QuestionStash: Decodable, Encodable {
public static var shared = QuestionStash.loadFromStorage()
private static let fileName = "stash"
private static let limit = 5000

private var questionDict: [String: Bool]
private var questionArray: [String]

private static func loadFromStorage() -> QuestionStash {
guard let answer: QuestionStash = try? SaveSystem.readObject(fileName: QuestionStash.fileName)
else {
return QuestionStash()
}
return answer
}

private init() {
questionDict = [:]
questionArray = []
}

private func saveToStorage() {
do {
try SaveSystem.saveObject(object: self, fileName: QuestionStash.fileName)
} catch {
print(error)
}
}

public func isRepeatedQuestion(question: String) -> Bool {
if questionDict[question] == nil {
return false
}
return true
}

public func addQuestion(question: String, answerStatus: Bool = true) {
guard questionDict[question] == nil else {return}
questionDict[question] = answerStatus
questionArray.append(question)
if questionArray.count > QuestionStash.limit {
questionDict.removeValue(forKey: questionArray[0])
_ = questionArray.remove(at: 0)
}
saveToStorage()
}
}
}
70 changes: 70 additions & 0 deletions SwiftUIQuizz/Manager/Manager.SessionManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// Manager.SessionManager.swift
// SwiftUIQuizz
//
// Created by Henrique Finger Zimerman on 26/05/22.
//

import Foundation

extension Manager {
class SessionManager {
public static let shared = SessionManager()
private var questionArray: [Question] = []
private var questionIndex: Int = 0

var correctAnswers: Int = 0
var wrongAnswers: Int = 0
var questionAmount: Int = 0

enum Errors: Error {
case sessionEnded
}

private init() {
}

func startSession(
category: Manager.API.QuestionCategory,
difficulty: Manager.API.Difficulty,
answerType: Manager.API.AnswerTypes,
amount: Int = 10
) async throws -> Bool {
questionIndex = 0
correctAnswers = 0
wrongAnswers = 0
questionArray = try await Manager.API.shared.fetchQuestions(category: category,
difficulty: difficulty,
answerType: answerType,
amount: amount)
questionAmount = amount
return true
}

func sessionEnded() -> Bool {
return questionIndex >= questionArray.count
}

func currentQuestion() throws -> Question {
guard !sessionEnded()
else {
throw Errors.sessionEnded
}
return questionArray[questionIndex]
}

func addResult(answerStatus: Bool) throws {
guard !sessionEnded()
else {
throw Errors.sessionEnded
}
if answerStatus {
correctAnswers += 1
} else {
wrongAnswers += 1
}
Manager.QuestionStash.shared.addQuestion(question: questionArray[questionIndex].question)
questionIndex += 1
}
}
}
4 changes: 3 additions & 1 deletion SwiftUIQuizz/Views/Buttons/BooleanButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ extension Views {
action: {
isAnimating = true
Manager.SFX.playSound(sound: isCorrect ? .correct: .wrong)
Manager.AnswerTracker.shared.addResult(answerStatus: isCorrect)
do {
try Manager.SessionManager.shared.addResult(answerStatus: isCorrect)
} catch { print(error) }
}
) {
ZStack {
Expand Down
4 changes: 3 additions & 1 deletion SwiftUIQuizz/Views/Buttons/MultipleChoiceButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ extension Views {
action: {
isAnimating = true
Manager.SFX.playSound(sound: isCorrect ? .correct: .wrong)
Manager.AnswerTracker.shared.addResult(answerStatus: isCorrect)
do {
try Manager.SessionManager.shared.addResult(answerStatus: isCorrect)
} catch { print(error) }
}
) {
HStack {
Expand Down
6 changes: 3 additions & 3 deletions SwiftUIQuizz/Views/ConclusionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ extension Views.ConclusionView {
}

class ViewModel: ObservableObject {
@Published var correctAnswers: Int = Manager.AnswerTracker.shared.correctAnswers
@Published var wrongAnswers: Int = Manager.AnswerTracker.shared.wrongAnswers
@Published var totalQuestions: Int = Manager.AnswerTracker.shared.questionAmount
@Published var correctAnswers: Int = Manager.SessionManager.shared.correctAnswers
@Published var wrongAnswers: Int = Manager.SessionManager.shared.wrongAnswers
@Published var totalQuestions: Int = Manager.SessionManager.shared.questionAmount

func answerRate() -> Int {
var rate: Double
Expand Down
7 changes: 4 additions & 3 deletions SwiftUIQuizz/Views/InitialView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,15 @@ extension Views {
.navigationBarHidden(true)
.task {
do {
Manager.AnswerTracker.shared.startQuiz(questionAmount: viewModel.numberQuestions)
let questions = try await Manager.API.shared.fetchQuestions(
let success = try await Manager.SessionManager.shared.startSession(
category: Manager.API.QuestionCategory.allCases[selectedCategoryIndex],
difficulty: Manager.API.Difficulty.allCases[selectedDifficultyIndex],
answerType: Manager.API.AnswerTypes.allCases[selectedTypeIndex],
amount: viewModel.numberQuestions
)
questionsViewModel.update(question: questions.first!)
if success {
try questionsViewModel.update(question: Manager.SessionManager.shared.currentQuestion())
}
} catch {
print(error)
}
Expand Down
50 changes: 31 additions & 19 deletions SwiftUIQuizz/Views/QuestionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@ extension Views {
struct QuestionView: View {
@ObservedObject var viewModel: ViewModel
@State var isAnimating: Bool = false
@State var currentQuestion: Int = 0 {
didSet {
viewModel.update(question: Manager.API.shared.questions[currentQuestion])
isAnimating = false
}
}

var body: some View {
ZStack {
DesignSystem.Color.byCategory(category: viewModel.category).uiColor.edgesIgnoringSafeArea(.all)
Expand Down Expand Up @@ -53,7 +48,6 @@ extension Views {
MultipleChoiceButton(
isAnimating: $isAnimating,
isCorrect: viewModel.checkIfRightAnswer(
questionNumber: currentQuestion,
index: index
),
buttonText: viewModel.answers.count == 0 ? "" : viewModel.answers[index]
Expand All @@ -63,29 +57,30 @@ extension Views {
HStack {
BooleanButton(isAnimating: $isAnimating,
isCorrect: viewModel.checkBooleanQuestion(
answer: "True",
questionNumber: currentQuestion
answer: "True"
),
buttonText: viewModel.answers.count == 0 ? "" : "True"
)
Spacer()
BooleanButton(isAnimating: $isAnimating,
isCorrect: viewModel.checkBooleanQuestion(
answer: "False",
questionNumber: currentQuestion
answer: "False"
),
buttonText: viewModel.answers.count == 0 ? "" : "False"
)
}
case .any:
fatalError("Don't insert .any")
}
if currentQuestion < Manager.API.shared.questions.count - 1 && self.isAnimating {
SwiftUI.Button(action: { currentQuestion += 1 }) {
if !Manager.SessionManager.shared.sessionEnded() && self.isAnimating {
SwiftUI.Button(action: {
viewModel.fetchQuestion()
self.isAnimating = false
}) {
Text("Next Question")
}
} else if currentQuestion >= Manager.API.shared.questions.count - 1 && self.isAnimating {
NavigationLink(destination: ConclusionView(viewModel: .init()).navigationBarHidden(true)
} else if Manager.SessionManager.shared.sessionEnded() && self.isAnimating {
NavigationLink(destination: ConclusionView(viewModel: .init()).navigationBarHidden(true)
.onAppear {
Manager.SFX.playSound(sound: .finished)
}
Expand All @@ -108,19 +103,36 @@ extension Views.QuestionView {
@Published var question: String = ""
@Published var answerType: Manager.API.AnswerTypes = .any
@Published var answers: [String] = []
@Published var questionObj: Question = Question(category: .all,
type: .any,
difficulty: "error",
question: "error",
correct_answer: "error",
incorrect_answers: ["", "", ""])

public func checkIfRightAnswer(questionNumber: Int, index: Int) -> Bool {
public func checkIfRightAnswer(index: Int) -> Bool {
if answers.count == 0 {
return false
}
return answers[index] == Manager.API.shared.questions[questionNumber].correct_answer
// print(questionObj.correct_answer, answers)
return answers[index] == questionObj.correct_answer
}

public func checkBooleanQuestion(answer: String) -> Bool {
// print(answers)
// print(questionObj.correct_answer, answers)
return answer == questionObj.correct_answer ? true : false
}

public func checkBooleanQuestion(answer: String, questionNumber: Int) -> Bool {
return answer == Manager.API.shared.questions[questionNumber].correct_answer ? true : false
public func fetchQuestion() {
do {
try questionObj = Manager.SessionManager.shared.currentQuestion()
update(question: questionObj)
} catch { print(error) }
}

public func update(question: Question) {
self.questionObj = question
self.title = question.category.categoryName
self.image = Image(question.category.categoryName)
self.question = question.question
Expand Down