Firebase number of results that contain a value - iOS Swift - ios

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!

Related

observeSingleEvent with queryEqual to value doesn't work

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.

Trouble retrieving data from Firebase using .Value/.Child added in swift

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
...

Firebase: How expensive is it to observe a lots of nodes?

I am writing an iOS app where I will have to observe a node for every contact a user has. E.g. this means observing up to 30 different nodes.
How expensive is this regarding the traffic that is caued by lots of observations instead of one big observation?
EDIT:
Let's assume every registered user adds new messages to:
- messages
- $userId
- $messageId
- timestamp
- text
Now a logged in user wants to retrieve the messages of all of his contacts (much like twitter):
let contactIds = [userId1, userId2, ...] // Array of Strings
for userId in contactIds {
let messagesRef = self.ref.child("messages").child(userId).observe.queryLimitedToLast(100)
messagesRef.observeEventType(.ChildAdded, withBlock: { (snapshot) -> Void in
// ...
})
}
Since the app shouldn't support followers / following like twitter, this cannot be solved like the firefeed.io example, where every user only observes one single feed node where each one of his contacts writes to.

Firebase: observeEventType() only returns one child

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.

How to save GeoFire with Firebase properties in Swift?

I am trying to save an object that contains a name string, an address string and location coordinates. In the docs it seems that there is one way to save firebase data and another way to save GeoFire data.
Firebase:
var alanisawesome = ["full_name": "Alan Turing", "date_of_birth": "June 23, 1912"]
var gracehop = ["full_name": "Grace Hopper", "date_of_birth": "December 9, 1906"]
var usersRef = ref.childByAppendingPath("users")
var users = ["alanisawesome": alanisawesome, "gracehop": gracehop]
usersRef.setValue(users)
GeoFire:
let geofireRef = Firebase(url: "https://<your-firebase>.firebaseio.com/")
let geoFire = GeoFire(firebaseRef: geofireRef)
geoFire.setLocation(CLLocation(latitude: 37.7853889, longitude: -122.4056973), forKey: "firebase-hq") { (error) in
if (error != nil) {
println("An error occured: \(error)")
} else {
println("Saved location successfully!")
}
}
Is it possible to save both location and other data in the same request? I'd like to do it in the same request because I don't want the user to be able to create an object without location data if the location request fails. Or is there another smart way to impose that restriction?
Since writing the original entity and its geolocation are two separate calls, they will be two separate write operations. One of these writes is done by your code, the other is done by GeoFire. Both calls write to different parts of the JSON tree.
Firebase recently added the ability to write to multiple locations with a single update() call. With that you could write both the geolocation and the entity in one call. If you want to do that, you'll have to change GeoFire to allow for it.
I also struggled for while finding a solution to this, I ended up taking an unorthodox approach. I make my own key title for the GeoFire key entered. Almost like a vin for a car. The first character is a number defining my annotation to use. Next set of numbers is date generated at time of post, after the date is a string of user data because it doesn't matter how long that text is. And that goes as the key. In my maps app the title key is then called and I retrieve the title as a string and cut it up. There are certain characters that are not allowed to be in the GeoFire key title, so you have to restrict the user from typing those.

Resources