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数组。
问题原因分析
- 内存数组与Core Data上下文不同步:首次调用
fetchIdeas时,先获取空数组并触发initialFetchIdeas从Firebase拉取数据插入Core Data。插入完成保存后,savedIdeas未重新从Core Data拉取最新结果,导致内存数组与上下文实体不同步,可能因监听机制重复添加实体。 - 重复调用风险:若
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
相关产品推荐
相关产品推荐

