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

Firestore拉取userRecipes子集合文档时出现重复打印问题

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]类型,追加数据时容易出现非预期的缓存残留问题。

修复方案

  1. 为recipeList指定明确的类型[[String: Any]],匹配Firestore文档返回的数据结构
  2. 拉取新数据前调用removeAll()清空数组旧缓存,避免历史数据残留
  3. 将打印逻辑移到for循环外部,等所有文档遍历追加完成后,仅打印一次全量结果
  4. 避免强制解包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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:18:22