Combine订阅异常:Core Data未读通知数无法实时更新UI
解决方案
问题的核心是原代码使用Just包装单次计数结果,导致仅在首次订阅时发送一次值,后续Core Data数据变更(如标记通知为已读)无法触发更新。以下是修正方案:
1. 改造Core Data计数Publisher
将单次计数改为监听上下文变化的持续Publisher,每次数据变更时重新计算未读数量:
public func fetchCountPublisher<T: PersistentModel>(_ type: T.Type, predicate: NSPredicate?) -> AnyPublisher<Int, Error> { let request = T.fetchRequest() request.predicate = predicate // 监听当前上下文的数据变更通知 return NotificationCenter.default.publisher( for: NSManagedObjectContext.didChangeNotification, object: context ) // 初始触发一次计数,保证启动时能获取初始值 .prepend(()) .flatMap { [weak self] _ -> AnyPublisher<Int, Error> in guard let self = self else { return Fail(error: NSError( domain: "Repository", code: -1, userInfo: [NSLocalizedDescriptionKey: "Repository已释放"] )).eraseToAnyPublisher() } do { let count = try self.context.count(for: request) return Just(count) .setFailureType(to: Error.self) .eraseToAnyPublisher() } catch { return Fail(error: error).eraseToAnyPublisher() } } // 确保在主线程发送结果,避免UI更新异常 .receive(on: DispatchQueue.main) .eraseToAnyPublisher() }
2. 修正未读判断逻辑
原代码中$0 > 1的判断不符合常规需求(只要有未读就应显示角标),建议修正:
var hasUnreadNotificationsPublisher: AnyPublisher<Bool, Error> { localNotificationsManager.hasUnreadNotifications() .map { $0 > 0 } // 只要存在未读通知就返回true .eraseToAnyPublisher() }
3. 原有ViewModel/ViewController代码无需修改
ViewModel中对hasUnreadNotificationsPublisher的订阅、ViewController中对@Published状态的监听逻辑保持不变,上游Publisher持续发送新值时,UI会自动更新。
关键说明
- 通过监听
NSManagedObjectContext.didChangeNotification,能捕获所有当前上下文的Core Data数据变更(包括标记通知为已读的操作) prepend(())确保首次订阅时立即获取初始计数receive(on: DispatchQueue.main)保证UI更新在主线程执行,避免线程问题
内容的提问来源于stack exchange,提问作者Stefan
相关产品推荐
相关产品推荐

