I’m new to Swift. I’m trying to make a quiz app using the API, I can show a question and answers, but I can’t move on to the next questions. I can access different questions in the api with the index, but when I click on the options, I can’t go to the next question. I looked at similar applications and watched a few how to make quiz app videos from youtube, but I couldn’t do it.
my QuizManager
import Foundation
protocol quizManagerDelegate {
func didUpdateQuiz(_ Quizmanager: QuizManager ,quiz: QuizModel)
}
struct QuizManager {
var index : Int = 0
var maxQuestion = 14
mutating func nextQuestion(result: Bool) -> Int{
if result == true{
return index + 1
} else {
return index
}
}
var delegate: quizManagerDelegate?
func performRequest(){
let urlString = "https://opentdb.com/api.php?amount=15&type=multiple"
if let url = URL(string: urlString){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
print(error!)
return
}
if let safeData = data{
if let quiz = self.parseJSON(quizdata: safeData){
delegate?.didUpdateQuiz(self, quiz: quiz)
}
}
}
task.resume()
}
}
func handle(data: Data?, response: URLResponse?, error: Error?) -> Void {
}
func parseJSON(quizdata: Data) -> QuizModel? {
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(Welcome.self, from: quizdata)
let correct = decodedData.results?[index].correct_answer ?? "error"
let quest = decodedData.results?[index].question ?? "error"
let incorrect = decodedData.results?[index].incorrect_answers ?? ["error"]
let question = QuizModel(correctAnswer: correct, question: quest, falseAnswer: incorrect)
return question
} catch {
print(error)
return nil
}
}
mutating func nextQuestion(){
if index + 1 < maxQuestion {
index += 1
}else {
index = 0
}
}
}
my QuizData
import Foundation
// MARK: - Welcome
struct Welcome: Codable {
let results: [Result]?
}
// MARK: - Result
struct Result: Codable {
let category: String?
let question, correct_answer: String?
let incorrect_answers: [String]?
}
my QuizModel
import Foundation
struct QuizModel {
let correctAnswer : String
let question : String
let falseAnswer : [String]
}
my ViewController
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var ScoreLabel: UILabel!
@IBOutlet weak var ChoiceButton4: UIButton!
@IBOutlet weak var ChoiceButton3: UIButton!
@IBOutlet weak var ChoiceButton2: UIButton!
@IBOutlet weak var ChoiceButton1: UIButton!
@IBOutlet weak var QuestionTextView: UITextView!
var quizMangager = QuizManager()
var score = 0
var theQuiz: QuizModel?
@IBAction func OptionsButtonPressed(_ sender: UIButton) {
guard let thisQuiz = theQuiz,
let btnTitle = sender.currentTitle
else { return }
if btnTitle == thisQuiz.correctAnswer {
score += 1
ScoreLabel.text = "SCORE: (score)"
quizMangager.nextQuestion()
sender.setTitleColor(.systemGreen, for: [])
} else {
sender.setTitleColor(.systemRed, for: [])
quizMangager.nextQuestion()
}
Timer.scheduledTimer(timeInterval: 0.35, target: self, selector: #selector(updateUI), userInfo: nil, repeats: false)
QuestionTextView.text = thisQuiz.question
}
@objc func updateUI() {
}
override func viewDidLoad() {
super.viewDidLoad()
QuestionTextView.layer.cornerRadius = 15
quizMangager.delegate = self
quizMangager.performRequest()
}
}
extension ViewController : quizManagerDelegate{
func didUpdateQuiz(_ Quizmanager: QuizManager, quiz: QuizModel) {
DispatchQueue.main.async { [self] in
self.theQuiz = quiz
self.QuestionTextView.text = quiz.question
var allOptions = []
allOptions.append(quiz.falseAnswer[0])
allOptions.append(quiz.falseAnswer[1])
allOptions.append(quiz.falseAnswer[2])
allOptions.append(quiz.correctAnswer)
let generatedValue = Array(allOptions.shuffled().prefix(4))
print(generatedValue)
print(quiz.correctAnswer)
ChoiceButton1.setTitle(generatedValue[0] as? String, for: .normal)
ChoiceButton2.setTitle(generatedValue[1] as? String, for: .normal)
ChoiceButton3.setTitle(generatedValue[2] as? String, for: .normal)
ChoiceButton4.setTitle(generatedValue[3] as? String, for: .normal)
}
}
}
2
Answers
Your code has lots of problems need to be solved.
I will explain as detail as possible and you will need to try it yourself.
First of all, you have
QuizManager
to controlQuiz
but you don’t store QuizData here you delegate data back toViewController
. So when you update answer you callquizMangager.nextQuestion()
is pointless becauseQuizManager
don’t control or has any data.For this you first need to refactor your
QuizManager
.protocol quizManagerDelegate
->protocol QuizManagerDelegate
for right naming convention.performRequest
you get yoursafeData
just parse one time and get the list Result. You can convert to listQuizModel
if you like and save it in yourQuizManager
QuizManagerDelegate
, just notice to the view controller the quizModel in order for updatingAnd in your
performRequest
code will be like thiswith
listQuizData
is the list result you parse and convert fromJson
inperformRequest
nextQuestion
after you update the index just need to calldelegate?.didUpdateQuiz(quiz:listQuizData[index])
Come to the
ViewController
You just need to take the
QuizModel
and view itBecause you are "new to Swift" I’ll skip critique of your code… but a couple notes…
Since you are using the "new"
UIButton
styles, you should use the button configuration instead ofsetTitle()
/.setTitleColor()
, etc.To "move on to the next question" add
quizManager.performRequest()
to yourupdateUI()
function.Take a look at this modified version of your
ViewController
class: