1

I am having issues getting data from firebase database and adding them to array. The print output from the method getNewQuote shows that the array is empty but the screenshot shows that the label was updated within the getNewQuote method.

How can this be happening? Is there a latency on the event in the getNewQuote method that is causing this? moreover, how is it that the label was updated with the 4th iteration of the getNewQuote method and not the last iteration.

Thank you in advance!

Breakdown of Code
Iterate in loop 5 times and do the following:
1.create a random number
2. pass random number into the method getNewQuote (This method gets back the data from the database and appends into the array quotesMessages)
After the iteration of the loop, the complete array is printed

Code:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var message: UILabel!

    var QuoteRef:String = "https://motivatr.firebaseIO.com/quotes"

    var quotesMessages = [String]()

    /* */
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        for (var i = 0; i < 5; i++) {
            var currRandomNumber = self.createRandomNum()
            self.getNewQuote(currRandomNumber)
        }

        println("viewDidLoad, array has \(quotesMessages.description)")
    }//eom

    /* */
    override func viewWillAppear(animated: Bool) {
        println("viewWillAppear, array has  \(quotesMessages.description)")
    }

   /* */
   override func viewDidAppear(animated: Bool) {
       println("viewDidAppear, array has  \(quotesMessages.description)")
   }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    /* creates a random number */
    func createRandomNum()-> Int {
        var randomNumberRange = UInt32(49)
        var UnsignedRandomNum = arc4random_uniform(randomNumberRange)
        var randomNum = Int(UnsignedRandomNum)

        return randomNum
    }

    /* gets a new data from databases */
    func getNewQuote(randomNum: Int){
        println("random number \(randomNum) created")
        //Temp var's
        var quoteText = ""
        var quoteAuthor = ""

        var DBQuoteURL = "\(QuoteRef)/\(randomNum)"
        var myRootRef =  Firebase(url:DBQuoteURL)
        myRootRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
            if snapshot.value is NSNull {
                // The value is null
            }
            else
            {
                if let quote = snapshot.value["quote"] as? String {
                    //                    println(quote)
                    quoteText = "'\(quote)'"
                    self.message.text = quoteText
                    self.quotesMessages.append(quoteText)
                }
            }
        })

        println("quote is: \(quoteText)")
    }//eom
}//eoc

Output from console:

random number 24 created
quote is: 
random number 18 created
quote is: 
random number 45 created
quote is: 
random number 47 created
quote is: 
random number 34 created
quote is: 
viewDidLoad, array has []
viewWillAppear, array has  []
viewDidAppear, array has  []

Iphone screenshot: enter image description here

2
  • 1
    Did my comment end up helping your with your problem? Commented Sep 9, 2015 at 19:13
  • yes and no. still learning how to deal with these type of asynchronous operations. had to put this project on hold for now. thanks again Commented Sep 9, 2015 at 20:18

2 Answers 2

4

You've encountered the wonderful problem of asynchronous methods!

I don't know if you know this, but an asynchronous piece of code runs while everything else keeps going, i.e. your code will not wait for its completion to continue.

What's happening here is that your loops gets to the myRootRef.observeSingleEventOfType... piece, starts the async call, and then keeps going, without necessarily having finished all of that code.

This is how it can seem like it's going through the loop out of order, or never getting filled, etc.

I'm not sure what you want to do with your self.message.text = quoteText within getNewQuote, but in the way you have it it'll just update your label with the most recently downloaded quote.

If you'd like to make something happen within the myRootRef.observeSingleEventOfType... piece of code, make sure it's within the curly brackets of if let quote = snapshot.value["quote"] as? String {.......}. If you wanna make it happen when you're done with your downloads, you could try a counter if quotesMessages.count == 5 {...}, within that if let, or an observer or whatever you please.

Best of luck!

Sign up to request clarification or add additional context in comments.

Comments

3

.Value is going to return all children at the specified node. So if you have a node called Quote and 322 children, using .Value will return all 322 child nodes along with every child nodes children. If you want to fill an array with each child node, you would make the observe .Value call and then within the {} underneath, iterate over the returned snapshot to fill the array.

override func viewDidLoad() {
        super.viewDidLoad()

        self.fillArray()


    func fillArray() {
      myRootRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
         if snapshot.value is NSNull {

         } else {
           //init myArray var and then fill with values
           for child in snapshot {
               myArray.append(child.value)
           }

           //now that we have filled the array, we can generate
           //  random numbers and pull quotes from it
         }
      })
}

Notice that we do no other processing until the observeSingleEvent call block completes. So if you want to pull data from it, it must be done inside that block. Remember the power of Firebase is it's asynchronous nature so other things are going on while your app is waiting on the data from Firebase to return.

Second thing is if the desired result is to generate a random number and use that to read a quote from Firebase, you may want to consider a query instead.

So the Firebase data would be structured like this:

Quotes
   autogenerated_child_node_name0
      quote: "No matter where you go, there you are"
      index:  1
   autogenerated_child_node_name1
      quote: "Don't call me Shirley"
      index:  2

now you can avoid reading in all 322 quotes! Generate a random number, say 1 in this case, and query for the quote that has that as an index of 1 and you have your quote without reading in all of them.

Remember though; a loop will need to be crafted that doesn't query for the next quote until the query block completes. This can be done via a while loop and a isDone = YES/NO variable inside the block. When the block completes isDone = YES while will let the while loop execute the next query.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.