Reputation: 23
I am trying to build an app for ios to display various festivals daily programs. In this app, I want the user to be notified each time one of the events that compose a festival program is either "added", "modified" or "deleted".
For the database I am using realms cloud service. The structure of my database is the following: I have a Realm object called "Festival". Inside this I have a list of "DailyProgram" objects and inside of each "DailyProgram" I have a list of "Event". I provide a snippet bellow of these objects.
When the app access the Realm Cloud, it downloads an array containing all the different festivals.
I would like to use realm built-in notification system in order to detect changes in the "Event" object.
I tried to implement a Collection Notification on this festival array, but when I make a change in one Event back on the database, the Collection Notification just returns the array index corresponding to the Festival in which that change occurred. And I would like to get information on which particular Event that change occurred as well as what was changed.
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()
}
I am well aware of the existence of Object Notifications in Realm. I think those might be the solution for my problem. I know how to implement an Object Notification on an individual object. However, I don't know how to implement these type of notifications in a case of nested objects.
Upvotes: 1
Views: 2100
Reputation: 35648
The short answer:
Create an inverse relationship between the event and the Festival it belongs to. Then get results based on the events that belong to a specific festival and observe them. Then when any events are added, modified or removed for that festival, you'll receive a notification.
The long code based answer. Start with modifying your Realm Objects (I took properties out to shorten the code)
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 = ""
}
Then create the objects ensuring an inverse relationship is added. Note the var naming, f0 = Fest 0, f1 = Fest 1, then dp = DailyProgram and e = Event. Did that to make it easier to see the hierarchy.
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
The above creates two festivals, each with a single daily program for Monday then each Monday program has two events.
Then we load our results and add an observer. In this case we are interested in Fest 1 events.
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
}
}
}
At this point we are set up to add an event to Fest 1's Monday events, and then receive notification that it was added.
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)
}
}
}
}
The last section of code creates a results object that is populated with a DailyProgram for Fest 1, Monday.
I then create a new event, ensuring I link it back to the Fest 1 festival and then add it to that DailyPrograms event list.
This code could be shortened considerably as well as levering LinkingObject to auto create the link back. However, in this case an event will only ever belong to one DailyProgram and Festival so it's not really needed.
Note gGetRealm
is a singleton I used to connect to realm.
Upvotes: 1