Firebase with Swift 3 Counting the Number of Children

Firebase with Swift 3 counting the number of children

Firebase data is loaded (and synchronized) asynchronously. This is easiest to see if you add some debug logging:

let ref = firebase.child("users").child(fUID).child("participating")

print("Starting observing");
ref.observe(.value, with: { (snapshot: FIRDataSnapshot!) in
print("Got snapshot");
print(snapshot.childrenCount)
rooms.count = snapshot.childrenCount
})

print("Returning count");
return rooms.count

When you run this snippet, the logging output will be:

Start observing

Returning count

Got snapshot

This is probably not the order you expected the output to be in. And it also explains why your count will never be correct: the data hasn't been loaded yet, so it can't be counted.

This is the reason why Firebase listeners work with callback blocks: the block is invoked when the data is synchronized.

how do you get children count from Firebase in swift?

That's weird... what you posted works for me... maybe the DataObject is not what is expected... Test this by adding a breakpoint and seeing what snapshot is.

You can try this instead... it should yield same result as the one that doesn't work for you, so maybe it won't work either:

snapshot.value.count

EDIT:
Ah, I think I know why you're not able to get the children count property! Try casting snapshot to FDataSnapShot! So try

ref.observeEventType(.Value, withBlock: { (snapshot: FDataSnapshot!) in
count += snapshot.childrenCount
})

Count Firebase Database children and display

If you're listening for .childAdded already, you can just keep a counter and increment that:

var nodeCount: Int = 0

ref.child("offers").observe(.childAdded, with: { snapshot in
nodeCount = nodeCount + 1
counterLbl.text = String(nodeCount)
}

If your use-case also makes it possible that nodes are removed from the database, you should also listen for .childRemoved to decrement the counter:

ref.child("offers").observe(.childRemoved, with: { snapshot in
nodeCount = nodeCount - 1
counterLbl.text = String(nodeCount)
}

More advanced scenario

Note that this approach requires that you download all nodes that you want to count. This should work fine in your current scenario, since you're downloading all offers anyway. But as you get more data, you might want to only read/display a subset of the offers, and in that case the code above would only count the nodes in that subset.

If you still want the count of all offers in that case, the common approach is to keep a separate counter value in the database, that you update every time you add/remove an offer. For more on this, see:

  • In Firebase, is there a way to get the number of children of a node without loading all the node data?
  • How to get size of an element/list in Firebase without get it all?

swift firebase nested children count

One important aspect of Firebase Structures is denormalizing or flattening the structure. Denormalized data generally makes queries much easier and while conceptually the structure you are using works for some tasks, it makes doing the query you want challenging.

So, I would suggest an alternate structure that would make the query super simple, and not loose other functionality.

A change to the structure like this:

groups
group1: true
group2: true

subgroups
subgroup1(autoid)
id
ownerId
description
belongs_to_group: "group1"
subgroup2(autoid)
id
ownerId
description
belongs_to_group: "group2"

Then if you want to count all of subgroups with a particular ownerId

let subGroupsRef = self.ref.child("subgroups")
let query = subGroupsRef.queryOrdered(byChild: "ownerId").queryEqual(toValue: "their id")
query.observeSingleEvent(of: .value) { snapshot in
let count = snapshot.childrenCount
print(count)
}

Edit:

Based on the comment, here's an way to get the count based on your current structure. It's pretty brute force and the code could be reduced considerably but I left it verbose for readability

let groupsRef = self.ref.child("groups")
groupsRef.observeSingleEvent(of: .value, with: { snapshot in
var count = 0
for groupChild in snapshot.children {
let groupSnap = groupChild as! DataSnapshot
for subGroupChild in groupSnap.children {
let subGroupSnap = subGroupChild as! DataSnapshot
let dict = subGroupSnap.value as! [String: Any]
let uid = dict["owner_id"] as! String
if uid == "uid_0" {
count += 1
print(count)
}
}
}
print("total found \(count)")
})

Where this fails is if you have a lot of nodes as they are all initially loaded in (by .value) so it could be iterated over in code. If it's a few thousand it works well and is very fast.

Swift 4 and Firebase How to count child values

The code you shared doesn't read any data, but only updates it with updateChildValues.

To count the number of child nodes, you'll need to read those nodes and then call DataSnapshot.childrenCount.

FIRDatabase.database().reference().child("going").child(postId).observe(DataEventType.value, with: { (snapshot) in
print(snapshot.childrenCount)
})

If you only want to count the child nodes that have a value of 1, you'd do:

FIRDatabase.database().reference().child("going").child(postId)
.queryOrderedByValue().queryEqual(toValue: 1)
.observe(DataEventType.value, with: { (snapshot) in
print(snapshot.childrenCount)
})

For more on this, read the Firebase documentation on sorting and filtering data.

Swift -FirebaseDatabase how to count and iterate through children that are only keyIds?

Mixing different entity types under a node is a bad idea for many reasons, one of them being the one you encountered here. I recommend restructuring your data model to have two top-level lists, with the names properties under one of these, and the push IDs under another.

That said, on your current structure you can get the count by:

  1. Excluding the properties you don't want to count.
  2. Subtracting the number of properties you don't want to count.

The first of these would look like this:

let excludedKeys = ["address", "city", "state", "zipcode", "country"]
Database.database().reference().child("locationRef")
.queryOrderedByKey()
.queryLimited(toLast: 10)
.observeSingleEvent(of: .value) { (snapshot) in
self.totalCountOfKeyIds = 0

for childSnapshot in snapshot.children.allObjects as! [DataSnapshot] {
for child in childSnapshot.children.allObjects as! [DataSnapshot] {
if !excludedKeys.contains(child.key) {
self.totalCountOfKeyIds = self.totalCountOfKeyIds + 1
}
}
}
})

Firebase: Query ordered by children count

There's no native query that lets you order by number of children. Instead, you'll have to maintain a count of the posts, stored in another child, and order the hashtags by the value of that.

You have two choices for this. You can maintain the count on the client whenever it adds a post to a hashtag. Or, write a Cloud Function that automatically keeps the count up to date whenever something changes. In either case, be sure to use a transaction to avoid conflict from multiple writers.

Also bear in mind that, because you want the reverse order of the count of posts, you'll have to store a value that naturally orders from most posts to least posts. You could store a negative count to make sure the most popular posts (which therefore have the smallest values) are ordered ahead of the least popular (which have the largest values).



Related Topics



Leave a reply



Submit