Firestore实时获取预订请求时TableView重复显示问题求助
解决Firestore实时监听导致TableView重复添加请求的问题
看起来你遇到的核心问题是异步请求的时序冲突——当Firestore的快照监听触发时,你清空了数组,但之前发起的异步请求(获取newrequest集合和用户信息)还在后台执行,这些旧请求完成后会把数据再次添加到新的数组里,导致重复显示。另外,循环中使用的bookedDateIndex在异步回调中会失效,也可能引发数据错位或重复。
问题拆解
- 未等待异步请求完成就重置数组:每次快照更新时,你立刻清空
newRequestArray和sectionHeader,但之前的getDocuments/getDocument请求还在运行,它们的回调会把旧数据塞回新数组。 - 索引依赖问题:
bookedDateIndex是循环时的临时索引,异步回调触发时,newRequestArray可能已经被重新构建,索引对应关系失效。 - 频繁刷新UI:在循环里每次添加数据都调用
reloadData,不仅效率低,还会导致UI多次更新,加重重复显示的问题。
修复方案
我们可以用DispatchGroup来等待所有异步请求完成,统一更新数据和UI;同时用字典临时存储按日期分组的数据,避免索引错位。
func loadAllRequestFromFirebasee() { // 定义一个组来管理所有异步任务 let dispatchGroup = DispatchGroup() // 临时字典:key是日期,value是该日期下的所有请求 var tempRequestsDict = [String: [NewRequest]]() db.collection("futsal_list").document(currentUser!.uid).collection("book_info").addSnapshotListener { (querySnapshot, error) in guard let snapshot = querySnapshot else { print("Error fetching snapshots: \(error!)") return } // 清空临时存储,准备接收新数据 tempRequestsDict.removeAll() // 筛选有效日期(今日及未来) let validDates = snapshot.documents .map { $0.documentID } .filter { !self.isPastDate(date: $0) } for bookedDate in validDates { dispatchGroup.enter() // 获取该日期下的所有newrequest文档 self.db.collection("futsal_list") .document(self.currentUser!.uid) .collection("book_info") .document(bookedDate) .collection("newrequest") .getDocuments(completion: { (uidSnapshots, error) in defer { dispatchGroup.leave() } // 不管成功失败都标记任务完成 guard let uidSnapshot = uidSnapshots else { print("Error fetching newrequest snapshots: \(error!)") return } // 为当前日期初始化空数组 tempRequestsDict[bookedDate] = [] for userDoc in uidSnapshot.documents { dispatchGroup.enter() let userUid = userDoc.documentID let bookedTimes = userDoc.data()["time"] as! [String: Any] // 获取用户信息 self.db.collection("users_list") .document(userUid) .getDocument(completion: { (userDoc, error) in defer { dispatchGroup.leave() } guard let userDoc = userDoc, userDoc.exists else { print("User document not found for uid: \(userUid)") return } let userName = userDoc.data()?["user_full_name"] as? String ?? "" let userPhone = userDoc.data()?["user_phone_number"] as? String ?? "" let userProfilePic = userDoc.data()?["user_profile_image"] as? String ?? "" // 遍历所有预订时间,创建NewRequest对象 for time in bookedTimes.keys { let newRequest = NewRequest( userUid: userUid, userName: userName, bookDate: bookedDate, bookTime: time, userPhoneNumber: userPhone, userProfilePicture: userProfilePic ) tempRequestsDict[bookedDate]?.append(newRequest) } }) } }) } // 所有异步任务完成后,更新UI dispatchGroup.notify(queue: .main) { // 把字典转换成有序的数组和sectionHeader(按日期排序,可选) self.sectionHeader = tempRequestsDict.keys.sorted(by: { date1, date2 in // 这里需要实现日期字符串的比较逻辑,比如转换成Date后比较 // 示例:假设日期格式是"MMM d, yyyy",比如"May 9, 2019" let dateFormatter = DateFormatter() dateFormatter.dateFormat = "MMM d, yyyy" guard let d1 = dateFormatter.date(from: date1), let d2 = dateFormatter.date(from: date2) else { return false } return d1 < d2 }) // 按sectionHeader的顺序填充newRequestArray self.newRequestArray = self.sectionHeader.compactMap { tempRequestsDict[$0] } // 只刷新一次UI self.newBookRequestTableView.reloadData() } } }
关键改进点
- DispatchGroup管理异步任务:确保所有嵌套的异步请求都完成后,再统一更新数据和UI,避免旧请求干扰新数据。
- 临时字典存储分组数据:不用依赖循环时的索引,直接按日期作为key存储,避免索引错位问题。
- 统一刷新UI:所有数据准备好后只调用一次
reloadData,保证UI状态一致。 - 日期排序:可选添加日期排序逻辑,让section按时间顺序展示。
另外,建议你优化isPastDate方法,确保日期字符串的解析逻辑和Firestore里的格式一致,避免误判有效日期。
内容的提问来源于stack exchange,提问作者Deependra Dhakal
相关产品推荐
相关产品推荐

