【问题标题】:Firebase observer reading data twiceFirebase 观察者两次读取数据
【发布时间】:2017-04-18 13:57:38
【问题描述】:

我的观察者会监听 Firebase 中添加的新 rooms,然后在添加时更新本地数组。它的工作原理是当我向 Firebase 添加一个房间时,其中两个房间被添加到阵列中。例如,如果我在 Firebase 中有 4 个房间(房间 1、房间 2 等),当应用程序加载时,表格视图中有 8 个房间(两个房间 1、两个房间 2 等)。然后添加,例如,五号房间,两个五号房间会出现。

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()
                }
            }

        }
    })
}

为什么数据被提取两次?有没有办法编写只读取每个房间一次的函数?

Firebase 数据库 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 打印到控制台,每个房间打印两次,房间 ID 的值不同。例如这里是一号房间:

工作函数(最终编辑):

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()
    })
}

【问题讨论】:

  • 你在哪里调用observeRooms方法?
  • viewDidAppear
  • 该代码中有很多冗余,并且 .childAdded 观察者没有被正确使用 - 请参阅我的答案。

标签: ios swift firebase firebase-realtime-database


【解决方案1】:

.childAdded 函数在第一次调用时读取每个孩子,一次一个,然后再读取任何新的孩子。

此事件为每个现有的孩子触发一次,然后再次触发 每次将新的孩子添加到指定的路径时。听众是 传递了一个包含新孩子数据的快照。

这里的关键是它会自动迭代节点中的每个房间一次。问题中的代码是多余的,导致数组中有多个条目。

这是一种用每个子节点填充数组的超短方法

    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()
    })

编辑

作为对评论的回应,另一种结构是

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 和 Jerry 都属于 Cool Room,但只有 Tom 属于 Romper Room。

如果您想用 Tom 所属的房间填充数组/tableView,您可以简单地深入查询房间/用户/其中 uid_0 == true。

【讨论】:

  • 嘿,杰,谢谢!我实际上让它的工作方式有点不同,我用底部的工作代码更新了我的问题。如果您认为它更有效/更好,我可以试一试——您似乎非常了解 Firebase,所以我相信您。所以只是为了完全理解之前出了什么问题 - 我之前每个房间都阅读了两次,因为当我添加一个房间时,观察者阅读了那个房间加上所有其他房间?
  • @KingTim .childAdded 读取每个房间,一次一个,因此使用 for eachRoom in roomSnap 遍历房间是没有意义的,因为你只有一个房间。我认为正在发生的是 for 循环试图遍历每个房间中的每个 child node ,因此由于有两个孩子,因此将整个房间添加了两次;房间名称和房间参与者。工作功能部分有很多不需要的代码;像 newRoomRef var 因为它什么都不做。按照我的答案中的设计模式,只需在 Firebase 键分配上添加一个 if let 以防它们不存在。
  • 我试过这个,但实际上发生了崩溃 - 我必须对其进行一些小的更改才能编译,如果你愿意,我可以发布我尝试过的内容,因为我的更改很可能坏了它。但我会在我的工作解决方案中解释额外的代码,也许这最终会成为一种好的方式:
  • 解释前面工作解决方案中的额外代码,例如newRoomRef,即在 Firebase 中将房间添加到另一个名为“房间”的节点。我设置数据库的方式是有一个“房间”节点,它保存应用程序中存在的所有房间,然后每个用户都有自己的“房间”节点,只保存他们所属的房间(这个所以他们只能在表格视图中看到他们的房间,而不是应用程序上每个用户的房间)。
  • 我想确保,因为每个房间都存在于数据库中的两个位置,所以它们都具有相同的 ID。因此,当一个新房间被创建时,它被设置为用户在 firebase 中的“房间”,并使用 childByAutoID 赋予一个唯一的 ID。然后,在observeRooms 中,该房间被读取并设置为 Firebase 中的全局“房间”节点,使用密钥使房间的 ID 相同。 Firebase 中的全局“房间”包含用户房间没有的一件事,那就是消息。希望这是设置数据库的一种合乎逻辑的方式 - 如果您有 5 分钟的时间,我很乐意与您交谈并听取您的意见。
猜你喜欢
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 2021-09-23
  • 2018-09-16
  • 1970-01-01
  • 1970-01-01
  • 2018-10-18
  • 1970-01-01
相关资源
最近更新 更多