如何使用领域通知来检测嵌套领域对象的变化

How to use realm notifications to detect changes in nested realm objects

我正在尝试为 ios 构建一个应用程序来显示各种节日的日常节目。在此应用程序中,我希望每次组成节日节目的事件之一是 "added"、"modified" 或 "deleted" 时通知用户。

对于数据库,我使用的是领域云服务。 我的数据库结构如下: 我有一个名为 "Festival" 的 Realm 对象。在这里面我有一个 "DailyProgram" 对象的列表,在每个 "DailyProgram" 里面我有一个 "Event" 的列表。 我在下面提供了这些对象的片段。

当应用程序访问 Realm Cloud 时,它会下载一个包含所有不同节日的数组。
我想使用领域内置的通知系统来检测 "Event" 对象的变化。

我试图在这个节日数组上实现一个收集通知,但是当我在一个事件中对数据库进行更改时,收集通知只是 returns 与节日对应的数组索引变化发生。我想获得有关发生更改的特定事件以及更改内容的信息。

class Festival: Object {

    @objc dynamic var festivalId: String = UUID().uuidString
    @objc dynamic var name: String = ""
    @objc dynamic var town: String = ""
    @objc dynamic var initialDate: Date = Date()
    @objc dynamic var finalDate: Date = Date()

    let program = List<DailyProgram>()

    override static func primaryKey() -> String? {
        return "festivalId"
    }
}
class DailyProgram: Object{

    @objc dynamic var day: Date = Date()
    let events = List<Event>()
}
class Event: Object {
    @objc dynamic var name: String = ""
    @objc dynamic var local: String = ""
    @objc dynamic var time: Date = Date()

}

我很清楚 Realm 中对象通知的存在。我认为这些可能是我的问题的解决方案。我知道如何在单个对象上实现对象通知。但是,我不知道如何在嵌套对象的情况下实现这些类型的通知。

简答:

在事件与其所属的节日之间创建反向关系。然后根据属于特定节日的事件获得结果并观察它们。然后,当为该节日添加、修改或删除任何活动时,您将收到通知。

基于长代码的答案。从修改您的领域对象开始(我去掉了属性以缩短代码)

class Festival: Object {
    @objc dynamic var festivalId: String = UUID().uuidString
    @objc dynamic var name: String = ""
    let program = List<DailyProgram>()
    override static func primaryKey() -> String? {
        return "festivalId"
    }
}

class DailyProgram: Object {
    @objc dynamic var day = ""
    @objc dynamic var festival: Festival!  //add this
    let events = List<Event>()
}

class Event: Object {
    @objc dynamic var daily_program: DailyProgram!
    @objc dynamic var festival: Festival! //add this
    @objc dynamic var name: String = ""
}

然后创建确保添加反向关系的对象。注意 var 命名,f0 = Fest 0,f1 = Fest 1,然后 dp = DailyProgram 和 e = Event。这样做是为了更容易看到层次结构。

let f0 = Festival()
f0.name = "Fest 0"

let f0dp0 = DailyProgram()
f0dp0.day = "Monday"
f0dp0.festival = f0

let f0dp0e0 = Event()
f0dp0e0.name = "Morning event"
f0dp0e0.daily_program = f0dp0
f0dp0e0.festival = f0

let f0dp0e1 = Event()
f0dp0e1.name = "Afternoon event"
f0dp0e1.daily_program = f0dp0
f0dp0e1.festival = f0


let f1 = Festival()
f1.name = "Fest 1"

let f1dp0 = DailyProgram()
f1dp0.day = "Monday"
f1dp0.festival = f1

let f1dp0e0 = Event()
f1dp0e0.name = "Evening event"
f1dp0e0.daily_program = f1dp0
f1dp0e0.festival = f1

let f1dp0e1 = Event()
f1dp0e1.name = "Midnight event"
f1dp0e1.daily_program = f1dp0
f1dp0e1.festival = f1

上面创建了两个节日,每个节日都有一个周一的每日节目,然后每个周一的节目都有两个活动。

然后我们加载我们的结果并添加一个观察者。在这种情况下,我们对 Fest 1 活动感兴趣。

var eventResults: Results<Event>? = nil
var eventNotificationToken: NotificationToken? = nil

func loadAndObserveFestivalEvents() {
    if let realm = gGetRealm() {
        self.eventResults = realm.objects(Event.self).filter("festival.name == %@", "Fest 1")
        self.observeEvents()
    }
}

func observeEvents() {
    self.eventNotificationToken = self.eventResults!.observe { (changes: RealmCollectionChange) in
        switch changes {
        case .initial:
            print("initial event load complete")
            if let results = self.eventResults { //just shows events are loaded
                for e in results {
                    let d = e.daily_program.day
                    let f = e.festival.name
                    print(f, d, e.name)
                }
            }
            break
        case .update(_, let deletions, let insertions, let modifications):
            print(" handle event delete, insert or mod")
            if let results = self.eventResults { //shows the list including the newly added event
                for e in results {
                    let d = e.daily_program.day
                    let f = e.festival.name
                    print(f, d, e.name)
                }
            }
            break
        case .error(let error):
            fatalError("\(error)")
            break

        }
    }
}

此时我们已准备好向 Fest 1 的星期一活动添加一个活动,然后收到添加该活动的通知。

func addEvent() {
    if let realm = gGetRealm() {
        let dp = realm.objects(DailyProgram.self).filter("festival.name == %@ && day == %@", "Fest 1", "Monday")
        if let daily = dp.first {
            let eventToAdd = Event()
            eventToAdd.daily_program = daily
            eventToAdd.festival = daily.festival
            eventToAdd.name = "Super Duper Event"

            try! realm.write {
                daily.events.append(eventToAdd)
            }
        }
    }
}

代码的最后一部分创建一个结果对象,其中填充了 Fest 1,星期一的 DailyProgram。

然后我创建一个新事件,确保我 link 将其返回到 Fest 1 音乐节,然后将其添加到 DailyPrograms 事件列表中。

此代码可以大大缩短并利用 LinkingObject 自动创建 link 返回。然而,在这种情况下,一个事件将永远只属于一个 DailyProgram 和 Festival,所以它并不是真正需要的。

注意gGetRealm是我用来连接realm的单例。