You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firestore实时获取预订请求时TableView重复显示问题求助

解决Firestore实时监听导致TableView重复添加请求的问题

看起来你遇到的核心问题是异步请求的时序冲突——当Firestore的快照监听触发时,你清空了数组,但之前发起的异步请求(获取newrequest集合和用户信息)还在后台执行,这些旧请求完成后会把数据再次添加到新的数组里,导致重复显示。另外,循环中使用的bookedDateIndex在异步回调中会失效,也可能引发数据错位或重复。

问题拆解

  1. 未等待异步请求完成就重置数组:每次快照更新时,你立刻清空newRequestArray和sectionHeader,但之前的getDocuments/getDocument请求还在运行,它们的回调会把旧数据塞回新数组。
  2. 索引依赖问题:bookedDateIndex是循环时的临时索引,异步回调触发时,newRequestArray可能已经被重新构建,索引对应关系失效。
  3. 频繁刷新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()
        }
    }
}

关键改进点

  1. DispatchGroup管理异步任务:确保所有嵌套的异步请求都完成后,再统一更新数据和UI,避免旧请求干扰新数据。
  2. 临时字典存储分组数据:不用依赖循环时的索引,直接按日期作为key存储,避免索引错位问题。
  3. 统一刷新UI:所有数据准备好后只调用一次reloadData,保证UI状态一致。
  4. 日期排序:可选添加日期排序逻辑,让section按时间顺序展示。

另外,建议你优化isPastDate方法,确保日期字符串的解析逻辑和Firestore里的格式一致,避免误判有效日期。

内容的提问来源于stack exchange,提问作者Deependra Dhakal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:44:32