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

Core Data初始化IdeaEntity时重复存储实体问题求助

Core Data缓存Firebase数据时首次查询出现重复实体的问题解决

问题描述

开发iOS应用时,使用Core Data缓存Firebase查询结果以减少重复请求。当前遇到的核心问题:首次获取IdeaEntity时,已发布的IdeaEntity数组savedIdeas出现重复实体,但关闭并重新打开应用后,重复项自动消失。

相关代码

// Fetch either from Firebase or Core Data
func fetchIdeas() {
        let request = NSFetchRequest<IdeaEntity>(entityName: "IdeaEntity")
        
        do {
            self.savedIdeas = try appContainer.viewContext.fetch(request)
            
            if self.savedIdeas.isEmpty {
                self.initialFetchIdeas()
            }
        } catch {
            print("ERROR - \(error)")
        }
    }

// Initial Function to fetch IdeaEntity
private func initialFetchIdeas() {
        do {
            let uid = self.firebaseService.fetchUserID()
            guard !uid.isEmpty else { throw AltyrErrors.cannotFetchUid }
            
            self.firebaseService.fetchUser(withUid: uid) { [weak self] user in
                guard let self = self else { return }
                self.firebaseService.fetchIdeas(user: user) { ideas in
                    guard !(ideas.isEmpty) else { return }

                    ideas.forEach { idea in
                        let newIdea = IdeaEntity(context: self.appContainer.viewContext)
                        
                        // ... Insert code for inserting parameters for `newIdea`
                        
                        if let feedback = idea.feedback {
                            let newFeedback = FeedbackEntity(context: self.appContainer.viewContext)
                            
                            // ... Insert code for inserting parameters for `newIdea` Feedback object
                            
                            newIdea.feedback = newFeedback
                        }
                    }
                    self.save()
                }
            }
        } catch {
            // TODO: Error handling...
            print("ERROR - \(error)")
        }
    }

前提假设

  • FirebaseService功能正常,可正确返回所需数据;
  • 上述函数位于继承自ObservableObject的CoreDataService类中;
  • savedIdeas是IdeaEntity类型的Published数组。

问题原因分析

  1. 内存数组与Core Data上下文不同步:首次调用fetchIdeas时,先获取空数组并触发initialFetchIdeas从Firebase拉取数据插入Core Data。插入完成保存后,savedIdeas未重新从Core Data拉取最新结果,导致内存数组与上下文实体不同步,可能因监听机制重复添加实体。
  2. 重复调用风险:若fetchIdeas被多次触发(如单例初始化+视图onAppear调用),异步的initialFetchIdeas可能重复执行,导致上下文临时存在重复实体,反映到savedIdeas中。

解决方案

方案1:修复Core Data同步逻辑

  • 插入完成后重新拉取数据:在initialFetchIdeas的save()后调用fetchIdeas,确保savedIdeas始终是Core Data的最新查询结果:
    self.save()
    // 重新拉取更新savedIdeas
    self.fetchIdeas()
    
  • 添加实体唯一性校验:为IdeaEntity添加Firebase数据对应的唯一ID字段,插入前检查Core Data中是否已存在该实体,避免重复插入:
    ideas.forEach { idea in
        // 假设idea有唯一id字段
        let request = NSFetchRequest<IdeaEntity>(entityName: "IdeaEntity")
        request.predicate = NSPredicate(format: "id == %@", idea.id!)
        do {
            let existing = try self.appContainer.viewContext.fetch(request)
            if !existing.isEmpty { return } // 已存在则跳过
        } catch {
            print("检查实体失败:\(error)")
        }
        
        // 创建新实体并赋值
        let newIdea = IdeaEntity(context: self.appContainer.viewContext)
        // ... 赋值逻辑
    }
    
  • 防止重复触发拉取:添加状态标记,确保initialFetchIdeas仅执行一次:
    private var isFetching = false
    
    func fetchIdeas() {
        guard !isFetching else { return }
        isFetching = true
        let request = NSFetchRequest<IdeaEntity>(entityName: "IdeaEntity")
        
        do {
            self.savedIdeas = try appContainer.viewContext.fetch(request)
            if self.savedIdeas.isEmpty {
                self.initialFetchIdeas()
            }
            isFetching = false
        } catch {
            print("ERROR - \(error)")
            isFetching = false
        }
    }
    

方案2:使用NSCache替代Core Data做临时缓存

若仅需临时缓存Firebase查询结果(无需持久化到本地),推荐使用NSCache,无需维护Core Data实体模型与上下文,实现更简洁:

class FirebaseCacheService: ObservableObject {
    private let ideaCache = NSCache<NSString, NSArray>()
    @Published var savedIdeas: [Idea] = [] // Idea为自定义模型类
    private let firebaseService = FirebaseService()

    func fetchIdeas() {
        // 先从缓存读取
        if let cachedIdeas = ideaCache.object(forKey: "user_ideas") as? [Idea] {
            savedIdeas = cachedIdeas
            return
        }
        
        // 缓存为空时从Firebase拉取
        guard let uid = firebaseService.fetchUserID(), !uid.isEmpty else {
            print("无法获取用户ID")
            return
        }
        
        firebaseService.fetchUser(withUid: uid) { [weak self] user in
            guard let self = self else { return }
            self.firebaseService.fetchIdeas(user: user) { ideas in
                self.savedIdeas = ideas
                // 将结果存入缓存
                self.ideaCache.setObject(ideas as NSArray, forKey: "user_ideas")
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Marcus Arnett

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 17:31:01