i'm trying to set up a viewCount for my app, when I set the breakpoints up and go through the code it always skips past the queryOrdered and i'm not exactly sure why
func increaseViewCount(username: String, time: NSNumber){
guard let uid = Auth.auth().currentUser?.uid else{
return
}
let refOfUserName = Database.database().reference().child("Users").child(uid)
refOfUserName.observeSingleEvent(of: .value, with: {(snapshot) in
let dictionaryOfUser = snapshot.value as? [String: AnyObject]
// let currentUsersName = dictionaryOfUser?["username"] as? String
let currentUsersName = "hello"
if username == currentUsersName {
print("this is the same user")
}else{
let postRef = Database.database().reference().child("HistoryOfPosts").child("post")
postRef.queryOrdered(byChild: "post").queryEqual(toValue: time).observeSingleEvent(of: .childAdded, with: {(snapshotPost) in
print(snapshotPost.exists())
print(snapshotPost)
let valString = snapshotPost.value
let number = valString as! NSNumber
var value = number.intValue
value = value + 1
let values = ["viewCount": value] as [String:Any]
postRef.updateChildValues(values)
})
}
})
}
The data is loaded from the Firebase Database asynchronously. Instead of waiting for that loading to complete, the program continues with the statement after you attach the observer.
In this case that means that the code pretty much exits increaseViewCount() straight after it attaches the observer. Then once the data comes back from the Firebase servers, the code in your callback block is executed.
To get into the callback block, place a breakpoint on the first statement in that block.
Related
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
}
I have 2 records in my users table
This code below
let fcmTokenRef = Database.database().reference().root.child("users").child(id!).child("fcmToken")
fcmTokenRef.observe(DataEventType.value, with: { (snapshot) in
print(">>",snapshot)
})
will print out the token of a child
How do I adjust my code to print all the tokens for all my children?
You can try
let fcmTokenRef = Database.database().reference().root.child("users").observe(DataEventType.value, with: { (snapshot) in
print(">>",snapshot)
let dic = snapshot.value as! [String:[String:Any]]
Array(dic.values).forEach {
let str = $0["fcmToken"] as! String
print(str)
}
})
You’re requesting a onetime read, hence you’re reading the data once. You need to use .childAdded
Try this:
let fcmTokenRef = Database.database().reference().child(“users”)
fcmTokenRef.observe(.childAdded, with: { (snapshot) in
print(">>",snapshot)
guard let data = snapshot as? NSDictionary else {return}
var each_token = data[“fcmToken”] as? String
print(“all tokens: \(each_token!)”)
})
#puf says something very important:
differences between child added and value firebase
The child_added event fires for each matching child under the node that you query. If there are no matching children, it will not fire.
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()
}
}
})
})
I'm trying to create a class with a failable initializer that takes a FIRUser and FIRDatabaseReference. It downloads the data from the Firebase database and sets its own variables based on what is returned. Else, the initializer is supposed to fail.
What happens is that the data is downloaded in the closure but then everything just reverts back to its default values as if the download had never happened!
I would really like to contain this server chat logic within the class initializer. Any way I can safely achieve this? I've tried a lot of things so far and can't figure it out.
init?(from user: FIRUser, withUserReference ref: FIRDatabaseReference){
let userID = user.uid
var init_succeeded = false
//These values don't matter. If the init fails
//It'll return an empty class.
//Yes this is a hack lol
self.incognito = false
self.email = "NO"
self.username = "NOPE"
self.ref = ref
self.fir_user = user
self.mute_all = false
//Getting the information from the database
ref.child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
//Unpacking user preferences
self.incognito = (value?["incognito"] as? Bool)!
self.mute_all = (value?["mute_all"] as? Bool)!
self.email = (value?["email"] as? String)!
self.username = (value?["username"] as? String)!
init_succeeded = true
}) { (error) in
print("ERROR : \(error.localizedDescription)")
}
if !init_succeeded { return nil }
}
Thanks! - Keenan
Simple answer: No
You simply shouldn't return a value from a function when it's dependent on async statements.
This method will always return nil since init_succeeded is most likely set to true after the method returns. Remember, Firebase queries are asynchronous, so once you call observeSingleEvent, the function doesn't wait for that statement to finish executing, it just runs it asynchronously and goes on with the rest of your code (which is the return in this case).
A completion closure is the closest you can get (but your code won't be exactly contained in the initializer that way):
init(from user: FIRUser, withUserReference ref: FIRDatabaseReference, completion: #escaping (Bool) -> Void){
let userID = user.uid
// default values
self.incognito = false
self.email = "NO"
self.username = "NOPE"
self.ref = ref
self.fir_user = user
self.mute_all = false
//Getting the information from the database
ref.child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
//Unpacking user preferences
self.incognito = (value?["incognito"] as? Bool)!
self.mute_all = (value?["mute_all"] as? Bool)!
self.email = (value?["email"] as? String)!
self.username = (value?["username"] as? String)!
completion(true) // true = success
}) { (error) in
completion(false) // false = failed
print("ERROR : \(error.localizedDescription)")
}
}
And now basically use it like this
let myObject = myClass(from: someUser, withUserReference: someRef, completion: { success in
if success {
// initialization succeeded
}
else {
// initialization failed
}
})
I would suggest though to not retrieve the data in the initializer generally. Perhaps write another function specifically for retrieving the data and only set the default values in init()
func generateDataForRecents() {
if URLArrayStringThisSeason.count == 0 {
self.activityIndicator2.isHidden = false
self.activityIndicator2.startAnimating()
}
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("palettes").queryLimited(toFirst: 100).observe(.value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict as [String:AnyObject]{
let URL = each.value["URL"] as! String
self.URLArrayStringRecents.append(URL)
//print(self.URLArrayString.count)
//print(snapshot)
//let pictureTitle = each.value["title"] as! String
print(self.URLArrayStringRecents.count)
}
}
self.whatsNewCollectionView?.reloadData() //Reloads data after the number and all the URLs are fetched
self.activityIndicator2.stopAnimating()
self.activityIndicator2.isHidden = true
})
}
The following code does a retrieval of data each time the function is called, or when a new data is added.
This is extremely useful when the app is first started up or closed and then restarted. However, when the app is running, whenever a new entry is added, the code seemed to run again and thus appending twice the amount of new data.
For example, when there are already 15 entries identified and then suddenly a new entry is added, the array of the URL would contain 15+16 thus amounting to a total of 31.
How do I make it such that the new data is added to the array instead of adding the entire snapshot in?
You do that by listening for .childAdded events, instead of listening for .value:
var query = databaseRef.child("palettes").queryLimited(toFirst: 100)
query.observe(.childAdded, with: { (snapshot) in
let URL = snapshot.childSnapshot(forPath/: "URL").value as! String
self.URLArrayStringRecents.append(URL)
}
Since you have a limit-query, adding a 101st item means that one item will be removed from the view. So you'll want to handle .childRemoved too:
query.observe(.childRemoved, with: { (snapshot) in
// TODO: remove the item from snapshot.key from the araay
})
I recommend that you spend some time in the relevant documentation on handling child events before continuing.
Please check below method. I have use this method not getting any duplicate entry.
func getallNotes()
{
let firebaseNotesString: String = Firebase_notes.URL
let firebaseNotes = FIRDatabase.database().referenceFromURL(firebaseNotesString).child(email)
firebaseNotes.observeEventType(.Value, withBlock: { snapshot in
if snapshot.childSnapshotForPath("Category").hasChildren()
{
let child = snapshot.children
self.arrNotes = NSMutableArray()
self.arrDictKeys = NSMutableArray()
for itemsz in child
{
let childz = itemsz as! FIRDataSnapshot
let AcqChildKey : String = childz.key
if AcqChildKey == AcqIdGlobal
{
if (childz.hasChildren() == true)
{
let dictChild = childz.value as! NSMutableDictionary
self.arrDictKeys = NSMutableArray(array: dictChild.allKeys)
for i in 0..<self.arrDictKeys.count
{
let _key = self.arrDictKeys.objectAtIndex(i).description()
print(_key)
let dictData : NSMutableDictionary = NSMutableDictionary(dictionary: (dictChild.valueForKey(_key)?.mutableCopy())! as! [NSObject : AnyObject])
dictData.setObject(_key, forKey: "notesId")
self.arrNotes.addObject(dictData)
}
}
}
}
self.tableviewNote.reloadData()
}
})
}
As for the query for removed child,
query.observe(.childRemoved, with: { (snapshot) in
print(snapshot)
let URL = snapshot.childSnapshot(forPath: "URL").value as! String
self.URLArrayStringThisSeason = self.URLArrayStringThisSeason.filter() {$0 != URL}
self.thisSeasonCollectionView.reloadData()
})
it will obtain the URL of the removed child and then update the array accordingly.