swiftui - 在领域数据库中进行更改时,如何在 SwiftUI 中自动更新 UI?
问题描述
我的领域数据库结构如下例所示:
class Person: Object, Identifiable {
@objc dynamic var id: String = NSUUID().uuidString
@objc dynamic var name: String = ""
var dogs = RealmSwift.List<Dog>()
}
class Dog: Object, Identifiable {
@objc dynamic var id: String = NSUUID().uuidString
@objc dynamic var name: String = ""
let human = RealmSwift.LinkingObjects<Person>(fromType: Person.self, property: "dogs")
}
为了简化 CRUD 操作,我有一个数据库管理器:
extension Realm {
public func safeWrite(_ block: (() throws -> Void)) throws {
if isInWriteTransaction {
try block()
} else {
try write(block)
}
}
}
class DatabaseManager {
private let realm: Realm
public static let sharedInstance = DatabaseManager()
private init(){
realm = try! Realm()
}
func save(_ obj: Object){
do {
try realm.safeWrite {
realm.add(obj, update: .all)
}
} catch {
NSLog("error saving object: %@", [error])
}
}
func save(_ obj: Object,_ block: () -> Void){
do {
try realm.safeWrite{
realm.add(obj, update: .all)
block()
}
} catch {
NSLog("error saving object: %@", [error])
}
}
func save(_ objs: [Object]){
do {
try realm.safeWrite {
realm.add(objs, update: .all)
}
} catch {
NSLog("error saving object: %@", [error])
}
}
func fetchData<T: Object>(type: T.Type) -> Results<T>{
let results: Results<T> = realm.objects(type)
return results
}
func delete(_ obj: Object){
do {
try realm.safeWrite {
realm.delete(obj)
}
} catch {
NSLog("error deleting object: %@", [error])
}
}
func update(_ block: @escaping () -> Void){
do {
try realm.safeWrite{
block()
}
} catch {
NSLog("error updating object: %@", [error])
}
}
}
现在我有不同的视图,我需要访问我的数据库并显示一些数据。对于这种情况,我创建了一个视图模型类:
class PersonViewModel: ObservableObject {
let realm = DatabaseManager.sharedInstance
@Published var persons: Results<Person> = DatabaseManager.sharedInstance.fetchData(type: Person.self).sorted(byKeyPath: "name", ascending: true)
public func fetch(){
self.persons = DatabaseManager.sharedInstance.fetchData(type: Person.self).sorted(byKeyPath: "name", ascending: true)
}
public func addPerson(person: Person){
realm.save(Person)
self.fetch() // <---- necessary to update the UI: how to auto update?
}
}
这个视图模型类将作为一个传递@EnvironmentObject
给我需要数据的视图。如您所见,我需要在每次数据库操作后再次获取所有数据以进行“新鲜更新” Results<Person>
。我知道这Results<T>
是实时的,但它对 UI 没有影响。当我对数据库进行更改而不再次手动获取所有数据时,有什么方法可以自动更新所有视图?
解决方案
虽然 Realm 会在删除、添加或更改新数据时自动更新集合,但需要有一个触发器来通知您的代码这些更改。
那是一个通知,也就是观察者。
在这种情况下,有一个persons
使用 fetchData 函数填充的 Results 对象:
@Published var persons: Results<Person> = DatabaseManager.sharedInstance.fetchData(type: Person.self).sorted(byKeyPath: "name", ascending: true)
因此,当一个人被删除、添加或更改时,persons
结果对象将自动更新。然而,要得到通知,需要添加一个观察者,并且有两个组件:一个类级别的通知令牌,然后是一个处理事件的函数。
通知令牌是这样定义的(注意需要强引用才能使其保持活动状态)
var notificationToken: NotificationToken? = nil
并确保在视图关闭时释放它
deinit {
self.notificationToken?.invalidate()
}
然后是当人们发生变化时将执行的代码(以最简单的形式)
self.notificationToken = self.persons?.observe { changes in
switch changes {
case .initial:
print("initial load")
//the initial data is ready - reload tableviews/update ui etc
case .update(_, _, _, _):
print("update")
//some change occurred, reload tableviews/update ui etc
case .error(let error):
fatalError("\(error)")
}
}
这是一个深入介绍此内容的链接Collection Notifications
推荐阅读
- python - 我无法从我的 python 表单获取输出
- google-cloud-platform - bigQuery 中缺少项目,但数据集和表仍然存在
- r - 受访者的答案不一致 - 给出多个答案时保持最低答案?在 R Tidyverse 中
- r - 根据日期合并两个长格式数据帧
- python - - 不支持的操作数类型:'datetime.time' 和 'datetime.time' 当我计算 OT 小时数和总小时数时
- python - Python Matplotlib:在 Windows 上将 matplotlib 更新到 3.1.2 的问题
- java - 在自己的类中实例化对象
- arrays - 模板只包含上下文传递的数组的最后一个元素
- r - 部分 pivot_longer 的更好解决方案
- javascript - 如何使用 VeeValidate 3 和 vue.js 制定自定义验证规则