SwiftUI + Firestore实现用户首页展示已收藏教练训练内容及收藏时同步训练数据至用户Feed集合的问题
解决方案:修复收藏教练后同步训练内容到用户Feed的函数
我来帮你解决这个问题,先拆解你现有addWorkoutsToFeed函数里的几个核心问题,再给出完整的修复方案:
现有函数的问题分析
- 参数传递错误:在
forEach循环里,你错误地把整个workouts数组传给了document(workouts),正确的做法应该传入单个训练内容的ID(比如workout.id或者文档的documentID) - 缺乏错误处理:没有处理
data(as: Workout.self)解析失败、网络请求出错的情况,调试时很难定位问题 - 低效的单文档写入:循环里每次单独调用
setData,不仅增加网络请求次数,还无法保证操作的原子性 - 不必要的实时监听:收藏教练是一次性操作,用
snapshotListener会持续监听该教练的训练内容变化,若不需要实时同步新增内容,用getDocuments单次获取更合适
修复后的函数实现
下面是修正后的代码,同时加入批量写入和完善的错误处理:
func addWorkoutsToFeed() { guard let trainerUid = trainer.id else { print("Error: Trainer ID is missing") return } guard let currentUid = AuthViewModel.shared.userSession?.uid else { print("Error: Current user is not logged in") return } // 单次获取该教练发布的所有训练内容 COLLECTION_WORKOUTS.whereField("ownerUid", isEqualTo: trainerUid).getDocuments { snapshot, error in if let error = error { print("Failed to fetch trainer's workouts: \(error.localizedDescription)") return } guard let workoutDocuments = snapshot?.documents else { print("No workouts found for this trainer") return } // 初始化Firestore批量写入,提升性能并保证原子性 let batch = Firestore.firestore().batch() let userFeedCollection = COLLECTION_USERS.document(currentUid).collection("user-feed") // 遍历所有训练内容,批量添加到用户Feed for workoutDoc in workoutDocuments { let workoutId = workoutDoc.documentID let feedDocRef = userFeedCollection.document(workoutId) // 写入空数据即可,我们只需要用文档ID标记该训练内容在Feed中 batch.setData([:], forDocument: feedDocRef) } // 提交批量写入操作 batch.commit { error in if let error = error { print("Failed to sync workouts to user feed: \(error.localizedDescription)") } else { print("Successfully added trainer's workouts to user feed") } } } }
关键改进点说明
- 改用
getDocuments:单次获取教练的训练内容,避免不必要的实时监听(如果后续需要实时同步教练的新训练内容,再考虑切换回snapshotListener) - 批量写入操作:通过
batch.commit()一次性提交所有写入请求,减少网络开销,同时保证操作原子性(要么全部成功,要么全部失败) - 正确使用文档ID:用训练内容的
documentID作为user-feed子集合的文档ID,后续查询用户Feed内容时更便捷 - 完善的错误日志:添加打印日志,方便调试过程中快速定位问题
首页获取用户Feed内容的优化
现在可以修改首页的内容获取函数,改为只加载用户user-feed子集合中标记的训练内容:
func fetchUserFeedWorkouts() { guard let currentUid = AuthViewModel.shared.userSession?.uid else { return } // 先获取用户Feed中的所有训练内容ID COLLECTION_USERS.document(currentUid).collection("user-feed").getDocuments { snapshot, error in if let error = error { print("Failed to fetch user feed: \(error.localizedDescription)") return } guard let feedDocuments = snapshot?.documents else { self.workouts = [] return } let workoutIds = feedDocuments.map { $0.documentID } if workoutIds.isEmpty { self.workouts = [] return } // 根据ID批量获取对应的训练内容,并按时间排序 COLLECTION_WORKOUTS.whereField("workout id", in: workoutIds) .order(by: "timestamp", descending: true) .getDocuments { snapshot, error in if let error = error { print("Failed to fetch feed workouts: \(error.localizedDescription)") return } guard let documents = snapshot?.documents else { self.workouts = [] return } self.workouts = documents.compactMap { try? $0.data(as: Workout.self) } } } }
补充:取消收藏时的清理操作
为了保证数据一致性,当用户取消收藏教练时,记得从user-feed子集合中删除该教练的所有训练内容:
func removeWorkoutsFromFeed() { guard let trainerUid = trainer.id else { return } guard let currentUid = AuthViewModel.shared.userSession?.uid else { return } COLLECTION_WORKOUTS.whereField("ownerUid", isEqualTo: trainerUid).getDocuments { snapshot, error in if let error = error { print("Failed to fetch trainer workouts: \(error.localizedDescription)") return } guard let workoutDocuments = snapshot?.documents else { return } let batch = Firestore.firestore().batch() let userFeedCollection = COLLECTION_USERS.document(currentUid).collection("user-feed") for workoutDoc in workoutDocuments { let feedDocRef = userFeedCollection.document(workoutDoc.documentID) batch.deleteDocument(feedDocRef) } batch.commit { error in if let error = error { print("Failed to remove workouts from feed: \(error.localizedDescription)") } else { print("Successfully removed trainer's workouts from user feed") } } } }
内容的提问来源于stack exchange,提问作者Jake12345
相关产品推荐
相关产品推荐

