Arrays 通过嵌套的Firebase数据循环填充数组

Arrays 通过嵌套的Firebase数据循环填充数组,arrays,swift,firebase,firebase-realtime-database,Arrays,Swift,Firebase,Firebase Realtime Database,我想通过从一个firebase节点获取值来填充数组,并使用这些值从不同的firebase节点获取信息。我该怎么做 这就是我的firebase数据库的外观: { "MainTree" : { "subTree1" : { "JiRtkpIFLVFNgmNBpMj" : { "PiRterKFLVFNgmFFFtu" : "PiRterKFLVFNgmFFFtu" "TfRterKFLVFNgmFGFre" : "TfRterKFLVFNgmF

我想通过从一个firebase节点获取值来填充数组,并使用这些值从不同的firebase节点获取信息。我该怎么做

这就是我的firebase数据库的外观:

{
  "MainTree" : {
    "subTree1" : {
      "JiRtkpIFLVFNgmNBpMj" : {
        "PiRterKFLVFNgmFFFtu" : "PiRterKFLVFNgmFFFtu"
        "TfRterKFLVFNgmFGFre" : "TfRterKFLVFNgmFGFre",
        "X4RterKFLVFNgmaDFca" : "X4RterKFLVFNgmaDFca"
        }
      },
    "subTree2" : {
        "PiRterKFLVFNgmFFFtu" : {
        "username" : "user1",
        "uid" : "PiRterKFLVFNgmFFFtu"
        },
        "TfRterKFLVFNgmFGFre" : {
        "username" : "user2",
        "uid" : "TfRterKFLVFNgmFGFre"
        },
        "X4RterKFLVFNgmaDFca" : {
        "username" : "user3",
        "uid" : "X4RterKFLVFNgmaDFca"
        }
    }
    }
}
我的职能

func fetchAllInformation(uid: String, completion: @escaping ([UserData]) -> (), withCancel cancel: ((Error) -> ())?) {

    let ref = Database.database().reference().child("MainTree").child("subTree1").child(uid)
    ref.observeSingleEvent(of: .value, with: { (snapshot) in

        if snapshot.exists(){
            guard let dictionaries = snapshot.value as? [String: Any] else {
                completion([])
                return
            }
            var Values = [UserData]()
            let group = DispatchGroup()
            dictionaries.forEach({ (key, value) in
                group.enter()
                let ref = Database.database().reference().child("MainTree").child("subTree2").child(key)
                ref.observeSingleEvent(of: .value, with: { (snapshot) in
                    guard let userDictionary2 = snapshot.value as? [String: Any] else { return }
                    let user = UserData(dictionary: userDictionary2)
                    Values.append(user)
                }) { (err) in
                    print("Failed to fetch all user data from database:", (err))
                    cancel?(err)
                }
            })
            group.notify(queue: .main) {
                print("loop done")
                completion(Values)
            }
        }
    }) { (err) in
        print("Failed to fetch all data from database:", (err))
        cancel?(err)
    }
}
我的呼叫功能:

fetchAllInformation(uid: "JiRtkpIFLVFNgmNBpMj", completion: { (userdata) in
                print("fetched all userdata! : ",userdata)

            }) { (err) in
                print("data fetch failed")
            }
我的数据结构

struct UserData {

let uid: String
let username: String

init(dictionary: [String: Any]) {
    self.uid = dictionary["id"] as? String ?? ""
    self.username = dictionary["username"] as? String ?? ""
    }
}

这可能是对异步代码的误解。现在的问题是数组变空了。

我想你要问的是如何迭代一系列节点,从该节点获取另一个节点的子键,然后基于这些键从另一个节点读取数据

让我从Firebase结构开始,这应该有助于澄清

MainTree
   subTree1
      some_node
         subTree2_0: true
         subTree2_1: true
         subTree2_2: true
   subTree2
      subTree2_0:
          user_name: "Larry"
      subTree2_1:
          user_name: "Moe"
      subTree2_1:
          user_name: "Curly"
这应该与问题的结构相匹配

我们将迭代位于MainTree/subTree1/some_节点中的子节点,以获得我们想要从subTree2读取的节点。我不知道某个_节点相对于其他数据是什么,所以我只是把它叫做。。。某个节点

这段代码的第一部分一次读取subTree1节点的子节点,然后对它们进行迭代以获取每个子键-该子键(例如subTree2_0)对应于subTree2中的子节点

func readMainTree() {
    let mainTreeRef = self.ref.child("MainTree")
    let subTree1Ref = mainTreeRef.child("subTree1")
    let someNodeRef = subTree1Ref.child("some_node")
    someNodeRef.observeSingleEvent(of: .value, with: { snapshot in
        let childNodes = snapshot.children.allObjects as! [DataSnapshot]
        for childSnap in childNodes {
            self.readSubTree2At(node: childSnap.key)
        }
    })
}
在for..循环中,我们获取每个子键,并将其传递给读取子数据(用户名)并打印出来的函数

func readSubTree2At(node: String) {
    let mainTreeRef = self.ref.child("MainTree")
    let subTree2Ref = mainTreeRef.child("subTree2")
    let childRef = subTree2Ref.child(node)
    childRef.observeSingleEvent(of: .value, with: { snapshot in
        let userName = snapshot.childSnapshot(forPath: "user_name").value as? String ?? "No Name"
        print(userName)
    })
}
输出为:

Larry
Mo
Curly
如果你愿意的话,你可以加入一个dispatchGroup。。这里有一个使用DispatchGroup的解决方案

func readMainTreeWithDispatch() {
    let mainTreeRef = self.ref.child("MainTree")
    let subTree1Ref = mainTreeRef.child("subTree1")
    let someNodeRef = subTree1Ref.child("some_node")
    someNodeRef.observeSingleEvent(of: .value, with: { snapshot in
        let childNodes = snapshot.children.allObjects as! [DataSnapshot]

        let myGroup = DispatchGroup()

        for childSnap in childNodes {
            let mainTreeRef = self.ref.child("MainTree")
            let subTree2Ref = mainTreeRef.child("subTree2")
            let childRef = subTree2Ref.child(childSnap.key)

            myGroup.enter()
            childRef.observeSingleEvent(of: .value, with: { snapshot in
                let userName = snapshot.childSnapshot(forPath: "user_name").value as? String ?? "No Name"
                print(userName)
                myGroup.leave()
            })
        }

        myGroup.notify(queue: .main) {
            print("Finished reading all user names.")
        }
    })
}
以及输出

Larry
Mo
Curly
Finished reading all user names.

您遇到了什么问题?@bsod firebaser这里的实时数据库与以往一样重要。我们有人积极致力于维护和改进它。虽然Firestore可能更适合某些场景,但实时数据库更适合其他场景。数据是从Firebase异步加载的。任何需要数据的代码都需要在
observe
的结尾处,或者从那里调用。这包括对
组的调用。通知(队列:.main){
,它现在在加载数据之前触发,因此当
bookmarkedUsers
仍然为空时触发。您可以通过添加一些日志语句来最轻松地进行测试,例如如下所示:@newswitcoder Yes,如果您只输入一次组,您将在调用
notify
(据我所知,我绝对不是派遣小组的专家)。但既然你为每件物品打电话
enter
,如果我读对了以下答案,你也应该为每件物品打电话
leave
:Firebase swag不出售。但是如果你在当地活动中留意Firebase的工作人员,他们通常会带来swag(尽管还没有帽子)。您的工作就在现场!但有一个问题:在所有循环之后,您将如何实现完成侦听器?假设您希望最终结果是数组:
[Larry,Mo,Curly]
。我的方法感觉多余,因为它调用
完成(值)
每一个循环。谢谢你的回答,杰!我很高兴知道调度组的人能在这里提供帮助。:)@newswiftcoder如果你需要它,我会在早上一起得到一个调度组解决方案。它开始了,我的答案就在后面。@newswiftcoder我用一个调度组的例子更新了答案。如果我的答案有帮助,一定要接受它,这样它可以帮助其他人。@FrankvanPuffelen调度组确实有点奇怪。哈哈。更新了答案与工作代码。