最近我告诉我以不同的方式构建我的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匹配的那些 card autoID 并将它们存储在数组#2中(其余的都是忽略)
3)**这是我被困的地方。如果我在.observe内部,那么我可以用数组做我想要的事情,比如打印它的内容。但是,如果我在.observe之外打印,我什么也得不到......
这是我的代码
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有关?
答案 0 :(得分:2)
我认为这里有一个不必要的复杂程度。您不需要为每个用户存储(在此用例中至少)一张单独的卡。用户和卡之间存在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
与想要的相反。代码执行速度比Internet快,因此在打印数据加载后,将发生异步观察闭包。仅在闭包内引用和处理firebase数据,并使用闭包来调整应用程序。
如果您在提供的第一个示例代码中注意到 - 我们只会在从Firebase返回数据后使用这些数据。
另请注意,我们完全取消了查询!与观察事件相比,查询“很重”,因为我们正在利用每个用户的uid,它的路径将是已知的,因此从使用childByAutoId创建的节点更改为使用uid。