Swift executing firebase calls out of order - ios

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

Related

TableView events not being added from firebase

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
}

How to Append Filter Data on Array from Firebase in Swift

Please find my code below. How can we append filter data on array from Firebase?
var childrenList = [DatabaseList]()
let ref = Database.database().reference(withPath: "Messages")
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
for childSnapshot in snapshot.children{
print(childSnapshot)
self.childrenList.append(snapshot)
}
})
DispatchQueue.main.async {
self.tableView.reloadData()
}
let ref = Database.database().reference(withPath: "Messages")
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
print(snapshot)
for (childSnapshotId, childSnapshotValue) in snapshot {
if let dataListDict = childSnapshotValue as? [String: AnyObject] {
//Init you newModel with the dataListDict here
let newModel = DatabaseList(dict: dataListDict)
print(childSnapshot)
self.childrenList.append(newModel)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
class DatabaseList : NSObject {
var messageBody : String?
var name : String?
var videoID : String?
init(dict: [String: AnyObject]) {
messageBody = dict["MessageBody"]
name = dict["Name"]
videoID = dict["videoID"]
}
}
Your query is correct but there are few mistakes in finishing block.
self.childrenList.append(snapshot) snapshot is an instance of DataSnapshot not a DatabaseList so you can not append it like this.
for childSnapshot in snapshot.children {
/// childSnapshot is an instance of DataSnapshot not a dictionary but its value will be
guard let data = (childSnapshot as! DataSnapshot).value else {continue}
let dataDict = data as! Dictionary<String, Any>
/// Initializing the new object of DatabaseList and passing the values from data
let list: DatabaseList = DatabaseList()
list.messageBody = dataDict["MessageBody"] as? String
list.name = dataDict["Name"] as? String
list.videoID = dataDict["VideoID"] as? String
/// This is correct, and now you can append it to your array.
childrenList.append(list)
}
Apart from this you will have to reload the tableView inside the finishing block not below the block because this is an asynchronous request and data will come later.
Also its always better to check the data existence. snapshot.exists().
One more suggestion if you want to fetch the data just once then do not use .observe use .observeSingleEvent instead. .observe will fire the block every time there is any change at this node.
Here is the full code snippet.
let query = ref.queryOrdered(byChild: "VideoID").queryEqual(toValue: "12345").observe(.value, with: { (snapshot) in
if !snapshot.exists() {
// Data doesn't exist
return
}
for childSnapshot in snapshot.children {
guard let data = (childSnapshot as! DataSnapshot).value else {continue}
let dataDict = data as! Dictionary<String, Any>
let list: DatabaseList = DatabaseList()
list.messageBody = dataDict["MessageBody"] as? String
list.name = dataDict["Name"] as? String
list.videoID = dataDict["VideoID"] as? String
childrenList.append(list)
}
/// Reload your tableView here
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
And expecting the class model like below:
class DatabaseList: NSObject {
var messageBody: String?
var name: String?
var videoID: String?
}

Let constant issue

I have this function that is grabbing events from firebase that a specific user is attending. Everything seems to be working up until the point where I try to alter an element relative to the event snapshot.
I get the error
Cannot assign to property: 'event' is a 'let' constant
//will show the vents that a user is attending
static func Events(for user: User, completion: #escaping ([Event]) -> Void)
{
var currentEvents = [Event]()
//Getting firebase root directory
let ref = Database.database().reference().child("users").child(user.uid).child("Attending")
ref.observe(.value, with: { (snapshot) in
// print(snapshot)
// guard snapshot.children.allObjects is [DataSnapshot] else {
// return completion([])
// }
guard let eventDictionary = snapshot.value as? [String: Any] else {
return completion([])
}
// print(snapshot)
let dispatchGroup = DispatchGroup()
eventDictionary.forEach({ (key,value) in
// print(key)
// print(value)
EventService.show(forEventKey: key , completion: { (event) in
dispatchGroup.enter()
AttendService.isEventAttended(event, byCurrentUserWithCompletion: { (isAttended) in
//error happens here
event?.isAttending = isAttended
dispatchGroup.leave()
})
currentEvents.append(.init(currentEventKey: key , dictionary: (event?.eventDictionary)!))
completion(currentEvents)
})
})
})
}
It is really confusing to me because I can't see where I declared it as a let constant.
This is the code for the method I am using to gather event info
static func show(forEventKey eventKey: String, completion: #escaping (Event?) -> Void) {
// print(eventKey)
let ref = Database.database().reference().child("events").child(eventKey)
// print(eventKey)
//pull everything
ref.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { (snapshot,eventKey) in
// print(snapshot.value ?? "")
guard let event = Event(snapshot: snapshot) else {
return completion(nil)
}
completion(event)
})
}
Below is my model for an event object
import Foundation
import FirebaseDatabase.FIRDataSnapshot
struct Event: Keyed {
var key: String?
let currentEventName: String
let currentEventImage: String
let currentEventPromo: String?
let currentEventDescription: String
//nested properties
let currentEventStreetAddress: String
let currentEventCity: String
let currentEventState: String
let currentEventDate: String?
let currentEventTime: String?
let currentEventEndTime: String?
let currentEventZip: Int
var category: String
//nested properties stop
var currentAttendCount: Int
var isAttending = false
var eventDictionary: [String: Any]{
let dateDict = ["start:date":currentEventDate, "start:time": currentEventTime,"end:time":currentEventEndTime]
return ["event:name":currentEventName,"event:imageURL" : currentEventImage,
"event:description": currentEventDescription, "attend:count": currentAttendCount,
"event:street:address": currentEventStreetAddress,"event:zip": currentEventZip,
"event:state": currentEventState, "event:city": currentEventCity, "event:promo": currentEventPromo ?? "", "event:date": dateDict, "event:category":category]
}
init(currentEventKey: String, dictionary: [String:Any]) {
self.key = currentEventKey
self.currentEventName = dictionary["event:name"] as? String ?? ""
self.currentEventImage = dictionary["event:imageURL"] as? String ?? ""
self.currentEventDescription = dictionary["event:description"] as? String ?? ""
self.currentEventPromo = dictionary["event:promo"] as? String ?? ""
self.currentAttendCount = dictionary["attend:count"] as? Int ?? 0
self.category = dictionary["event:category"] as? String ?? ""
//nested properties
self.currentEventStreetAddress = dictionary["event:street:address"] as? String ?? ""
self.currentEventCity = dictionary["event:city"] as? String ?? ""
self.currentEventState = dictionary["event:state"] as? String ?? ""
self.currentEventZip = dictionary["event:zip"] as? Int ?? 0
let eventDate = dictionary["event:date"] as? [String: Any]
self.currentEventDate = eventDate?["start:date"] as? String ?? ""
self.currentEventTime = eventDate?["start:time"] as? String ?? ""
self.currentEventEndTime = eventDate?["end:time"] as? String ?? ""
}
init?(snapshot: DataSnapshot) {
guard let dict = snapshot.value as? [String : Any],
let currentEventName = dict["event:name"] as? String,
let currentEventImage = dict["event:imageURL"] as? String,
let currentEventDescription = dict["event:description"] as? String,
let currentEventPromo = dict["event:promo"] as? String,
let category = dict["event:category"] as? String,
let currentEventStreetAddress = dict["event:street:address"] as? String,
let currentEventCity = dict["event:city"] as? String,
let currentEventState = dict["event:state"] as? String,
let currentEventZip = dict["event:zip"] as? Int,
let currentAttendCount = dict["attend:count"] as? Int,
let eventDate = dict["event:date"] as? [String: Any],
let currentEventDate = eventDate["start:date"] as? String,
let currentEventTime = eventDate["start:time"] as? String,
let currentEventEndTime = eventDate["end:time"] as? String
else { return nil }
self.key = snapshot.key
self.currentEventName = currentEventName
self.currentEventImage = currentEventImage
self.currentEventDescription = currentEventDescription
self.currentEventStreetAddress = currentEventStreetAddress
self.currentEventCity = currentEventCity
self.currentEventState = currentEventState
self.currentEventZip = currentEventZip
self.currentAttendCount = currentAttendCount
self.currentEventPromo = currentEventPromo
self.currentEventDate = currentEventDate
self.currentEventTime = currentEventTime
self.currentEventEndTime = currentEventEndTime
self.category = category
}
}
Event is a struct, so its instances are passed by value. That is, the call completion(event) passes a copy of event to completion.
Since completion doesn't take the Event as an inout, it is treated as a let, not a var, so you cannot modify it.
You could change the type of completion to #escaping (inout Event?) -> Void. Then the Event will be treated as a var, and changes to it will be effectively copied back out to the caller.
However, that won't make a difference in your case, because you're creating a brand new Event instance for the call to completion (by saying guard let event = Event(snapshot: snapshot)). So even if you let completion modify the Event it receives, you just throw it away after. You also need to write it back to your database somehow after completion returns, if you want the change to isAttending to have any effect.

How can I retrieve data from many child in firebase database

I was trying to retrieve data from firebase. I can retrieve data from a single child. But how can I retrieve data from many child?
I tried this way
func fatchSchdual(){
ref.child("Products").child(productdetailes[myIndex].userId!).child("Schedule").child("0").observeSingleEvent(of: .value) { (snapshot) in
if snapshot.childrenCount > 0{
for Schdu in snapshot.children.allObjects as! [DataSnapshot]{
let schdual = Schdu.value as? [String: AnyObject]
let startTime = schdual?["start"]
let endTime = schdual?["end"]
if let StartTime = startTime{
self.publishTime.text = StartTime as? String
}
}
}
}
}
but I didn't get any data.
func fatchFacilities(){
ref.child("Products").child(productdetailes[myIndex].userId!).child("Facility").observeSingleEvent(of: .value) { snapshot in
for child in snapshot.children {
if let value = (child as! DataSnapshot).value as? [String : Any] {
let name = value["name"] as? String
let unit = value["unit"] as? String
let facility = Facility(name: name, unit: unit)
facilities.append(facility)
}
}
}
}

Convert observe .value to .childAdded in swift

Currently I'm having some problems with this bit of code that is loading data from firebase database into an array. Since this is inside of viewDidLoad I have to empty my array food = [] before loading the data into it, if I don't then it will duplicate all the objects and I will have double duplicates the second time it loads, triple the third time and etc... However this was not a good fix for multiple reasons so what I would like is that it would only add new objects from the database with .childAdded however if I just switch out .value with .childAdded it will crash, I get a Thread 1: signal SIGABRT on this line: let dict = user_snap.value as! [String: String?]. I am pretty new to swift and don't know how to fix this, would really appreciate some help.
let parentRef = Database.database().reference().child("Recipes")
let storage = Storage.storage()
parentRef.observe(.value, with: { snapshot in
if ( snapshot.value is NSNull ) {
// DATA WAS NOT FOUND
print("– – – Data was not found – – –")
} else {
//Clears array so that it does not load duplicates
food = []
// DATA WAS FOUND
for user_child in (snapshot.children) {
let user_snap = user_child as! DataSnapshot
let dict = user_snap.value as! [String: String?]
//Defines variables for labels
let recipeName = dict["Name"] as? String
let recipeDescription = dict["Description"] as? String
let downloadURL = dict["Image"] as? String
let storageRef = storage.reference(forURL: downloadURL!)
storageRef.getData(maxSize: 1 * 1024 * 1024) { (data, error) -> Void in
let recipeImage = UIImage(data: data!)
food.append(Element(name: recipeName!, description: recipeDescription!, image: recipeImage!))
self.tableView.reloadData()
}
}
}
})
let dict = user_snap.value as! [String: String?]
Instead of
let dict = snapshot.value as! Dictionary<String, String>
and maybe you can do null test :
let dict = snapshot.value as! Dictionary<String, String>
if let recipeName = dict["Name"] as String!, let recipeDescription = dict["Description"] as String!, let downloadURL = dict["Image"] as String! {
let storageRef = storage.reference(forURL: downloadURL)
storageRef.getData(maxSize: 1 * 1024 * 1024) { (data, error) -> Void in
let recipeImage = UIImage(data: data!)
food.append(Element(name: recipeName, description: recipeDescription, image: recipeImage!, downloadURL: downloadURL))
self.tableView.reloadData()
}
}else {
print("Error! Could not decode data")
}
try this. It should work
for child in snapshot.children.allObjects as! [FIRDataSnapshot] {
let dict = child.value as! Dictionary<String, Any>
//.....
}

Resources