SwiftUI中Realm集合变更监听实现方案咨询
解决方案:基于Repository+Combine实现Realm集合变更监听
核心思路
通过在RealmManager中封装通用的集合监听逻辑,结合Combine发布数据流;Repository负责Realm对象与普通业务模型的转换,对外暴露干净的数据流接口;ViewModel订阅该数据流并更新@Published属性,让SwiftUI视图自动刷新。全程ViewModel无需直接持有Realm对象。
1. 实现通用RealmManager(封装监听与写入)
import RealmSwift import Combine class RealmManager { static let shared = RealmManager() private let realm: Realm private init() { do { realm = try Realm() } catch { fatalError("Realm初始化失败: \(error.localizedDescription)") } } // 监听指定类型的集合变化,返回转换后的业务模型流 func observeCollection<T: Object, M>(_ type: T.Type, transform: @escaping (Results<T>) -> [M]) -> AnyPublisher<[M], Never> { let results = realm.objects(type) return PassthroughSubject<[M], Never>() .handleEvents(receiveSubscription: { subscription in // 添加Realm通知监听,保存token防止提前释放 let token = results.observe { changes in switch changes { case .initial(let results), .update(let results, _, _, _): subscription.subject.send(transform(results)) case .error(let error): print("Realm监听错误: \(error.localizedDescription)") } } subscription.token = token }) .eraseToAnyPublisher() } // 通用写入操作封装 func write(_ block: @escaping (Realm) throws -> Void) { do { try realm.write(block) } catch { print("Realm写入失败: \(error.localizedDescription)") } } } // 给Subscription扩展,用于保存Realm监听token private var tokenKey: UInt8 = 0 extension Subscription { var token: NotificationToken? { get { objc_getAssociatedObject(self, &tokenKey) as? NotificationToken } set { objc_setAssociatedObject(self, &tokenKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } // 给PassthroughSubject的Subscription扩展,方便获取Subject extension PassthroughSubject { var subject: PassthroughSubject<Output, Failure> { self } }
2. 实现ConversationRepository(隔离业务与Realm细节)
先定义普通业务模型(与Realm对象解耦),再封装监听和操作方法:
// 普通业务模型,无Realm依赖 struct Conversation { let id: String let title: String // 其他业务属性... } // Realm对象定义(仅在Repository/Manager层使用) class ConversationObject: Object { @Persisted(primaryKey: true) var id: String @Persisted var title: String // 其他对应属性... } class ConversationRepository { private let realmManager = RealmManager.shared // Realm对象转业务模型的映射方法 private func transform(_ object: ConversationObject) -> Conversation { Conversation( id: object.id, title: object.title // 映射其他属性 ) } // 对外暴露会话列表的监听数据流 func observeConversations() -> AnyPublisher<[Conversation], Never> { realmManager.observeCollection(ConversationObject.self) { results in results.map(self.transform) } } // 删除所有会话的业务方法 func deleteAllConversations() { realmManager.write { realm in realm.delete(realm.objects(ConversationObject.self)) } } }
3. 改造ChatViewModel(订阅数据流更新视图)
import Combine class ChatViewModel: ObservableObject { @Published var conversations: [Conversation] = [] private let repository = ConversationRepository() private var cancellables = Set<AnyCancellable>() init() { setupConversationSubscription() } private func setupConversationSubscription() { repository.observeConversations() .receive(on: DispatchQueue.main) // 确保UI更新在主线程 .assign(to: \.conversations, on: self) .store(in: &cancellables) } }
4. SettingViewModel调用删除方法
class SettingViewModel: ObservableObject { private let conversationRepo = ConversationRepository() func deleteAllConversations() { conversationRepo.deleteAllConversations() } }
关键说明
- 当在SettingsView中调用
deleteAllConversations后,Realm会触发集合变更通知,RealmManager捕捉到变化后将转换后的空数组通过Publisher发送给ChatViewModel,@Published属性更新后自动刷新ChatView。 - 全程ViewModel仅持有普通业务模型,完全隔离Realm的底层细节,符合分层架构设计。
- 监听token通过Subscription的关联对象保存,确保监听生命周期与ViewModel一致,不会提前失效。
内容的提问来源于stack exchange,提问作者Vader20FF
相关产品推荐
相关产品推荐

