3

I have an array of strings and I am trying to populate it through firebase. It is a chat application and when a user creates a room he or she names the room. When the user logs in and goes to the landing page it queries all the rooms that he or she is participating in and I want that to fill the tableview. In the firebase docs i found childrenCount but I cannot seem to get it to work. This is what I have tried so far

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

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

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

    return rooms.count
}

I get an error that count is a get only property. How to i populate that array count?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
RubberDucky4444
  • 2,330
  • 5
  • 38
  • 70
  • you cant design the tableview this way, the var "room" has to be initialized before you call tableView.reloadData(), function numberOfRowsInSection should contain only one line of code "return rooms.count" and you modify rooms in diferent function (for example in viewDidLoad) – Mazel Tov Jun 20 '16 at 09:00
  • 1
    because it looks the problem with your code is this `rooms.count = snapshot.childrenCount` – Mazel Tov Jun 20 '16 at 09:01
  • @MazelTov yes the problem is with that line, any idea how to fix it? – RubberDucky4444 Jun 20 '16 at 09:08
  • there is couple of ways how to do it, I would do it that in viewDidLoad you will do the Firebase query and save rooms in some array... and in numberOfRowsInSection you will just return the count of objects... – Mazel Tov Jun 20 '16 at 09:46

3 Answers3

11

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.

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
1

I guess that "rooms" is an array of class Room.

"an error that count is a get only property" happen when you try to set the range of array "rooms" - You can't do that.

'count' properties is read-only access. In person language, It be like. You have a bag. You put a apple to the bag. You put another apple to the bag. Now you have 2 apple. You can't only say "My bag have 2 apples."

To fix it:

  • You have to create variable with type 'Room' class for each snapshot : FIRDataSnapshot
  • Add it to rooms.

Example:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

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

ref.observe(.value, with: { (snapshot: FIRDataSnapshot!) in
    print(snapshot.childrenCount)

    let room = Room()
    // Use you snapshot(FIRDataSnapshot) to create the data of the room.
    rooms.append(room)
})

return rooms.count

}

Luan Tran
  • 1,142
  • 7
  • 15
  • I get an error on the let room = Room()..use of unresolved identifier – RubberDucky4444 Jun 20 '16 at 10:46
  • This is incorrect as you are calling an asynchronous call within numberOfRowsInSection which needs to return synchronously without waiting. The better way to do this is to make your Firebase calls in viewDidLoad or some setup time like that, store the data in a property and within the callback block call tableView.reloadData – Sherwin Zadeh Mar 19 '19 at 05:10
0

My way is to have the closure. When the call has completed then it will return the number of children

typealias countAllPostsResult = (UInt) -> Void
class func countAllPosts(isDeleted: Bool,completedHandler: @escaping countAllPostsResult) {
    print("countAllPosts ...is running...", isDeleted)
    var dataSnapshot = [DataSnapshot]()

    Constants.Commons.posts_node_ref.queryOrdered(byChild: Constants.Posts.is_deleted).queryStarting(atValue: "false").queryEnding(atValue: "false\u{f8ff}").observeSingleEvent(of: .value, with: { (snapshot) -> Void in

        for snap in snapshot.children {
            dataSnapshot.append(snap as! DataSnapshot)
        }
        if dataSnapshot.count > 0 {
            completedHandler(UInt(dataSnapshot.count))
        } else {
            completedHandler(0)
        }
    })

}
coders
  • 2,287
  • 1
  • 12
  • 20