【问题标题】:Pulling Data from Firebase into Array将数据从 Firebase 拉入数组
【发布时间】:2017-06-04 17:42:45
【问题描述】:

我最近被告知要以不同的方式构建 Firebase。在我将与特定用户相关的所有内容放在他或她的树下之前。但是有人告诉我将其展平并分别创建他或她的节点,然后在需要时将该节点链接到该用户树。

所以我的树看起来像这样

root
    card
        *card autoID*
            nickname: "foo"
            type: "bar"

    user
        *user uid*
            card
                *card autoID*: true

随着用户在应用程序中的进展,我将向卡片添加更多内容,如果我了解我应该如何构造数据,我会将其添加到卡片节点,因为该卡片已链接到用户.

我的问题是如何从 Firebase 中提取数据然后放入数组或字典中?如果它都在一棵树上,我会做这样的事情

let ref = FIRDatabase.database().reference()

let user = FIRAuth.auth()?.currentUser

let userCard = ref.child((user?.uid)!).child("card")

但既然用户名下的那张卡只是一个参考,那我怎么去卡的真实位置……有昵称和类型的部分?

编辑

因此,在其他 SO 帖子、文档和朋友的帮助下,我的代码 90% 正常工作。

我能做的是

1) 找到用户节点下与用户关联的所有card autoID,并将这些字符串存储到数组中#1

2) 我可以查询节点卡下的所有card autoID,然后找到与数组#1 中的内容匹配的那些并将它们存储在数组#2 中(其余的是忽略)

3) **这是我卡住的地方。如果我在 .observe 内部,那么我可以对数组做我想做的事情,比如打印它的内容。但是,如果我在 .observe 之外调用 print ,我什么也得不到......

这是我的代码

  func pullCurrentUserCardInfo() {

let userCardsRef = ref.child("users").child((user?.uid)!).child("cards")

userCardsRef.observeSingleEvent(of: .value, with: {(snapshot) in

  if let snapDict = snapshot.value as? [String: AnyObject] {
    for each in snapDict {
      self.usersCardRefArray.append(each.key)

      self.count = Int(snapshot.childrenCount)

    }
  }
})

self.ref.child("cards").observe(.value, with: { (snapshot) in
  if snapshot.hasChildren() {

    for item in snapshot.value as! [String: AnyObject] {

      for test in self.usersCardRefArray {

        if test == item.key {
          self.allCurrentUsersCards.append(item.key)
        }
      }
    }
  } else {
    print("no children")
  }

})
}

如果我要在函数内部但在 .observe ....}) 之外说以下内容,那么它不会做任何事情.....

for item in allCurrentUsersCards {
    print(item)
} 

我是否在某处遗漏了一些小东西,或者这与 firebase 有什么关系?

【问题讨论】:

  • 基于问题,将结构非规范化到该级别的理由为零。如果子节点的类型与您的示例中的匹配,请将您的用户数据放在用户节点中。除非特定用例有保证,否则没有理由增加复杂性。 (有一些,但在这个例子中没有)

标签: ios swift firebase firebase-realtime-database


【解决方案1】:

我认为这里存在不必要的复杂性。您不需要为每个用户存储(至少在这个用例中)单独的卡。用户和卡之间存在 1-1 的关系,因此最好将每个用户的卡数据存储在用户节点中。

但是,要直接回答问题,请按以下步骤操作。我们将稍微改变 Firebase 结构:

root
    cards
        *user uid* <- CHANGE
            nickname: "foo"
            type: "bar"

    users
        user uid: true <- CHANGE

由于用户 uid 始终是唯一的并且是为您创建的,因此在与用户合作时利用它们。所以在这种情况下,只需将用户 uid 存储在用户节点中,并将相同的 uid 存储在卡节点中。

创建一个用户类和一个数组来存储它们。这通常可以在 viewController 中完成,例如

class ViewController: UIViewController {
  class UserClass {
    var uid = ""
    var nickname = ""
    var type = ""
  }

  var usersArray = [UserClass]()

然后,制作一个 Firebase 观察者来填充 usersArray,为每个用户获取每张卡片

//iterate over all of the users, get the user and its card data
let usersRef = ref.child("users") 
usersRef.observeSingleEvent(of: .value, with: { snapshot in

    for snap in snapshot.children { //iterate over all users
        let userSnap = snapshot as! FIRDataSnapshot
        let userKey = userSnap.key //the uid of each user

        //now that we have the uid, get it's card data
        let thisUserCardRef = cardsRef.child("uid")
        thisUserCardRef.observeSingleEvent(of: .value, with: { userSnap in
          let userCardSnap = userSnap as! FIRDataSnapshot
          let userCardDict = userCardSnap.value as! [String:AnyObject]
          let nickname = userCardDict["nickname"]
          let type = userCardDict["type"]

          let aUser = UserClass()
          aUser.userKey = userKey
          aUser.nickname = nickname
          aUser.type = type

          self.usersArray.append(aUser)

          //In general, this is where the tableView is refreshed
          // because the user data and card data is valid at this point
          //usersTableView.reload data /
        })
    }
})

这里的关键是要记住 Firebase 是异步的,而且代码比互联网快得多。所以这个高级示例在大多数情况下会失败

func getData() {
   loadDataFromFirebase()
   print("data loaded, do something with it") //<- executes before the prior line completes
}

func loadDataFromFirebase() {
  someRef.observeEvent...
     print("data is now valid inside observe closure")
}

这通常会导致

data loaded, do something with it
data is now valid inside observe closure

这与想要的相反。代码的执行速度比互联网快,因此异步观察关闭将在数据加载后发生...打印。仅在闭包内引用和使用 Firebase 数据,并使用闭包来调整您的应用。

如果您在提供的第一个示例代码中注意到 - 我们仅在数据从 Firebase 返回后处理数据。

另请注意,我们完全消除了查询!与观察事件相比,查询“繁重”,由于我们利用每个用户的 uid,它的路径将是已知的,因此从使用 childByAutoId 创建的节点更改为使用 uid。

【讨论】:

  • 太棒了,我没想到创建一个小的自定义类来保存所有数据,效果更好。
猜你喜欢
  • 2012-04-28
  • 2017-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-19
  • 1970-01-01
相关资源
最近更新 更多