Firebase填充数组重复行:聊天App会话列表重复问题求助
问题根源与修复方案
核心问题
- 重复注册监听器:每次调用
fetchRooms都会新增Firestore快照监听器,同一条消息变更会被多个监听器处理,重复添加到rooms数组。 - 未处理变更类型:直接将所有
documentChanges追加到数组,没有针对会话更新(比如用最新消息替换旧消息)做去重或替换逻辑。 - 异步加载导致重复追加:
UserService.fetchUser是异步操作,多次触发时会重复创建Room实例并追加,未检查数组中是否已存在该会话。
修复步骤
1. 移除重复监听器
不要每次调用fetchRooms都新增监听器,改为初始化时注册一次,或在注册前移除旧监听器。可以保存监听器引用,在页面销毁时清理:
private var roomsListener: ListenerRegistration? func setupRoomsListener() { // 先移除旧监听器,避免重复监听 roomsListener?.remove() let query = COLLECTION_ROOMS.order(by: "timestamp", descending: false).limit(toLast: 10) roomsListener = query.addSnapshotListener { [weak self] snapshot, err in guard let self = self else { return } // 后续变更处理逻辑 } }
2. 按会话唯一标识去重,更新而非追加
每个会话应有唯一标识(比如用户UID或房间ID),处理变更时先检查数组中是否已有该会话,存在则更新最新消息,不存在则添加:
snapshot?.documentChanges.forEach({ [weak self] change in guard let self = self else { return } let dictionary = change.document.data() let message = RoomMessage(dictionary: dictionary) let ownerID = dictionary["ownerID"] as? String ?? "" UserService.fetchUser(withUid: ownerID) { user in let conversation = Room(user: user, recentMessage: message) // 按用户UID判断会话是否已存在 if let index = self.rooms.firstIndex(where: { $0.user.uid == user.uid }) { // 存在则更新最新消息 self.rooms[index].recentMessage = message } else { // 不存在则添加新会话 self.rooms.append(conversation) } // 排序后统一刷新UI,避免频繁刷新 self.rooms.sort(by: { $0.recentMessage.timeStamp.compare($1.recentMessage.timeStamp) == .orderedDescending }) self.tableView.reloadData() } })
3. 拆分分页与实时监听逻辑
分页加载历史数据用getDocuments(不需要实时监听),实时监听只用于最新消息的更新:
@objc func fetchRooms() { tableView.refreshControl?.beginRefreshing() var query: Query! if rooms.isEmpty { query = COLLECTION_ROOMS.order(by: "timestamp", descending: false).limit(toLast: 10) print("First 10 room loaded") } else { query = COLLECTION_ROOMS.order(by: "timestamp", descending: false).end(beforeDocument: lastDocumentSnapshot).limit(toLast: 10) print("Next 10 room loaded") currentPage += 1 } // 用getDocuments加载历史分页数据,而非添加监听器 query.getDocuments { [weak self] snapshot, err in guard let self = self else { return } self.tableView.refreshControl?.endRefreshing() if let err = err { print("\(err.localizedDescription)") return } guard let snapshot = snapshot, !snapshot.isEmpty else { return } self.lastDocumentSnapshot = snapshot.documents.first for document in snapshot.documents { let dictionary = document.data() let message = RoomMessage(dictionary: dictionary) let ownerID = dictionary["ownerID"] as? String ?? "" UserService.fetchUser(withUid: ownerID) { user in let conversation = Room(user: user, recentMessage: message) // 去重检查 if !self.rooms.contains(where: { $0.user.uid == user.uid }) { self.rooms.append(conversation) self.rooms.sort(by: { $0.recentMessage.timeStamp.compare($1.recentMessage.timeStamp) == .orderedDescending }) self.tableView.reloadData() } } } } }
4. 优化刷新逻辑
刷新时重置分页状态,清空数组后重新拉取数据:
@objc func refresh() { self.rooms.removeAll() self.currentPage = 1 self.lastDocumentSnapshot = nil self.setupRoomsListener() self.tableView.reloadData() }
内容的提问来源于stack exchange,提问作者Unknown
相关产品推荐
相关产品推荐

