从 Firebase 检索数据并在另一个 Firebase 数据库参考中使用它
Posted
技术标签:
【中文标题】从 Firebase 检索数据并在另一个 Firebase 数据库参考中使用它【英文标题】:Retrieving data from Firebase and using it in another Firebase database reference 【发布时间】:2017-02-28 17:39:27 【问题描述】:我的数据结构如下:
restaurant_owners
|
|owner_id (a unique ID)
|
|restaurant_name
|email
restaurant_menus
|
|restaurant_name
|
|dish_type (drinks, appetizer, etc...)
|
|dish_id (a unique ID)
|
|name
|
|price
该应用程序的想法基本上是允许“restaurant_owners”登录并管理他们各自餐厅的菜单。但是我在使用以下代码时遇到问题:(请注意,在 viewDidLoad 中调用了 fetchDish 函数)
func fetchDish()
var restaurantName: String?
let uid = FIRAuth.auth()?.currentUser?.uid
//first time referencing database
FIRDatabase.database().reference().child("owners").child(uid!).observeSingleEvent(of: .value, with: (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]
DispatchQueue.main.async
restaurantName = dictionary["name"] as? String
print(restaurantName!)
)
//second time referencing database
FIRDatabase.database().reference().child("restaurants").child(restaurantName!).child("appetizer").observe(.childAdded, with: (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]
let dish = Dish()
dish.setValuesForKeys(dictionary)
self.dishes.append(dish)
DispatchQueue.main.async
self.tableview.reloadData()
, withCancel: nil)
我要做的是检索当前登录用户的餐厅名称并将其存储在变量“restaurantName”中。然后,当我第二次引用数据库时,我可以在 .child 中使用这个变量(例如:.child(restaurantName))。
但是,当我运行它时,我收到一条错误消息,指出 restaurantName(在数据库引用中)的值为零。我尝试放置一些断点,似乎第二个数据库引用的第一行是在第一个数据库引用“内”的任何内容之前操作的,所以基本上 restaurantName 在存储任何值之前被调用。
为什么会这样?我该如何解决这个问题?另外,如果我做错了,实现这一目标的最佳做法是什么?
NoSQL 对我来说非常陌生,我完全不知道应该如何设计我的数据结构。提前感谢您的帮助,如果您需要任何其他信息,请告诉我。
更新:
通过将我的数据结构更改为 Jay 的建议,问题得到了解决。以下代码对我有用:(稍微修改了杰的代码)
func fetchOwner()
let uid = FIRAuth.auth()?.currentUser?.uid
let ownersRef = FIRDatabase.database().reference().child("owners")
ownersRef.child(uid!).observeSingleEvent(of: .value, with: snapshot in
if let dict = snapshot.value as? [String: AnyObject]
let restaurantID = dict["restaurantID"] as! String
self.fetchRestaurant(restaurantID: restaurantID)
, withCancel: nil)
func fetchRestaurant(restaurantID: String)
let restaurantsRef = FIRDatabase.database().reference().child("restaurants")
restaurantsRef.child(restaurantID).child("menu").observe(.childAdded, with: snapshot in
if let dictionary = snapshot.value as? [String: AnyObject]
let dish = Dish()
dish.setValuesForKeys(dictionary)
self.dishes.append(dish)
DispatchQueue.main.async
self.tableView.reloadData()
, withCancel: nil)
【问题讨论】:
请删除 DispatchQueue.main.async。不需要。 使用 DispatchQueue.main.async 是否有任何特定的负面影响?当我将 self.tableview.reloadData() 放入其中时,它显着加快了表格加载信息的时间。 在这个应用程序中,它不应该对性能产生任何明显的影响,因为在从服务器接收到 Firebase 数据并处理闭包中的代码之前,不会刷新 tableView。在这种情况下,您将在主串行队列上抛出一个异步任务,该任务将任务按顺序排列在其前面的任何内容之后。即不需要,因为 Firebase 函数已经是异步的。 【参考方案1】:有几点:
Firebase 是异步的,您必须在代码中考虑到这一点。正如帖子中所述,第二个 Firebase 函数可能会在第一个 Firebase 函数成功返回数据之前执行,即第二次调用发生时 restaurantName 可能为 nil。
您应该嵌套调用(在此用例中)以确保数据在使用之前有效。像这样..并继续阅读
let ownersRef = rootRef.child("owners")
let restaurantRef = rootRef.child("restaurants")
func viewDidLoad()
fetchOwner("owner uid")
func fetchOwner(ownerUid: String)
var restaurantName: String?
let uid = FIRAuth.auth()?.currentUser?.uid
ownserRef.child(ownerUid).observeSingleEvent(of: .value, with: snapshot in
if let dict = snapshot.value as? [String: AnyObject]
restaurantId = dict["restaurant_id"] as? String
fetchRestaurant(restaurantId)
)
func fetchRestaurant(restaurantId: String)
restaurantRef.child(restaurantId).observeSingleEvent(of: .value, with: snapshot in
if let dict = snapshot.value as? [String: AnyObject]
let restaurantName = dict["name"] as! String
let menuDict = dict["menu"] as! [String:Any]
self.dataSourceArray.append(menuDict)
menuTableView.reloadData()
最重要的是,将键名与其包含的数据分离几乎总是最佳做法。在本例中,您使用餐厅名称作为键。如果餐厅名称更改或更新怎么办?您不能更改密钥!唯一的选择是删除它并重新编写它......并且......数据库中引用它的每个节点。
更好的选择是利用 childByAutoId 并让 Firebase 为您命名节点并保留具有相关数据的子节点。
restaurants
-Yii9sjs9s9k9ksd
name: "Bobs Big Burger Barn"
owner: -Y88jsjjdooijisad
menu:
-y8u8jh8jajsd
name: "Belly Buster Burger"
type: "burger"
price: "$1M"
-j8u89joskoko
name: "Black and Blue Burger"
type: "burger"
price: "$9.95"
如您所见,我利用 childByAutoId 为这家餐厅以及菜单上的项目创建了密钥。我还在所有者节点中引用了所有者的uid。
在这种情况下,如果 Belly Buster Burger 更改为 Waist Slimming Burger,我们可以进行一次更改并完成,并且任何引用它的内容也会更新。与所有者相同,如果所有者更改,则只需更改所有者 uid。
如果餐厅名称更改为 Tony's Taco Tavern,只需更改子节点即可。
希望有帮助!
编辑:回复评论:
获取由 .childByAutoId() 立即创建的字符串(即键:值对的“键”)
let testRef = ref.child("test").childByAutoId()
let key = testRef.key
print(key)
【讨论】:
非常感谢杰伊的帮助。真的很感激。 后续问题:有没有办法立即存储由 .childByAutoId 生成的字符串,以便我可以在以下参考中使用它?还是我必须再次执行此嵌套函数?我知道一个快速的解决方法是使用 NSUUID().uuidString,但它似乎不够优雅。 @JustTro11 确定!超级简单!我在答案的末尾添加了一些代码。 @Jay 你能帮我解答一下吗?我需要从一个已经链接到一个 firebase 项目的项目中访问另一个 firebase 数据库,我不知道从这里去哪里,或者我是否认为必须为 2 个 ios 应用程序建立数据库是一个好主意。 ***.com/questions/44866220.以上是关于从 Firebase 检索数据并在另一个 Firebase 数据库参考中使用它的主要内容,如果未能解决你的问题,请参考以下文章
从 firebase 数据库中检索数据并在 Objective-C 中的 UITable 中显示
Swift:从 Firebase 检索数据并在表格视图中显示
Android 从片段中检索 Json 并在另一个活动中使用