通过Firebase查询拉取新评论时,刷新tableView出现评论重复如何解决
评论重复加载问题修复方案
核心问题根因
- 查询逻辑错误:你现有的
CommentsQuery在已有评论时使用queryEnding(atValue: lastTimestamp),查询的是时间早于/等于已有最新评论的历史数据,和刷新需要拉取最新数据的需求完全相反。如果你的评论数组是新评论在前、旧评论在后(插入用insert(at:0)),你取self.comments.last拿到的是最旧的评论,也不符合刷新需要对比最新评论时间的要求。 - 去重逻辑错误:遍历单条评论快照时,你判断的是外层帖子快照的
snapshot.key == lastComment?.id,和单条评论的id完全无关,该判断几乎不会生效,旧评论也会被加入结果集。 - 插入逻辑错误:结果集没有做任何去重校验就直接插入评论数组头部,必然会出现重复内容。
修复后代码
直接替换原有handleRefresh实现即可,原有CommentsQuery可以保留给上拉加载更多旧评论的逻辑使用,不要复用在刷新场景:
@objc func handleRefresh() { let postRef = Database.database().reference().child("posts") let postKey = keyFound // 直接定位到评论节点,减少后续快照取值步骤 let postCommentRef = postRef.child(postKey).child("comments") var queryRef: DatabaseQuery // 假设timestamp数值越大,评论发布时间越新 if self.comments.isEmpty { // 无任何评论时直接拉取最新的20条 queryRef = postCommentRef.queryOrdered(byChild: "timestamp").queryLimited(toLast: 20) } else { // 已有评论时,拉取时间晚于当前最新评论的所有新内容 let latestTimestamp = self.comments.first!.createdAt.timeIntervalSince1970 * 1000 // +1避免拉到和最新评论时间戳完全相同的重复内容 queryRef = postCommentRef.queryOrdered(byChild: "timestamp").queryStarting(atValue: latestTimestamp + 1) } queryRef.observeSingleEvent(of: .value) { [weak self] snapshot in guard let self = self else { return } var tempComments = [Comments]() let allComments = snapshot.children.allObjects as! [DataSnapshot] for commentSnap in allComments { // 可选值安全解包,避免异常数据导致闪退 guard let degree = commentSnap.childSnapshot(forPath: "reply degree").value as? String, let name = commentSnap.childSnapshot(forPath: "reply name").value as? String, let text = commentSnap.childSnapshot(forPath: "reply text").value as? String, let university = commentSnap.childSnapshot(forPath: "reply university").value as? String, let photoURL = commentSnap.childSnapshot(forPath: "reply url").value as? String, let url = URL(string: photoURL), let timestamp = commentSnap.childSnapshot(forPath: "timestamp").value as? Double else { continue } let newComment = Comments(id: commentSnap.key, fullname: name, commentText: text, university: university, degree: degree, photoURL: photoURL, url: url, timestamp: timestamp) // 二次去重:通过评论id判断是否已经存在于数组中,彻底避免重复 if !self.comments.contains(where: { $0.id == newComment.id }) { tempComments.append(newComment) } } // 新评论按时间倒序排列,插入数组头部 tempComments.sort { $0.timestamp > $1.timestamp } self.comments.insert(contentsOf: tempComments, at: 0) self.fetchingMore = false self.refreshControl.endRefreshing() self.tableView.reloadData() } }
额外优化建议
- 给
Comments模型实现Equatable协议,后续判断评论是否重复的逻辑可以直接简化为!self.comments.contains(newComment) - 数据处理完成后可以计算新增的索引位置,调用
tableView.insertRows(at:with:)做局部刷新,比全量reloadData的滑动体验更流畅 - 如果评论量较大,建议给Firebase的
comments节点的timestamp字段加索引,提升查询速度
内容的提问来源于stack exchange,提问作者CodingForFun333
相关产品推荐
相关产品推荐

