I am trying to update values in a tableView value from firebase. The values are being put in the array while in the observe closure, but afterwards, it remains 0, this makes sense because it is asynchronous (correct me if I am wrong). However, this has worked before in previous tableView, so I am not sure what the problem is. This is the code:
let id = Auth.auth().currentUser?.uid
Database.database().reference().child("users").child(id!).child("createdEvents").observe(.value) { snapshot in
//self.eventsArray.removeAll()
let ref = Database.database().reference().child("users").child(id!).child("createdEvents")
ref.observe(.value) { snapshot in
print("start")
print(snapshot.childrenCount)
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot {
self.eventsArray.append(rest.key as! String)
}
Database.database().reference().child("Events").observe(.value) { (data) in
let events = data.value as! [String:[String:Any]]
for(_,value) in events{
print(self.eventsArray)
if(self.eventsArray.contains(value["EventName"]! as! String)){
self.actualEvents.append(AdminEvents(evName: value["EventName"]! as! String, evDesc: value["EventDescription"]! as! String, evStartDate: value["start time"]! as! String, evEndDate: value["end time"] as! String, evNumPeople: value["NumberOfPeople"]! as! Int, evNumRegistered: value["currentPeople"] as! Int))
}
}
print("Actual events array " + "\(self.actualEvents)")
}
}
self.tblEvents.reloadData()
}
And it prints out the values that I want it to when I tests it.
The end objective here is to go into the users createdEvents and print eventTest and eventTest1 to the tableView, but no values inside there.
Edit: I forgot to add the events firebase structure, it does exist.
I don't see any child in your DB with "Events" name.
Database.database().reference().child("Events").observe(.value) { (data) in
Ok I fixed it. For those wondering, I simply just deleted the observe closure around this part, because I realized that the first part was already doing that.
let ref = Database.database().reference().child("users").child(id!).child("createdEvents")
ref.observe(.value) { snapshot in
print("start")
print(snapshot.childrenCount)
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot {
self.eventsArray.append(rest.key as! String)
}
Here is the new code:
Database.database().reference().child("users").child(id!).child("createdEvents").observe(.value) { snapshot in
//self.eventsArray.removeAll()
print("start")
print(snapshot.childrenCount)
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot {
self.eventsArray.append(rest.key as! String)
}
Database.database().reference().child("Events").observe(.value) { (data) in
let events = data.value as! [String:[String:Any]]
for(_,value) in events{
print(self.eventsArray)
if(self.eventsArray.contains(value["EventName"]! as! String)){
self.actualEvents.append(AdminEvents(evName: value["EventName"]! as! String, evDesc: value["EventDescription"]! as! String, evStartDate: value["start time"]! as! String, evEndDate: value["end time"] as! String, evNumPeople: value["NumberOfPeople"]! as! Int, evNumRegistered: value["currentPeople"] as! Int))
}
}
print("Actual events array " + "\(self.actualEvents)")
}
self.tblEvents.reloadData()
}
self.tblEvents.dataSource = self
self.tblEvents.delegate = self
}
Related
My code makes calls to my firebase database, but the order in which it receives the data is incorrect in terms of the function call. It calls the data from ref3 then ref2 then ref4 and I would like for it to retrieve the data in order of ref2, ref3, ref4 of course. No matter what it will always do it in this order.
var ref2: DatabaseReference?
var ref3: DatabaseReference?
var ref4: DatabaseReference?
ref2 = Database.database().reference().child("User data").
ref3 = Database.database().reference().child("User Info").child("Name")
ref4 = Database.database().reference().child("User Info").child("Address")
ref2?.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0{
for data in DataSnapshot.children.allObjects as![DataSnapshot]{
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["Username"] as! String
let n: String = proObj?["User login"] as! String
}
}
})
ref3?.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0{
for data in DataSnapshot.children.allObjects as![DataSnapshot]{
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User first name"] as! String
let n: String = proObj?["User last name"] as! String
}
}
})
ref4?.observe(DataEventType.value, with:{(DataSnapshot) in
if DataSnapshot.childrenCount > 0{
for data in DataSnapshot.children.allObjects as![DataSnapshot]{
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User email"] as! String
}
}
})
When querying data from your firebase database, you are performing an asynchronous call. To put things in simple terms, your code is executed on a different thread and, subsequently, performs parallel operations. This is exactly what is happening in your case.
You are observing data from three different references, and even though you have defined their sequence programmatically, nothing guarantees that the code within the completion handler blocks of your observers will run in that exact same sequence.
If you want to run them sequentially, then you have to nest your observers so that the next database query is executed only after the previous one has finished.
The below should hypothetically work
ref2?.observe(DataEventType.value, with: { (DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for data in DataSnapshot.children.allObjects as! [DataSnapshot] {
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["Username"] as! String
let n: String = proObj?["User login"] as! String
}
}
ref3?.observe(DataEventType.value, with: { (DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for data in DataSnapshot.children.allObjects as! [DataSnapshot] {
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User first name"] as! String
let n: String = proObj?["User last name"] as! String
}
}
ref4?.observe(DataEventType.value, with: { (DataSnapshot) in
if DataSnapshot.childrenCount > 0 {
for data in DataSnapshot.children.allObjects as![DataSnapshot] {
let proObj = data.value as? [String: AnyObject]
let p: String = proObj?["User email"] as! String
}
}
}) // ref4 observer
}) // ref3 observer
}) // ref2 observer
I try to retrieve data from Firebase into Array. Because it runs asynchronously, the results that I want to show in my CollectionView is a delay until I switch back and forth. I am very new to asynchronous functions in iOS. Please help me to complete my code.
ref = Database.database().reference(withPath: "MyTest/Video")
ref?.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.exists() { return }
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let autoID = child.key as String //get autoID
let title = snapshot.childSnapshot(forPath: "\(autoID)/Title").value
let url = snapshot.childSnapshot(forPath: "\(autoID)/URL").value
let views = snapshot.childSnapshot(forPath: "\(autoID)/Views").value
self.arrayAllTitle.append(title as! String)
self.arrayAllId.append(url as! String)
self.arrayAllDesc.append(views as! String)
}
}
})
You need to reload the collection after you retrieve the data so after the for loop call reloadData()
for child in result {
}
self.collectionView.reloadData()
//
func getValueFromDatabase(completion: #escaping (_ status: Bool) -> Void){
ref = Database.database().reference(withPath: "MyTest/Video")
ref?.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.exists() { return }
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let autoID = child.key as String //get autoID
let title = snapshot.childSnapshot(forPath: "\(autoID)/Title").value
let url = snapshot.childSnapshot(forPath: "\(autoID)/URL").value
let views = snapshot.childSnapshot(forPath: "\(autoID)/Views").value
self.arrayAllTitle.append(title as! String)
self.arrayAllId.append(url as! String)
self.arrayAllDesc.append(views as! String)
}
completion(true)
}
else {
completion(false)
}
})
}
//
self.getValueFromDatabase { (status) in
if status {
// success
}
}
I'm working with Firebase in my project right now. I would suggest the following solution: wrap the database observer in a distinct function which gets completion block as a parameter.
func getValueFromDatabase(completion: ()->Void){
ref = Database.database().reference(withPath: "MyTest/Video")
ref?.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.exists() { return }
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let autoID = child.key as String //get autoID
let title = snapshot.childSnapshot(forPath: "\(autoID)/Title").value
let url = snapshot.childSnapshot(forPath: "\(autoID)/URL").value
let views = snapshot.childSnapshot(forPath: "\(autoID)/Views").value
self.arrayAllTitle.append(title as! String)
self.arrayAllId.append(url as! String)
self.arrayAllDesc.append(views as! String)
}
completion()
}
})
}
This way you can call the function from anywhere providing the desired action after fetching data from db is finished:
getValueFromDatabase(completion:{
self.collectionView.reloadData() //or any other action you want to fulfil
})
I'm trying to fill the collectionView with posts. I have to get the posts, then get some data for the users who posted them. For some reason it isn't working.
DataService.ds.REF_POSTS.child("\(self.loggedInUser!.uid)").queryLimitedToLast(30).observeSingleEventOfType(.Value, withBlock: { postDictionary in
if postDictionary.exists() {
if let snapshots = postDictionary.children.allObjects as? [FIRDataSnapshot] {
self.posts = [Post]()
for snap in snapshots {
if let postDict = snap.value as? NSDictionary {
for(name, value) in postDict {
let interval = postDict.objectForKey("timePosted") as! Double
let formattedDate = NSDate(timeIntervalSince1970: interval)
let timeAgo = self.getDate(formattedDate)
if name as! String == "postedBy" {
DataService.ds.REF_USERS.child(value as! String).observeSingleEventOfType(.Value, withBlock: { (userDictionary) in
let userDict = userDictionary.value as! NSDictionary
let username = userDict.objectForKey("username")!
let profileThumbUrl = userDict.objectForKey("profileThumbUrl")!
let key = snap.key
let post = Post(postKey: key, dictionary: postDict, username: username as! String, profileThumbUrl: profileThumbUrl as! String, timeAgo: timeAgo)
self.posts.append(post)
})
}
}
}
}
}
}
self.collectionView?.reloadData()
})
It works if I perform the reload() right after appending the posts, but there is some sort of memory leak. There isn't a problem in the Post class or filling the collection view, if I use dummy values. The problem is in this code that I posted. I think I have an extra loop or something can anyone help?
If you fear that reload() is the reason of the memory leak , you can use this hack:-
if name as! String == "postedBy" {
DataService.ds.REF_USERS.child(value as! String).observeSingleEventOfType(.Value, withBlock: { (userDictionary) in
let userDict = userDictionary.value as! NSDictionary
let username = userDict.objectForKey("username")!
let profileThumbUrl = userDict.objectForKey("profileThumbUrl")!
let key = snap.key
let post = Post(postKey: key, dictionary: postDict, username: username as! String, profileThumbUrl: profileThumbUrl as! String, timeAgo: timeAgo)
self.posts.append(post)
if posts.count == postDictionary.childrenCount{
self.collectionView?.reloadData()
}
})
}
Then also see this answer :- Firebase observeSingleEventOfType stays in memory
My query to load tableViewData doesn't work anymore. My code below.
let imageKeysQuery = ref.child("categories").child("Multimedia").child("Fotos").queryOrdered(byChild: "date")
imageKeysQuery.observeSingleEvent(of: .value, with: {
(snapshot) in
for (item) in (snapshot.children) {
if (item as AnyObject).value!["release"] as! Bool != false {
self.keysForImages.append((item as AnyObject).value!["key"] as! String)
self.datesForImages.append((item as AnyObject).value!["date"] as! String)
} else {
let post = "Freizugeben!"
let childUpdates = ["/categories/Multimedia/AAAAA-Fotos-Not-Released/\((item as AnyObject).value!["key"] as! String)": post]
self.ref.updateChildValues(childUpdates)
}
}
self.downloadImages()
})
I get an error message in every line where a (item as AnyObject).value![".."] is. Below is the error message:
Type NSFastEnumerationIterator.Element (aka 'Any') does not conform to protocol AnyObject.
UPDATE:
Found a solution:
let newsQuery = (ref.child("categories").child("Aktuelles").child("de")).queryOrdered(byChild: "date")
newsQuery.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let data = (child as! FIRDataSnapshot).value! as! NSDictionary
self.titleTableData.append(data["title"]! as! String)
self.textTableData.append(data["text"]! as! String)
self.postKeys.append(data["key"]! as! String)
self.dateTableData.append(data["date"]! as! String)
}
self.loadimages()
})
The important thing is to convert child to FIRDataSnapshot as a NSDictionary!
Hope this helps someone.
I am trying to get data from Firebase, I have tried like this:
FIREBASE_REF.childByAppendingPath("tasks").observeEventType(.Value, withBlock: { (snapshot) -> Void in
print(snapshot.value)
self.tasks = [Task]()
var task = Task()
let data = snapshot.value as! NSDictionary
let tasksFromServer = data.allValues as! [NSDictionary]
for taskFromServer in tasksFromServer {
task.description = taskFromServer.objectForKey("description") as! String
task.startTime = taskFromServer.objectForKey("startTime") as! String
task.endTime = taskFromServer.objectForKey("endTime") as! String
task.progress = taskFromServer.objectForKey("progress") as! Int
let priorityTemp = taskFromServer.objectForKey("priority") as! Int
switch priorityTemp {
case 0: task.priority = .Low
case 1: task.priority = .Medium
case 2: task.priority = .High
default: break
}
task.assignee = taskFromServer.objectForKey("assignee") as! String
self.tasks.append(task)
}
MBProgressHUD.hideAllHUDsForView(self.view, animated: true)
self.tableView.reloadData()
}
but it shows error in this line:
let data = snapshot.value as! NSDictionary
It says: Could not cast value of type '__NSArrayM' (0x10ebfc8d8) to 'NSDictionary' (0x10ebfcd60).
My data from Firebase like this:
But in other side, I use another code in another ViewController to get users name and role from Firebase, it works.
FIREBASE_REF.childByAppendingPath("users").observeEventType(.Value, withBlock: { (snapshot) -> Void in
self.names = []
self.roles = []
let data = snapshot.value as! NSDictionary
let employees = data.allValues as! [NSDictionary]
for employee in employees {
let name = (employee.objectForKey("firstName") as! String) + " " + (employee.objectForKey("lastName") as! String)
self.names.append(name)
let role = employee.objectForKey("position") as! String
self.roles.append(role)
}
MBProgressHUD.hideAllHUDsForView(self.view, animated: true)
self.collectionView.reloadData()
})
But why the first code always crash.
Any helps would be appreciated. Thanks.
Firebase transforms your dictionary object to an array if more than 50% of keys are between 0 and maximum key (exactly your case with one zero element).
https://www.firebase.com/docs/ios/guide/understanding-data.html#section-arrays-in-firebase