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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:16:12