Filling an array in Swift with Firebase dictionary - ios

I am trying to fetch data from Firebase and add it as a object in a Swift array but whenever I run the application the array prints out empty. When I put a print statement inside the observe each purse prints out like I want it to but the purse never gets appended to the array.
var purses = [Purse]()
override func viewDidLoad() {
fetchPurse(completion: {print(self.purses.count)})
}
func fetchPurse(completion: #escaping () -> ()){
let ref = FIRDatabase.database().reference(fromURL: "https://test-database-ba3a2.firebaseio.com/")
let user = (FIRAuth.auth()?.currentUser?.uid)!
let userRef = ref.child("users").child(user).child("devices")
userRef.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]{
let purse = Purse()
purse.setValuesForKeys(dictionary)
self.purses.append(purse)
completion()
}
}, withCancel: nil)
}

This sounds like a misunderstanding of how async methods work.
If you use code like this:
//at this point purses is empty..
fetchPurse()
print("purses count = \(purses.count)")
You will always see an empty purses array.
The problem is that the fetchPurse() function uses the Firebase function observe, which is asynchronous. It requests that Firebase run the code you pass as your with: closure when a new entry is added. The observe function returns immediately, and invokes the closure you pass to it as some future time when FireBase adds a new child object.
As a result, your fetchPurse() function also returns before the new purse object is added to your array of purses.
As #DávidPásztor said in his comment, you should refactor fetchPurse to take a completion handler that gets called when the purses array is updated:
func fetchPurse(completion: () -> ){
let ref = FIRDatabase.database().reference(fromURL: "https://test-database-ba3a2.firebaseio.com/")
let user = (FIRAuth.auth()?.currentUser?.uid)!
let userRef = ref.child("users").child(user).child("devices")
userRef.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]{
let purse = Purse()
purse.setValuesForKeys(dictionary)
self.purses.append(purse)
//This is the new line that calls your new completion handler
completion()
}
}, withCancel: nil)
}
And then you'd call it like this:
fetchPurse(completion: {
print("purses count = \(purses.count)")
}

Related

Can't seem to save local array in Swift

So I'm running into a problem where I can't seem to save the contents of a local array outside of a for loop. This code iterates through notifsTop (which is a dictionary) and saves the values into another array. However, outside of the for loop, the contents of tempnotifsarray are empty, which doesn't make sense to me since I appended them to an array that is outside of the loop block. I have been struggling with this for some time and can't figure out what is going on. Any help would be appreciated, thanks!
func createArray() -> [notificationLabel] {
let newUserInfo = Auth.auth().currentUser
let uid = newUserInfo?.uid
self.ref = Database.database().reference()
let practionerRef = self.ref.child("users").child(uid!)
var tempnotifsArray: [notificationLabel] = []
practionerRef.observeSingleEvent(of: .value, with: {(snapshot) in
let value = snapshot.value as? NSDictionary
if let notifsTop = value?["Notifications"] as? NSDictionary { //top of the notifications hierarchy
for (_, myValue) in notifsTop {
// Iterate in here
//self.notifications.append(myValue)
let notification = notificationLabel(label: myValue as! String)
tempnotifsArray.append(notification)
//if I print here the array is full with the values I want
}
}
})
print(tempnotifsArray) //comes out as []
return tempnotifsArray
}

(Firebase) printing wrong number of users in database when counting users in array

I am retrieving a snapshot of all of the users in my Firebase database. After I append all of my users from my "users" node of my database to an array of users , I attempt to print the user count (which should be 12) and it says it is 0 instead.
I added a breakpoint at the closing brace of the if-statement which shows to me that the user count reaches 12. However, by the time it comes time to print this number, it is zero. Stepping through a bunch of times is just a lot of assembly code that tells me nothing.
I call this function in the viewDidLoad() method and tried to print the users count after this function is called in addition to printing it within the function and it is still 0.
func getUsers() {
let ref = Database.database().reference().child("users")
ref.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
user.id = snapshot.key
user.setValuesForKeys(dictionary)
self.users.append(user)
}
}, withCancel: nil)
print(users.count)
}
Here you go, a fix below. Like Ahmed is saying: it takes some time to get the data since it is async.
func getUsers(completionHandler:#escaping (Int) -> ()){
let ref = Database.database().reference().child("users")
ref.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let user = User()
user.id = snapshot.key
user.setValuesForKeys(dictionary)
self.users.append(user)
completionHandler(self.users.count)
}
})
override func viewDidLoad() {
super.viewDidLoad()
getUsers(){ (count) in
print(count)
}
}
It is pretty straightforward what is going on, I just added a completion handler.
Because observe work asynchronously, and you are printing users.count before updating users array...

printing only a array with all data with firebase

