0

I am trying to retrieve data from firebase and display into multiple buttons and label. After retrieving i am saving it into a dictionary. This is my model

import Foundation
import Firebase

    class QuestionModel: NSObject {

        var CorrectAnswer: String!
        var Question: String!
        var optionA: String!
        var optionB: String!
        var optionC: String!

        init(snapshot: FIRDataSnapshot) {
            if let snapshotDict = snapshot.value as? Dictionary<String, Any> {
                CorrectAnswer = snapshotDict["CorrectAnswer"] as? String
                Question = snapshotDict["Question"] as? String
                optionA = snapshotDict["optionA"] as? String
                optionB = snapshotDict["optionB"] as? String
                optionC = snapshotDict["optionC"] as? String
            }
        }
    }

My JSON

enter image description here

I tried some ways but it return me nil .I think coz of asynchronous firebase.

This is my code .

import Foundation
import UIKit
import Firebase
import FirebaseDatabase

class AnsweringQuestionViewController: UIViewController {

    @IBOutlet weak var qLabel: UILabel!
    @IBOutlet weak var buttonA: UIButton!
    @IBOutlet weak var buttonB: UIButton!
    @IBOutlet weak var buttonC: UIButton!
    @IBOutlet weak var correctAnswer: UIButton!

    //var buttons: [UIButton]! thinking of using button tags?

    var ref: FIRDatabaseReference!
    var questionModel : [QuestionModel] = []

    override func viewDidLoad(){
        super.viewDidLoad()
      //  FIRDatabase.database().persistenceEnabled = true
        ref = FIRDatabase.database().reference()
        db()
    }
    func db(){
        ref.child("Science").observe(.value, with: {
            snapshot in
            for child in snapshot.children {
                let user = QuestionModel.init(snapshot: (child as? FIRDataSnapshot)!)
                self.questionModel.append(user)
            }
         self.qLabel.text = self.questionModel[0].Question
        self.buttonA.setTitle("\(self.questionModel[0].CorrectAnswer)", for: UIControlState.normal)
        }, withCancel: nil)
        }
}

Pls bare with my code i am still learning. I don't have any idea where to go from here in displaying my data into multiple buttons and identifying which button is 'clicked' is the CorrectAnwer.

I would be very happy if you could reconstruct my code to a cleaner way if you want. Thank you :)

AL.
  • 36,815
  • 10
  • 142
  • 281
yveszenne
  • 666
  • 8
  • 18

1 Answers1

0

Try this...

import FirebaseDatabase

class Question {

   var correctAnswer: String?
   var question: String?
   var optionA: String?
   var optionB: String?
   var optionC: String?

   init(snapshot: FIRDataSnapshot){
       if let snap = snapshot.value as? [String: String] {
           correctAnswer = snap["CorrectAnswer"]
           question = snap["Question"]
           optionA = snap["OptionA"]
           optionB = snap["OptionB"]
           optionC = snap["OptionC"]
       }
   }

}




import FirebaseDatabase
import UIKit

class ViewController: UIViewController {

   @IBOutlet weak var qLabel: UILabel!
   @IBOutlet weak var buttonA: UIButton!
   @IBOutlet weak var buttonB: UIButton!
   @IBOutlet weak var buttonC: UIButton!
   @IBOutlet weak var correctAnswer: UIButton!

   var fireRootRef: FIRDatabaseReference!
   var questions = [Question]()

   override func viewDidLoad() {
       super.viewDidLoad()
       fireRootRef = FIRDatabase.database().reference()
       fetchQuestions(inCategory: "Science")
   }


   func fetchQuestions(inCategory category: String){
       let questionsRef = fireRootRef.child(category)

       questionsRef.observe(.value, with: {(snapshot)-> Void in

           var allQuestions = [Question]()
           for child in snapshot.children {
               allQuestions.append(Question(snapshot: child as! FIRDataSnapshot))
           }
           self.questions = allQuestions
           self.reloadUI(withQuestions: self.questions)
       })
   }

   func reloadUI(withQuestions questions: [Question]){
       guard questions.count > 0 else {return}

        if let answerToQuestion1 = questions[0].correctAnswer, let wrongAnswer = questions[0].optionA, let wrongAnswer2 = questions[0].optionB, let wrongAnswer3 = questions[0].optionC {

            let randomOrderedAnswers = [answerToQuestion1, wrongAnswer, wrongAnswer2, wrongAnswer3].shuffled()

            buttonA.setTitle(randomOrderedAnswers[0], for: .normal)
            buttonB.setTitle(randomOrderedAnswers[1], for: .normal)
            buttonC.setTitle(randomOrderedAnswers[2], for: .normal)
            correctAnswer.setTitle(randomOrderedAnswers[3], for: .normal)//keep in mind the correctAnswer label wont always contain the actual correct answer now

        } else {
            print("failed to get value for one of the answes options")
        }

       if let questionText = questions[0].question {
           qLabel.text = questionText
       } else {
           qLabel.text = "failed to get question text"
       }
   }

}




    extension MutableCollection where Indices.Iterator.Element == Index {
/// Shuffles the contents of this collection.
    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }

        for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
            guard d != 0 else { continue }
            let i = index(firstUnshuffled, offsetBy: d)
            swap(&self[firstUnshuffled], &self[i])
        }
    }
}

extension Sequence {
    /// Returns an array with the contents of this sequence, shuffled.
    func shuffled() -> [Iterator.Element] {
        var result = Array(self)
        result.shuffle()
        return result
    }
}

update I've added extensions that allow you to shuffle the order of elements in an array so that you can set the answer labels "randomly" and the correct answer won't always be in the same place. The extension are courtesy of @NateCook from this question here

To know if the correct answer was selected just check if the text of the selected button is == to the correctAnswer property of the question, for example if buttonA.title(for: .normal)! == questions[0].correctAnswer {print("correct answer selected)"} . Just make sure to unwrap the optional value of buttonA.title(for: .normal) otherwise it will have the word "optional" before it and therefore won't be exactly equal to the correct answer text

Community
  • 1
  • 1
MikeG
  • 3,745
  • 1
  • 29
  • 51
  • Yes it works in displaying in buttons. Now my problem is all the correctAnswer is always in the correctAnswer button. But i'd take the chance to ask this , how can i display it into a random buttons and know if the button clicked is the correct answer(a print statement is enough for me). – yveszenne Mar 17 '17 at 08:10
  • see my updated answer and you may accept this is the correct answer – MikeG Mar 17 '17 at 13:33
  • From the bottom of heart . Thank you! You helped me a lot.Yes it works now :). Thank you for the last paragraph it helps me get the overview of what i'll do next. Thank you again :) – yveszenne Mar 17 '17 at 17:31
  • I have a follow-up question. Given i have IBaction func next(), how can i move from question 1 to question2? – yveszenne Mar 17 '17 at 18:19
  • you're welcome! Thats a very broad question, theres lots of ways to accomplish that, i highly recommend you try it yourself and if you have trouble, post the code you tried into a new question. However, here are my thoughts on how to do that... in the `next()` function, get the next element in the `questions` array that was set from the retrieved firebase values, and reload all the UI labels/button titles with the properties in the new question object. So when use taps "next". You execute `setupNextQuestion()` , which gets the next question object from array of questions and resets all labels – MikeG Mar 17 '17 at 20:16