观察SwiftData中所有对象的属性变化

问题描述 投票:0回答:1

在 Core Data 中,您可以执行类似的操作来观察某个类型的所有对象的属性变化:

.onReceive(NotificationCenter.default
    .publisher(for: .NSManagedObjectContextObjectsDidChange)
) { notif in
    guard let userInfo = notif.userInfo else { return }
    let updatedItems: [Item] = (userInfo[NSUpdatedObjectsKey] as?
                                Set<NSManagedObject> ?? [])
        .compactMap{$0 as? Item}
    let becameActive = updatedItems.filter{ (item: Item) -> Bool in
        guard let activeBefore = item
            .changedValuesForCurrentEvent()[#keyPath(Item.active)]
            as? Bool else { return false }
        return !activeBefore && item.active
    }
}

如何在 SwiftData 中实现这一点?该事件仍然会触发,但

compactMap{$0 as? Item}
始终返回 nil。我想这是因为在 SwiftData 中
Item
是一个 PersistentModel,而不是 NSManagedObject 子类。更清洁的解决方案可加分。

swift core-data swiftdata
1个回答
0
投票

我发现私人确实更改通知:

extension ModelContext {
    public static let didChangeX = NSNotification.Name(rawValue: "_SwiftDataModelsChangedInContextNotificationPrivate")
}

但是,我在拆箱私有

AnyPersistentObject
类型时遇到了困难,以便能够在我的应用程序中获取真实类型。但由于我不需要对象的实际属性,只需要它的类型,所以我想出了一个解决方法来比较包含该类型的字符串:

    public var modelContext: ModelContext! {
        didSet {
            if modelContext == oldValue { return } // perhaps should be removed, to have same semantics as the current var fetchDescriptor
            _results = nil
            NotificationCenter.default.removeObserver(self)
            if let modelContext {
                NotificationCenter.default.addObserver(self,
                                                       selector: #selector(contextModelsChanged),
                                                       name: ModelContext.didChangeX,
                                                       object: modelContext)
            }
        }
    }

    @objc private func contextModelsChanged(_ notification: Notification) {
        guard let userInfo = notification.userInfo else { return }
        // since AnyPersistentObject is private we need to use string comparison of the types
        let search = "AnyPersistentObject(boxed: \(String(reflecting: T.self)))" // e.g. AppName.Item
        for key in ["updated", "inserted", "deleted"] {
            if let set = userInfo[key] as? Set<AnyHashable> {
                if set.contains(where: { String(describing: $0) == search }) {
                    withTransaction(transaction) {
                        _results = nil
                    }
                    return
                }
            }
        }
    }

仅供参考,此代码来自我的 SwiftDataX 项目,在该项目中,我实现了

NSFetchedResultsController
等效项,并将其用于
@DynamicQuery
,这是一个类似于
@Query
的属性包装器,但允许动态配置获取描述符(如排序和谓词)。

© www.soinside.com 2019 - 2024. All rights reserved.