我的主要问题是如何消除闪烁,但是我也只想知道我是否正确,最有效地使用了非规范化的Firebase数据。我的方法接近正确吗?
因此,我正努力尝试使用已规范化的数据正确显示Firebase数据库中的数据。我有帖子,然后有与每个帖子相关的评论。每次有人打开某个帖子的注释部分时,通过从一个视图控制器中筛选到一个新的视图控制器,它将获取该帖子的唯一键(postKey),然后扫描与postCommentGroup中包含的postKey关联的注释组。注释组是postCommentGroup中每个postKey的子级,只是commentKey作为键,“true”作为值,指示哪些注释与哪些帖子相关。注释位于一个完全不同的分支中,因为我认为Firebase文档建议这样做。
我基本上有3层嵌套的观察者。
为了清楚起见,我在表视图中使用dequeuereusablecells回收单元,并且我也有一个可能会干扰事物的基本懒惰加载/图像缓存机制,但是在其他较不复杂的表视图中我具有相同的机制,因此不要以为那是问题。
由于缺乏知识,除了经历此循环外,我不知道该如何显示数据。我认为这个周期可能导致闪烁,但是我不知道如何使它加载数据。我尝试了多种其他方式来执行此操作,例如使用查询,但是我从未能够使其正常工作。
附带说明一下,我尝试加快了如何查询数据的速度(我认为这可能会对我有所帮助),但是对Swift的语法进行了更新,同时对Firebase进行了更新,制作了前面的示例有点难以理解。
另外,在Firebase网站或Github上的任何Firebase文档中,我都找不到很好的最近的示例,它们以某种复杂的方式正确地使用了非规范化数据。是否有人知道使用Swift 3.0和Firebase(最新版本-不是旧版本)使用非规范化数据的好参考资料,无论它是GitHub上的项目还是博客,还是仅仅是大多数项目的集合关于stackoverflow有用的帖子?
这是firebase数据结构:
"comments" : {
"-KaEl8IRyIxRbYlGqyXC" : {
"description" : "1",
"likes" : 1,
"postID" : "-KaEfosaXYQzvPX5WggB",
"profileImageUrl" : "https://firebasestorage.googleapis.com",
"timePosted" : 1484175742269,
"userID" : "9yhij9cBhJTmRTexsRfKRrnmDRQ2",
"username" : "HouseOfPaine"
}
},
"postCommentGroup" : {
"-KaEfosaXYQzvPX5WggB" : {
"-KaEl8IRyIxRbYlGqyXC" : true,
"-KaEl9HiPCmInE0aJH_f" : true,
"-KaF817rRpAd2zSCeQ-M" : true
},
"-KaF9ZxAekTEBtFgdB_5" : {
"-KaFEcXsSJyJwvlW1w2u" : true
},
"-KaJyENJFkYxCffctymL" : {
"-KaQYa0d08D7ZBirz5B4" : true
}
},
"posts" : {
"-KaEfosaXYQzvPX5WggB" : {
"caption" : "Test",
"comments" : 11,
"imageUrl" : "https://firebasestorage.googleapis.com/",
"likes" : 0,
"profileImageUrl" : "https://firebasestorage.googleapis.com/",
"timePosted" : 1484174347995,
"title" : "test",
"user" : "17lIDKNx6LgzQmaeQ2ING582zi43",
"username" : "Freedom"
}
},
这是我的代码:
func commentGroupObserver() {
DataService.ds.REF_POST_COMMENT_GROUP.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.value != nil {
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] , snapshots.count > 0 {
self.comments = []
for snap in snapshots {
if let tempVarPostKeyForCommentGroup = snap.key as String? {
if tempVarPostKeyForCommentGroup == self.post.postKey {
self.postKeyForCommentGroup = tempVarPostKeyForCommentGroup
self.commentObservers()
} else {
}
} else {
}
}
}
} else {
print("error")
}
})
}
func commentObservers() {
if postKeyForCommentGroup != nil {
constantHandle = DataService.ds.REF_POST_COMMENT_GROUP.child(postKeyForCommentGroup).observe(.value, with: { (snapshot) in
if snapshot.value != nil {
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot], snapshots.count > 0
{
self.comments = []
for snap in snapshots {
if let theCommentIDForEachComment = snap.key as String? {
DataService.ds.REF_COMMENTS.child(theCommentIDForEachComment).queryOrdered(byChild: "timePosted").observeSingleEvent(of: .value, with: { (snapshots) in
if let commentDict = snapshots.value as? Dictionary<String, AnyObject> {
let key = snapshots.key
let comment = Comment(commentKey: key, dictionary: commentDict)
self.comments.insert(comment, at: 0)
}
self.tableView.reloadData()
})
}
}
}
} else {
}
})
} else {
}
}
更新:
我在上一个stackoverflow帖子中指出了如何使用查询和委托模式:
getting data out of a closure that retrieves data from firebase
但是我不知道我是否正确使用了委托模式。
通过使用查询已简化了代码,但仍然闪烁。也许我没有正确使用委托模式?
func commentGroupObserver() {
DataService.ds.REF_POST_COMMENT_GROUP.queryOrderedByKey().queryStarting(atValue: post.postKey).queryEnding(atValue: post.postKey).observeSingleEvent(of: .value, with: { (snapshot) in
self.postKeyForCommentGroup = self.post.postKey
self.commentObservers()
})
}
func commentObservers() {
if postKeyForCommentGroup != nil {
constantHandle = DataService.ds.REF_POST_COMMENT_GROUP.child(postKeyForCommentGroup).observe(.value, with: { (snapshot) in
if snapshot.value != nil {
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot]
{
self.comments = []
for snap in snapshots {
if let theCommentIDForEachComment = snap.key as String? {
DataService.ds.REF_COMMENTS.child(theCommentIDForEachComment).queryOrdered(byChild: "timePosted").observe(.value, with: { (snapshots) in
if let commentDict = snapshots.value as? Dictionary<String, AnyObject> {
let key = snapshots.key
let comment = Comment(commentKey: key, dictionary: commentDict)
self.comments.insert(comment, at: 0)
}
self.didFetchData(comments: self.comments)
})
}
}
}
} else {
}
})
} else {
}
}
func didFetchData(comments data:[Comment]){
self.tableView.reloadData()
}
}
和协议
protocol MyDelegate{
func didFetchData(comments:[Comment]) }
我端解决该问题的代码:
在杰伊的建议下,我消除了不必要的postCommentGroup,只是在评论下查询了评论所属的帖子的UID:
func commentObservers() {
let queryRef = DataService.ds.REF_COMMENTS.queryOrdered(byChild: "postID").queryEqual(toValue: self.post.postKey)
queryRef.observe(.value, with: { snapshot in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
if let commentDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let comment = Comment(commentKey: key, dictionary: commentDict)
self.comments.insert(comment, at: 0)
}
}
}
self.tableView.reloadData()
})
}
最佳答案
您的方法可能需要通过简化进行调整。我想提供所有的螺母和螺栓,所以它有点冗长,并且可以简化。
虽然非正规化是正常的,但这不是必需的,并且在某些情况下会增加额外的复杂性。您的结构postCommentGroup中的“层”似乎是不需要的。
看起来您有一个包含帖子的视图控制器,以及一个第二个视图控制器,当用户在第一个控制器上点击帖子时,该视图控制器显示评论。
您实际上只需要一个posts节点和一个comment节点
posts
post_id_0
title: "my post title"
caption: "some caption"
uid: "uid_0"
post_id_1
title: "another post title
caption: "another caption
uid: "uid_0"
和引用该帖子的评论节点
comments
comment_0
post_id: "post_id_0"
uid: "uid_1"
likes: "10"
comment_1
post_id: "post_id_0"
uid: "uid_1"
likes: "7"
comment_2
post_id: "post_id_1"
uid: "uid_1"
likes: "2"
设置:
class CommentClass {
var commentKey = ""
var comment = ""
var likes = ""
}
var postsArray = [ [String: [String:AnyObject] ] ]()
var commentsArray = [CommentClass]()
加载所有帖子的代码:
let postsRef = ref.child("posts")
postsRef.observeSingleEvent(of: .value, with: { snapshot in
for snap in snapshot.children {
let postSnap = snap as! FIRDataSnapshot
let postKey = postSnap.key //the key of each post
let postDict = postSnap.value as! [String:AnyObject] //post child data
let d = [postKey: postDict]
self.postsArray.append(d)
}
//postsTableView.reloadData
print(self.postsArray) //just to show they are loaded
})
然后,当用户点击帖子时,加载并显示评论。
self.commentsArray = [] //start with a fresh array since we tapped a post
//placeholder, this will be the post id of the tapped post
let postKey = "post_id_0"
let commentsRef = ref.child("comments")
let queryRef = commentsRef.queryOrdered(byChild: "post_id")
.queryEqual(toValue: postKey)
//get all of the comments tied to this post
queryRef.observeSingleEvent(of: .value, with: { snapshot in
for snap in snapshot.children {
let commentSnap = snap as! FIRDataSnapshot
let commentKey = commentSnap.key //the key of each comment
//the child data in each comment
let commentDict = commentSnap.value as! [String:AnyObject]
let comment = commentDict["comment"] as! String
let likes = commentDict["likes"] as! String
let c = CommentClass()
c.commentKey = commentKey
c.comment = comment
c.likes = likes
self.commentsArray.append(c)
}
//commentsTableView.reload data
//just some code to show the posts are loaded
print("post: \(postKey)")
for aComment in self.commentsArray {
let comment = aComment.comment
print(" comment: \(comment)")
}
})
和结果输出
post: post_id_0
comment: I like post_id_0
comment: post_id_0 is the best evah
上面的代码已经过测试,没有闪烁。显然,由于要加载一些图像等,因此需要针对您的用例进行调整,但是以上内容可以解决问题并且更易于维护。