So, I want to check if user's device is in my list (for testing). For this I have a small set of values in my Firebase dataset.
It looks like that:
But when I'm trying to check if the user's id is in this list, I'm getting nothing.
let childString = "tempfreeuuid/"
let pointref = ref.child(childString)
let query = pointref.queryOrderedByKey().queryEqual(toValue: "FFFFF")
query.observeSingleEvent(of: DataEventType.value) { (snapshot) in
if snapshot.hasChildren() {
print("that's good")
} else {
print("snapshot has no children")
}
}
And the result is "snapshot has no children".
I've already tried query.observeSingleEvent(of: .childAdded) (doesn't work), .queryLimited(toFirst: 3), and .queryOrderedByKey() — these two don't work either.
At the same time this query is working if I'm not trying to use queryEqual.
What am I doing wrong?
update: I want to clarify the problem I have. I don't have any results, snapshot is null. At the same time if I'm using queryLimited(toFirst: 1) instead of queryEqual, I'm getting one result from the table to work with. It seems to me it's not about snapshot issues, it's about query without any results.
You are using the wrong OrderedBy method. Because you are using queryOrderedByKey the queryEqual function will check if its equal to the Key while you want to compare it to the Value.
Instead you should be using queryOrderedByChild or queryOrderedByValue to make sure you compare the right values.
Here is the reference to the firebase docs explaining how queries (specificly queryEqualToValue in your case) work.
Related
So my goal is to get rid of these bugs completely. I am in a dilemma where each decision leads to a bug.
The first thing I can do that eventually becomes an issue is use a String-interpolated collection path in all my query functions like so:
func getEventName() {
listener = db.collection("school_users/\(user?.uid)/events").order(by: "time_created", descending: true).addSnapshotListener(includeMetadataChanges: true) { (querySnapshot, error) in
if let error = error {
print("There was an error fetching the data: \(error)")
} else {
self.events = querySnapshot!.documents.map { document in
return EventName(eventName: (document.get("event_name") as! String))
}
self.tableView.reloadData()
}
}
}
The thing with this is, when I run the app on the simulator, I am restricted from pressing buttons and then sometimes I can press them and then sometimes they get restricted again. This bug is so confusing because it makes no sense where it springs from.
The other issue is I can use a Constants value in all the query functions in my collections path.
static let schoolCollectionName = "school_users/\(user?.uid)/events"
This is nested in a Firebase struct within the Constants struct. In order to keep Xcode from giving errors I create a let users = Auth.auth().currentUser variable outside the Constants struct. The issue with this value is that when I put that in all of my query functions collection paths, all the buttons are accessible and selectable all the time, but when a user logs out and I log in as a new user, the previous user's data shows up in the new user's tableview.
It would obviously make more sense to use the Constants value because you prevent typos in the future, but I can't figure out how to get rid of the bug where the old user's data shows up in the new user's tableview. Thanks in advance.
The user id should definitely not be a constant. What it sounds like is that right now, you have no reliable way to change users -- your setup probably depends on which user is logged in at app startup, since that's where your variable gets set.
I would do something more like this:
func getEventName() {
guard let user = Auth.auth().currentUser else {
//handle the fact that you don't have a user here -- don't go on to the next query
return
}
listener = db.collection("school_users/\(user.uid)/events").order(by: "time_created", descending: true).addSnapshotListener(includeMetadataChanges: true) { (querySnapshot, error) in
Note that now, user.uid in the interpolated path doesn't have the ? for optionally unwrapping it (which Xcode is giving you a warning for right now). It will also guarantee that the correct query is always made with the currently-logged-in user.
Regarding being able to press the buttons, that sounds like an unrelated issue. You could run your app in Instruments and check the Time Profiler to see if you have long-running tasks that are gumming up the main/UI thread.
I tried googling this problem but it seems like everyone has the opposite problem where the app runs on simulator but not their device. I've been struggling a LOT all week with firebase asynchronous calls returning null and linked the issue to persistence being enabled. All my problems go away if I disable persistence, but I want it enabled. I learned recently about synchronous issues with the different listeners/persistence and have been struggling with firebase returning outdated/nil values for a while.
Simulator was working just a week or two ago and I'm not sure what's changed. I've tried messing with / switching out .observeSingleEvent for .observe and still crashes at this code:
let synced = ref.child("profiles").child((FIRAuth.auth()?.currentUser?.uid)!).child("level")
synced.observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
print(snapshot)
print(snapshot.ref)
if (snapshot.value as! String == "One") {
........//CRASH
With the message:
Could not cast value of type 'NSNull' (0x10b7cf8c8) to 'NSString' (0x10a9dfc40).
When I try to print snapshot, it shows me an empty snapshot. But when I print the ref, the link works and takes me to the right place in my db (where I can see the data exists)
Any ideas how to fix/get around this without disabling persistence? Or more importantly I guess, should I care that it doesn't work in simulator if it works on a device? Will this matter for app store approval / affect future users?
If you'd like to see for yourself that this is an issue of firebase getting a nil/outdated value when the reference exists, here is what I see when I follow the printed ref link
The error seems fairly explicit: there is no value, so you can't convert it to a string.
synced.observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
if (snapshot.exists()) {
if (snapshot.value as! String == "One") {
........//CRASH
i am working on a project where I am pulling data from firebase (which is "queued"). Essentially data is being saved w/ a time stamp so when it's called, it can be ordered sequentially (first in, first out).
The problem I am facing is when I retrieve the data on my app. From my research, on Stack overflow as well as firebase docs, .Value gives a snapshot and continues to listen to data when new data is added. However when new data is added it will take a new snapshot of the entire set (hence "duplicating data" on my app's array).
To get around this I have tried to instead use .ChildAdded, which works well to add new data to my array when new data is added to the firebase database. However it isn't adding the full data set (data that is already existing in the database), which is what I need in addition to new data being added.
Suppose firebase nodes as such:
App_Queue:
Category1:
Timestamp1:
Uid: User_1_uid
Timestamp2:
Uid: User_2_uid
Swift code (2.3):
Case1:
self.databaseRef.child("App_Queue/\(Category1)").queryLimitedToLast(15).observeEventType(.Value, withBlock: { (snapshot) in
if let userDict = snapshot.value as? [String:AnyObject]{
for each in userDict{
let timeIdExtract = each.0 as! String
self.timeIdArray.append(timeIdExtract)
print(self.timeIdArray)
}
//this gives me full snapshot of time stamps & a userId
//but if timestamp3 and user_3_uid is added to firebase, the array is appended with a new snapshot thus, duplicating items.
Case2:
self.databaseRef.child("App_Queue/\(Category1)").queryLimitedToLast(15).observeEventType(FIRDataEventType.ChildAdded, withBlock: { (snapshot : FIRDataSnapshot) in
if let userDict = snapshot.value as? [String:AnyObject]{
for each in userDict{
let timeIdExtract = each.0 as! String // Every follwers ID.
self.timeIdArray.append(timeIdExtract)
print(self.timeIdArray)
}
//this gives me only new items added, but not already added.
// if timestamp3 and user_3_uid is added the array is appended with this new item. But timestamp1 & timestamp2 not added
Case 3:
I have tried a hybrid of Case1 (instead use .observeSingleEventOfType()) & Case2, by adding a self.databaseRef.removeAllObservers() after the code in case 1, and then turning on the .ChildAdded observer for case2. It almost works.... added initial snapshot via case 1, then listens however if say, timestamp2 is recently added it will append this again when Case2 is called, thus duplicating the array.
So my question: how the heck do I go about getting existing objects in the database AND adding on new ones, without duplicating data in the array?
EDIT
Im silly-- had some extraneous code after the block which was throwing things off. DOH! For anyone experiencing issues, first place to check! also .ChildAdded paired with observeEventType did the trick. (takes all previous data already in DB + adds new data as it comes in).
self.databaseRef.child("App_Queue/\(Category1)").queryLimitedToLast(15).observeEventType(.ChildAdded, withBlock: { (snapshot) in
...
Im silly-- had some extraneous code after the block which was throwing things off. DOH! For anyone experiencing issues, first place to check! also .ChildAdded paired with observeEventType did the trick. (takes all previous data already in DB + adds new data as it comes in).
self.databaseRef.child("App_Queue/\(Category1)").queryLimitedToLast(15).observeEventType(.ChildAdded, withBlock: { (snapshot) in
...
I'm trying to fetch the child from https://sizzling-heat-9137.firebaseio.com/users, which has 4. This is what I'm doing:
let ref = Firebase(url:"https://sizzling-heat-9137.firebaseio.com/users")
ref.observeEventType(.ChildAdded, withBlock: {
(snapshot) in
print("Snapshot: \(snapshot)")
})
According to the guides on Firebase this should give a snapshot of every child (4) and their data, but instead, I get the snapshot for only one child, call it X (the first one added but not first in order at /users).
When I do snapshot.childrenCountI get 7, which is the number of children in the first child X. Isn't that supposed to be four which is the total number of children at /users?
Am I doing something bad here or what could be the problem? I have tried both on the simulator and my real device.
I think you have to change your observeEventType from ChildAdded to FIRDataEventType.Value.
Then you can use your snapshot as dictionnary (for example) :
tempUser.firstName = snapshot.value!["firstname"] as! String
it will give number of children at the specified url. just check if ur url is pointing to that or the base of the nodes.
I have a fairly simple scenario whereby I am trying to get the number of users with a particular favorite color. For example, I would like to retreive the number of users that have a favorite color of 'Blue'.
The following code will get me the number of children nodes each user has which in this case is 4 (favoriteColor, displayName, email and provider). I would instead like to get the number of users that have a particular favorite color.
let ref = Firebase(url: "https://project.firebaseio.com/users")
ref.queryOrderedByChild("favoriteColor").queryEqualToValue("Blue").observeEventType(.ChildAdded, withBlock: { snapshot in
print(snapshot.childrenCount)
})
I am trying to keep a live count of the number of users with a particular favorite color via UILabel so I will update the label each time there is a change to the number of results.
Is there currently a way to do this?
I solved this by changing .ChildAdded to .Value.
I also changed observeEventType to observeSingleEventOfType
let ref = Firebase(url: "https://project.firebaseio.com/users")
ref.queryOrderedByChild("favoriteColor").queryEqualToValue("Blue").observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot.childrenCount)
})
Hope this helps someone in future!