Reputation: 1301
My observer listens for new room
s being added in Firebase then updates a local array when one is added. It's working except when I add a room to Firebase, two of that room are added to the array. For example if I have 4 rooms in Firebase (Room One, Room Two, etc), when the app loads, there are 8 rooms in the table view (two Room One's, two Room Two's, etc). Then adding, for example, Room Five, two Room Fives would show up.
private func observeRooms() {
guard let uid = FIRAuth.auth()?.currentUser?.uid else {print("Error getting user UID"); return}
let userRoomRef: FIRDatabaseReference = FIRDatabase.database().reference().child("users").child(uid).child("rooms")
userRoomRef.observe(.childAdded, with: { (roomSnap) in
for eachRoom in roomSnap.value as! [String : AnyObject] {
let roomID = eachRoom.key as! String
print("Roomsnap 2 (observer): \(roomSnap.value)")
if let roomInfo = roomSnap.value as? [String : AnyObject] {
guard let roomName = roomInfo["roomName"] as! String! else {print("Error getting room name"); return}
guard let participants = roomInfo["participants"] as! [String]! else {print("Error getting room participants"); return}
print("\n\nRoom Name: \(roomName)\nRoom Participants: \(participants)\nRoom ID: \(roomID)\n")
self.usersRooms.append(Room(id: roomID, name: roomName, participants: participants))
print("User's Rooms: \(self.usersRooms)\n")
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
})
}
Why is the data being fetched twice? Is there a way to write the function to just read each room once?
Firebase DB JSON:
"tzfHgGKWLEPzPU9GvkO4XE1QKy53" : {
"gender" : "male",
"handle" : "TestHandleOne",
"name" : "Timothy",
"profilePicture" : "https://graph.facebook.com/*removed*/picture?type=large&return_ssl_resources=1",
"rooms" : {
"-KhY2GnJOxBwdPK669ui" : {
"participants" : [ "tzfHgGKWLEPzPU9GvkO4XE1QKy53" ],
"roomName" : "Room One"
},
"-KhY2Hnz48lTtRpzmBuw" : {
"participants" : [ "tzfHgGKWLEPzPU9GvkO4XE1QKy53" ],
"roomName" : "Room Two"
},
"-KhY2IZL4l16dMxGopt6" : {
"participants" : [ "tzfHgGKWLEPzPU9GvkO4XE1QKy53" ],
"roomName" : "Room Three"
},
"-KhY8SdHnkfI7bZIpyjI" : {
"participants" : [ "tzfHgGKWLEPzPU9GvkO4XE1QKy53" ],
"roomName" : "Room Four"
}
}
}
roomSnap.value
printed to the console, each room printed twice with different values for Room ID. This is Room One for example:
Working function (final edit):
private func observeRooms() {
guard let uid = FIRAuth.auth()?.currentUser?.uid else {print("Error getting user UID"); return}
let userRoomRef: FIRDatabaseReference = FIRDatabase.database().reference().child("users").child(uid).child("rooms")
roomRefHandle = userRoomRef.observe(.childAdded, with: { (snapshot) -> Void in
let roomData = snapshot.value as! Dictionary<String, AnyObject>
let id = snapshot.key
guard let name = roomData["roomName"] as! String! else {print("Error getting user name"); return}
self.usersRooms.append(Room(id: id, name: name, participants: [uid]))
// Add new room to "rooms" in Firebase
let roomDict = ["roomName" : name, "participants": [uid]] as [String : Any]
let newRoomRef = self.roomRef.child(id)
newRoomRef.setValue(roomDict)
self.tableView.reloadData()
})
}
Upvotes: 0
Views: 1874
Reputation: 35677
The .childAdded function reads in each child, one at a time, when first called and then any new children thereafter.
This event is triggered once for each existing child and then again every time a new child is added to the specified path. The listener is passed a snapshot containing the new child's data.
The key here is that it automatically iterates over each room in the node one at a time. The code in the question is redundant which leads to multiple entries in the array.
Here's a super short way to populate the array with each child node
let usersRef = self.ref.child("users")
let uid = "user_0" //some uid
let userRoomRef = usersRef.child(uid).child("rooms")
userRoomRef.observe(.childAdded, with: { roomSnap in
let roomDict = roomSnap.value as! [String: AnyObject]
let roomID = roomSnap.key
let roomName = roomDict["roomName"] as! String
print("roomID = \(roomID) roomName = \(roomName)")
var aRoom = Room() //could also initWith...
aRoom.id = key
aRoom.name = roomName
//aRoom.participants = partipants
self.usersRooms.append(aRoom)
self.tableView.reloadData()
})
Edit
In response to a comment, an alternative structure is
users
uid_0
name: "Tom"
uid_1
name: "Jerry"
rooms
room_0
name: "Cool Room"
users:
uid_0: true
uid_1: true
room_1
name: "Romper Room"
users
uid_0: true
Tom and Jerry both belong to the Cool Room, but only Tom belongs to Romper Room.
If you want to populate an array/tableView with the rooms that Tom belongs do, you can simply deep query on the rooms/users/ where uid_0 == true.
Upvotes: 1