My main question is how to get rid of the flicker, but I also just want to know if I am working with denormalized Firebase data correctly, and most efficiently. Is my approach anywhere near being correct?
So I am struggling with trying to properly display the data from a firebase database with data that's been denormalized. I have posts, and then comments associated with each post. Every time somebody opens up the comments section of a post, by segueing from a viewcontroller to a new viewcontroller, it grabs the unique key for the post (postKey), and then scans the group of comments associated with the postKey contained in the postCommentGroup. The group of comments, which are children of each postKey in the postCommentGroup are just the commentKey as key and "true" as the value, which indicates which comments are associated with which posts. The comments are in an entirely different branch as that is what I think the Firebase documentation suggests one should do.
I essentially have 3 layers of nested observers.
For the sake of clarity I'm recycling the cells with dequeuereusablecells in a tableview, and I also have a rudimentary lazy load/image caching mechanism that might be interfering with things too, but I have the same mechanism on other less complicated tableviews so I don't think that's the problem.
Due to my lack of knowledge I don't know how else to display the data other than going through this cycle. I think this cycle, may be causing the flicker, but I don't know how else to make it load the data. I have tried various other ways of doing it, such as using a query, but I've never been able to get it to work.
As a side note, I've tried to get up to speed on how to query data (which I assume might help me), but there's been an update to the syntax of Swift, and also an update to Firebase, making the previous examples a little difficult to follow.
Also, I just can't find good, recent examples of properly using denormalized data in a somewhat complex way in any of the Firebase documentation, either on the Firebase site or on Github. Does anyone know of good reference material to look at with regard to working with denormalized data using Swift 3.0 and Firebase (newest version - not the legacy version), whether it's a project on GitHub, or a blog, or just a collection of the most useful posts on stackoverflow?
Here is the firebase data structure:
"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"
}
},
Here is my code:
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 {
}
}
UPDATE:
I figured out how to use queries and a delegate pattern outlined in a previous stackoverflow post:
getting data out of a closure that retrieves data from firebase
But I don't know if I am using the delegate pattern correctly.
The code has been simplified by using the query, but it is still flickering. Maybe I am not using the delegate pattern correctly?
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()
}
}
And the protocol
protocol MyDelegate{
func didFetchData(comments:[Comment]) }
The code on my end which solved it:
Upon Jay's suggestion I eliminated the unnecessary postCommentGroup and just queried the UID of the post the comment belongs to under the comment:
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()
})
}