0

I was working on a new page in one of my apps and was facing a small issue involving Firebase and While Loops. Below is my code:

var user = User()

var i = 0

while i < 101 {

print("Print Statement 1: " + String(i))

//Correctly prints i, incrementing it by 1 every time like it should.

self.ref.child("Directory")
    .child(String(i))
    .observeSingleEvent(of: .value, with: { (snapshot) in

        print("Print Statement 2: " + String(i))

        //Always prints 101

        let nameValue = snapshot.value as? NSDictionary

        if nameValue != nil {

            user.id = String(i)

            //Always get set to 101, and not the current value of i

        }

    }) { (error) in

        print(error.localizedDescription)

}

i += 1

}

Logs:

Print Statement 1 :

0
1
2
3
4
etc.

Print Statement 2:

101
101
101
101
101
etc.

Pretty much the only question I have here is why is the second print statement always printing 101 and not the incremented value of i. Is this something related to the Firebase Observer not observing the single event every time the while loop is executed? Also, what can I do to fix this?

Thanks, KPS

kingkps
  • 156
  • 1
  • 13
  • 1
    First; don't do that. Firebase is asynchronous and that while loop is going to fire way faster than the data can be returned from the internet. Second; what are you trying to accomplish specifically? Data in Firebase is only valid within the closure so that's the only time it should be worked with. In this case, that while loop is going to get to 101 *before firebase can retrieve the data from firebase* and complete the closure even once. e.g. If you have to run Firebase in a tight loop, you're probably doing it wrong. – Jay Nov 04 '17 at 21:32
  • Yes, that’s the whole issue. Is there anything else I can do? I am trying to loop through all of the items in the database to see which one of them exists. When I find one that exists, I set the user.id to the number of the one that exists and then I get all of the data from the nameValue variable and put it into an array. Would adding a delay to the end of the while loop work to resolve this issue? – kingkps Nov 04 '17 at 21:35
  • No need to loop. Just check to see if the node exists (not nil). See my answer to [this question](https://stackoverflow.com/questions/35682683/checking-if-firebase-snapshot-is-equal-to-nil-in-swift/35682760#35682760) – Jay Nov 04 '17 at 21:37
  • The problem is, I don’t know if the data is stored under Directory/0 or Directory/15, which is why I am trying to loop through all of the possible Directory/i directorys and get the ones that exist. – kingkps Nov 04 '17 at 21:43
  • Can you please include a snapshot of your Firebase Structure (as text please, NO images. Firebase console->Export JSON). Also update your question with what *data* you are looking for; i.e. /Directory/0/data_being_looked for. Also, it appears (for the moment) you've names your directories 0, 1, 2 which is an array. Arrays should generally be avoided in Firebase so there may be a better way to structure the data. – Jay Nov 05 '17 at 12:58
  • Hi Jay, Anushk’s solution was able to solve the issue. Thanks – kingkps Nov 06 '17 at 00:35
  • Glad it was able to resolve the issue. However. You will encounter enormous issues running Firebase Asynchronous code in a tight loop like that. On top of that you are blocking the main thread which is going to cause UI issues for your user and you may find that searching data in Firebase in that fashion is highly inefficient - it indicates you may have long term problems with the Firebase Structure. – Jay Nov 06 '17 at 15:33

1 Answers1

1

That's because firebase queries your request asynchronously and the completion handler (where you're printing i) so happens to get called when your while loop is finished and i has a value of 101.

Edit: A workaround would be to have a separate counter and incrementing only when you're checking for values. So, that would change only when the results are in and you can know which result was nil. However, this won't be reliable as network requests can be slow and unordered but should work in most cases

var user = User()

var i = 0

var counter = 0

while i < 101 {

...

self.ref.child("Directory")
    .child(String(i))
    .observeSingleEvent(of: .value, with: { (snapshot) in

        ...

        let nameValue = snapshot.value as? NSDictionary

        if nameValue != nil {

            user.id = String(i)
            // prints the item number
            print(counter)

        }
        // increment the counter when you actually have data
        counter += 1

    }) { (error) in

        print(error.localizedDescription)

}

i += 1

}
Anushk
  • 482
  • 4
  • 20
  • Oh ok, is there is a way for me to catch the value of i where nameValue is not nil? – kingkps Nov 04 '17 at 21:32
  • A workaround would be to have a separate counter and incrementing only when you're checking for values. So, that would change only when the results are in and you can know which result was `nil`. However, this won't be reliable as network requests can be slow and unordered but should work in most cases – Anushk Nov 04 '17 at 21:47
  • Hi Anushk, can you give a code example of this? Thanks. – kingkps Nov 04 '17 at 21:49
  • Glad it helped :) Please accept the answer https://stackoverflow.com/help/someone-answers – Anushk Nov 04 '17 at 22:31