user2640633
user2640633

Reputation:

Pulling Data from Firebase into Array

I was recently told to structure my Firebase differently. Before I was putting everything related to a particular user under his or her tree. I was told however to flatten it and create his or her nodes separately and then to just link that node into that users tree when you need to.

So my tree looks like this

root
    card
        *card autoID*
            nickname: "foo"
            type: "bar"

    user
        *user uid*
            card
                *card autoID*: true

I am going to add more to the card as the user progresses through the app, and if I understand how I am supposed to structure the data I will be adding it to the the card node since that card is linked to the user.

My question is how do I pull data from Firebase then into say an array or a dictionary? If it was all in one tree I would do something like this

let ref = FIRDatabase.database().reference()

let user = FIRAuth.auth()?.currentUser

let userCard = ref.child((user?.uid)!).child("card")

But since that card under the user is only a reference how do I then go to the real place where the card is...the part that has the nickname and type?

Edit

So with some help from other SO posts, the documentation, and a friend I have the code 90% working.

What I am able to do is

1) find all of the card autoID under the user node that is associated to the user and store those strings into an array # 1

2) I am able to query all of the card autoID under the node card and then find the ones that match what is in array # 1 and store them in array # 2 (the rest are ignored)

3) **Here is where I am stuck. If I am inside of the .observe then I can do what I want with the array like printing its contents. HOWEVER, if I call print outside of the .observe I get nothing...

here is my code

  func pullCurrentUserCardInfo() {

let userCardsRef = ref.child("users").child((user?.uid)!).child("cards")

userCardsRef.observeSingleEvent(of: .value, with: {(snapshot) in

  if let snapDict = snapshot.value as? [String: AnyObject] {
    for each in snapDict {
      self.usersCardRefArray.append(each.key)

      self.count = Int(snapshot.childrenCount)

    }
  }
})

self.ref.child("cards").observe(.value, with: { (snapshot) in
  if snapshot.hasChildren() {

    for item in snapshot.value as! [String: AnyObject] {

      for test in self.usersCardRefArray {

        if test == item.key {
          self.allCurrentUsersCards.append(item.key)
        }
      }
    }
  } else {
    print("no children")
  }

})
}

if I were to say the following inside of the function but outside of the .observe ....}) then it doesn't do anything.....

for item in allCurrentUsersCards {
    print(item)
} 

Am I missing something small somewhere or is this something to do with firebase?

Upvotes: 0

Views: 508

Answers (1)

Jay
Jay

Reputation: 35648

I think there's an unneeded level of complexity here. You do not need to store (in this use case at least) a separate card for each user. There's a 1-1 relationship between user and card so just storing the card data for each user within the user node would be the best answer.

However, to answer the question directly, here's how to do it. We going to slightly alter the Firebase structure:

root
    cards
        *user uid* <- CHANGE
            nickname: "foo"
            type: "bar"

    users
        user uid: true <- CHANGE

Since user uid's are always unique and created for you, leverage them when working with users. So in this case just store the user uid's in the user node and that same uid in the cards node.

Create a User Class and an array to store them in. This would typically be done right inside a viewController for example

class ViewController: UIViewController {
  class UserClass {
    var uid = ""
    var nickname = ""
    var type = ""
  }

  var usersArray = [UserClass]()

Then, craft a Firebase observer to populate the usersArray, getting each card for each user

//iterate over all of the users, get the user and its card data
let usersRef = ref.child("users") 
usersRef.observeSingleEvent(of: .value, with: { snapshot in

    for snap in snapshot.children { //iterate over all users
        let userSnap = snapshot as! FIRDataSnapshot
        let userKey = userSnap.key //the uid of each user

        //now that we have the uid, get it's card data
        let thisUserCardRef = cardsRef.child("uid")
        thisUserCardRef.observeSingleEvent(of: .value, with: { userSnap in
          let userCardSnap = userSnap as! FIRDataSnapshot
          let userCardDict = userCardSnap.value as! [String:AnyObject]
          let nickname = userCardDict["nickname"]
          let type = userCardDict["type"]

          let aUser = UserClass()
          aUser.userKey = userKey
          aUser.nickname = nickname
          aUser.type = type

          self.usersArray.append(aUser)

          //In general, this is where the tableView is refreshed
          // because the user data and card data is valid at this point
          //usersTableView.reload data /
        })
    }
})

The key here is to remember that Firebase is asynchronous and that code is way faster than the internet. So this high level example will fail most of the time

func getData() {
   loadDataFromFirebase()
   print("data loaded, do something with it") //<- executes before the prior line completes
}

func loadDataFromFirebase() {
  someRef.observeEvent...
     print("data is now valid inside observe closure")
}

This will usually result in

data loaded, do something with it
data is now valid inside observe closure

Which is opposite of what is wanted. Code executes faster than the internet so the asynchronous observe closure will occur after the data loaded... is printed. Only reference and work with firebase data inside a closure and use the closure to pace your app.

If you notice in the first example code provided - we only work with the data once it's been returned from Firebase.

Also note that we completely eliminated queries! Queries are 'heavy' by comparison to observe events and since we are leveraging the uid of each user, it's path will be known, hence the change from a node created with childByAutoId to using the uid.

Upvotes: 1

Related Questions