iOS应用ViewModel订阅后数组未更新问题求助
iOS CoreData 数据更新后 ViewModel 未同步问题解决思路
问题分析
- 不必要的 Debounce 延迟:ViewModel 中对
savedEntities的订阅添加了 1 秒.debounce,会导致界面更新被延迟,容易让你误以为数据未同步。 - CoreData 操作线程不统一:初始化时
getHistoryData()在后台线程执行(container.loadPersistentStores闭包运行在后台),此时对@Published属性的赋值不在主线程,Combine 可能无法正确将更新传递到主线程订阅者。 - 手动刷新的可靠性不足:当前依赖手动调用
getHistoryData()更新数据,一旦遗漏调用就会导致数据不同步,且重复 fetch 的效率较低。
解决方案
1. 移除不必要的 Debounce
删除订阅中的防抖逻辑,添加单条数据只会触发一次更新,不需要延迟:
func addSubscribers(){ historyDataService.$savedEntities .receive(on: RunLoop.main) // 确保在主线程更新 UI 相关数据 .sink { [weak self] returnData in guard let self = self else { return } self.historyArray = returnData self.updateFilteredContacts() } .store(in: &cancellables) }
2. 强制 CoreData 操作在主线程执行
修改 getHistoryData(),确保所有涉及 @Published 属性的操作都在主线程完成:
private func getHistoryData(){ DispatchQueue.main.async { [weak self] in guard let self = self else { return } let request = NSFetchRequest<HistoryData>(entityName: self.entityName) // 可选:添加排序规则保证数据顺序一致 request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryData.time, ascending: false)] do { self.savedEntities = try self.container.viewContext.fetch(request) } catch let error { print("## Error fetch: \(error)") } } }
3. 监听 CoreData 上下文变化自动刷新
替换手动调用刷新的逻辑,通过监听上下文通知实现自动更新,更可靠高效:
class HistoryCallDataService { // ... 原有代码 ... init() { container = NSPersistentContainer(name: containerName) container.loadPersistentStores { _, error in if let error = error { print("## Error loading Core data :\(error)") } // 添加上下文变化监听 NotificationCenter.default.addObserver( self, selector: #selector(contextDidChange(_:)), name: NSManagedObjectContext.objectsDidChangeNotification, object: self.container.viewContext ) self.getHistoryData() } } @objc private func contextDidChange(_ notification: Notification) { getHistoryData() } deinit { NotificationCenter.default.removeObserver(self) } // 修改 add 方法,只需保存,监听会自动触发刷新 func add(firstName: String, lastName: String, telephone: String, callHidden: Bool) { let entity = HistoryData(context: container.viewContext) let date = Date() let df = DateFormatter() df.dateFormat = "yyyy-MM-dd HH:mm:ss" let timeString = df.string(from: date) entity.firstName = firstName entity.lastName = lastName entity.telephone = telephone entity.time = timeString entity.callHidden = callHidden save() } // ... 原有代码 ... }
4. 完善 Delete 方法(补充)
视图中用到了删除逻辑,但 ViewModel 未实现对应方法,补充后确保删除数据也能同步更新:
// 在 HistoryViewModel 中添加 func delete(entity: HistoryData) { historyDataService.delete(entity: entity) } // 在 HistoryCallDataService 中添加 extension HistoryCallDataService { func delete(entity: HistoryData) { container.viewContext.delete(entity) save() } }
额外优化建议
- 给
HistoryData显式声明遵循Identifiable协议(NSManagedObject 默认已实现),让 ForEach 用法更规范。 - 优化 DateFormatter,避免重复创建实例:
private static let dateFormatter: DateFormatter = { let df = DateFormatter() df.dateFormat = "yyyy-MM-dd HH:mm:ss" return df }() // 使用时: let timeString = Self.dateFormatter.string(from: date)
内容的提问来源于stack exchange,提问作者DrikiDev
相关产品推荐
相关产品推荐