printed result
["data1"]
["data1", "data2"]
["data1", "data2", "data3"]
...
wanted result is only the last printed array with all data or just getting that last array would help me out
["data1", "data2", "data3"]
here is the code I use to get the data from firebase
var dataArray = [String]()
func fetchFirebaseData(){
var datref: DatabaseReference!
datref = Database.database().reference()
datref.child("Data").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]{
let data = SpeisekarteInfos(dictionary: dictionary)
data.setValuesForKeys(dictionary)
self.dataArray.append(data.Name!)
print(self.dataArray)
}
}, withCancel: nil)
}
You are observing .childAdded events, which will get called repeatedly, once for each entry in your database. I haven't used FireBase, but based on a little Googling it sounds like you should instead observe the .value event, and then refactor your code to expect all of them at once. Something like this:
var dataArray: [String]?
func fetchFirebaseData(){
dataArray = []
var datref: DatabaseReference!
datref = Database.database().reference()
datref.child("Data").observe(.value, with: { (snapshot) in
for item in snapshot.children {
if let dictionary = item as? [String: AnyObject]{
let data = SpeisekarteInfos(dictionary: dictionary)
data.setValuesForKeys(dictionary)
self.dataArray.append(data.Name!)
}
}
print(self.dataArray)
}, withCancel: nil)
}
I'm doing some guesswork on the format of the data you get back from a .value call, so I'm not positive the above will work, but it should at least point you in the right direction.
Note that in my version we request everything at once with a .value observer, then loop through all the children returned, build them into an array, and only print the array once they're all returned.
From what I'm reading it looks like your .value observer will be called with all the elements every time the database is updated. If your database is dynamic this would not be a good approach.

How to pass data out of a closure that retrieves info from Firebase?

I'm trying to pass data that I've retrieved from my firebase database into a field of my singleton. The data is received via closure, and in that closure I'm passing some data into my singleton's properties. If I print the data structure inside the closure (after everything's been assigned) I get the output I'm expecting, but if I print it at the end of the initializer after all the data should've been passed in, it's empty.
import Foundation
import Firebase
class EmployeeList {
static let sharedInstance = EmployeeList()
var employeeDictionary: [String: [EmployeeData]]
var ref: DatabaseReference!
private init() {
employeeDictionary = [String: [EmployeeData]]()
ref = Database.database().reference()
ref.child("employeeList").observeSingleEvent(of: .value, with: { snapshot in
if let dictionary = snapshot.value as? [String: [String: AnyObject]] {
for subsection in dictionary {
var subsectionEmployees: [EmployeeData] = []
for item in subsection.value {
self.ref.child("employeeList/\(subsection.key)/\(item.key)").observeSingleEvent(of: .value, with: { employeeSnapshot in
let employeeObject = EmployeeData(snapshot: employeeSnapshot)
subsectionEmployees.append(employeeObject)
self.employeeDictionary[subsection.key] = subsectionEmployees
//print(self.employeeDictionary) This print statement prints out the expected data every time another employee is appended
})
}
}
}
//print(self.employeeDictionary) This print statement prints an empty data structure
})
}
}
get data from Firebase as Below
var messagedata = [String:AnyObject]()
let databaseReff = Database.database().reference().child("message")
databaseReff.queryOrdered(byChild: "fromId").queryEqual(toValue: self.recieverId).observe(.value, with: { snapshot in
if snapshot.exists(){
self.messagedata = snapshot.value! as! [String : AnyObject]
self.getAllMessagesSent(snapshot: self.messagedata)
} else
self.getAllMessagesSent(snapshot: self.messagedata) //Function Created
}
})
pass the data fetched from Clousre to a dictionary and pass that dict to a function and do anything you want to do or use escaping blocks
func getAllMessagesSent(snapshot: [String:AnyObject]) {
//data is here
}

I need to know when firebase observation done completely

I am trying to observe multiple data at once using firebase, the observation block keeps looping until it fetchs all the data. I need to know when it is actually done so I can execute another block. How can I do that?
databaseRef.child("First_Secondary_Grade").child("0").child("0").queryOrderedByKey().observe(.childAdded, with: {
(snapshot) in
if let dictoinary = snapshot.value as? [String: AnyObject] {
let dataofthequsation = structofthedata()
dataofthequsation.setValuesForKeys(dictoinary)
}
})
i think i figured it out
let databaseRef = FIRDatabase.database().reference()
var gotitall = 0
// First you need to observe single event to get the real count of children in swift 3 observe will count the keys inside a child. That's why!
databaseRef.child("First_Secondary_Grade").child("0").child("0").observeSingleEvent(of:.value, with:{ (snap) in
gotitall = Int(snap.childrenCount)
databaseRef.child("First_Secondary_Grade").child("0").child("0").observe(.childAdded, with: {
snapshot in
if let dictoinary = snapshot.value as? [String: AnyObject] {
let dataofthequsation = structofthedata()
dataofthequsation.setValuesForKeys(dictoinary)
self.dataofthequsation.append(dataofthequsation)
// this is will run when the block runs through all children
if gotitall == self.dataofthequsation.count {
completion()
}
}
})
})

Resources