Firestore拉取userRecipes子集合文档时出现重复打印问题
问题现象
- 拉取Firestore中用户路径下
userRecipes子集合的所有文档,子集合内实际仅存储2份文档,但控制台多次重复打印文档内容。
异常打印输出如下:
["Flounder": {
createdAt = "<FIRTimestamp: seconds=1656102838 nanoseconds=66710000>";
directions = (
Example
);
ingredientItem = {
Example = Example;
};
recipeImage = "";
recipePrepTime = "";
recipeTitle = Flounder; }]] [["eggs": {
createdAt = "<FIRTimestamp: seconds=1656102790 nanoseconds=482897000>";
directions = (
);
ingredientItem = {
"1 cup" = Flour;
};
recipeImage = "";
recipePrepTime = "20 Mins";
recipeTitle = eggs; }]] [["eggs": {
createdAt = "<FIRTimestamp: seconds=1656102790 nanoseconds=482897000>";
directions = (
);
ingredientItem = {
"1 cup" = Flour;
};
recipeImage = "";
recipePrepTime = "20 Mins";
recipeTitle = eggs; }], ["Flounder": {
createdAt = "<FIRTimestamp: seconds=1656102838 nanoseconds=66710000>";
directions = (
Example
);
ingredientItem = {
Example = Example;
};
recipeImage = "";
recipePrepTime = "";
recipeTitle = Flounder; }]]
运行截图参考:
原始实现代码
class RecipeLogic: ObservableObject { @Published var recipes = [RecipeItems]() @Published var recipeList = [] init(){ grabRecipes() } private func grabRecipes(){ guard let uid = FirebaseManager.shared.auth.currentUser?.uid else { return } FirebaseManager.shared.firestore .collection("users") .document(uid) .collection("userRecipes") .getDocuments() { (snapshot, err) in if let err = err { print("error") } else{ for recipe in snapshot!.documents { self.recipeList.append(recipe.data()) print(self.recipeList) } } } } } struct RecipeItems: Codable, Hashable{ var recipeTitle, recipePrepTime, recipeImage, createdAt: String var directions, ingredientItem: [String] } struct RecipeModel:Codable, Identifiable { var id: String = UUID().uuidString var recipes: [RecipeItems] }
问题根因
- 打印位置错误:
print(self.recipeList)写在遍历快照的for循环内部,每追加1条文档就会打印一次当前数组全量内容。子集合共2条文档时,第一次循环追加第1条文档后打印长度为1的数组,第二次循环追加第2条文档后打印长度为2的数组,天然会产生2次输出,而非仅输出一次最终全量结果。 - 旧数据未清空:
grabRecipes()执行前未清空recipeList的原有缓存数据。如果RecipeLogic实例被多次初始化(例如SwiftUI视图重建触发Observable对象重新创建),每次拉取数据都会在原有数组基础上重复追加2条文档,数组长度会持续累加,重复打印的内容会越来越多。 - 数组类型缺失:
@Published var recipeList = []未指定明确类型,Swift类型推断会将其识别为隐式的[Any]类型,追加数据时容易出现非预期的缓存残留问题。
修复方案
- 为
recipeList指定明确的类型[[String: Any]],匹配Firestore文档返回的数据结构 - 拉取新数据前调用
removeAll()清空数组旧缓存,避免历史数据残留 - 将打印逻辑移到
for循环外部,等所有文档遍历追加完成后,仅打印一次全量结果 - 避免强制解包
snapshot!,用可选绑定做安全校验
修复后的核心代码如下:
class RecipeLogic: ObservableObject { @Published var recipes = [RecipeItems]() // 明确指定数组类型 @Published var recipeList = [[String: Any]]() init(){ grabRecipes() } private func grabRecipes(){ guard let uid = FirebaseManager.shared.auth.currentUser?.uid else { return } FirebaseManager.shared.firestore .collection("users") .document(uid) .collection("userRecipes") .getDocuments() { (snapshot, err) in if let err = err { print("获取食谱失败: \(err.localizedDescription)") return } guard let snapshot = snapshot else { return } // 拉取新数据前清空旧缓存 self.recipeList.removeAll() for recipe in snapshot.documents { self.recipeList.append(recipe.data()) } // 所有数据处理完成后仅打印一次 print(self.recipeList) } } }
额外提示:RecipeItems模型中createdAt声明为String类型,但Firestore返回的createdAt字段是FIRTimestamp时间戳类型,后续如果要做Codable模型解码,需要手动做类型转换或自定义解码逻辑,否则会抛出类型不匹配错误。
内容的提问来源于stack exchange,提问作者Swink


