I have a UITableView that updates when there is a RealmCollectionChange. I want to know how to convert a modified object's child to an IndexPath so that I can reload the relevant row in that TableView section.
Each table section header in my TableView is represented by a Mum in the Granny.secondGeneration List. Each TableViewCell in that section is represented by each Kid object in the Mum.thirdGeneration List.
When a Kid object is modified, I want to access that tableRow Index to reload it. But the modifications array only returns the section/section index, I'm not sure how to get the rowIndex from that to reload just the Kid TableViewCell.
class Granny:Object {
@Persisted var name:String = ""
@Persisted var secondGeneration = RealmSwift.List<Mum>()
}
class Mum:Object {
@Persisted var name:String = ""
@Persisted var thirdGeneration = RealmSwift.List<Kid>()
}
class Kid:Object {
@Persisted var name:String = ""
}
...
let granny = Granny(name: "Sheila")
let mum = Mum(name: "Mary")
granny.secondGeneration.append(mum)
let kid1 = Kid(name: "Lola")
let kid2 = Kid(name: "Greg")
mum.thirdGeneration.append(kid1)
mum.thirdGeneration.append(kid2)
RealmManager.add(object: granny)
...
notificationToken = granny.secondGeneration.observe { [weak self] (changes: RealmCollectionChange) in
guard let tableView = self?.tableView else { return }
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
tableView.reloadData()
case .update(_, let deletions, let insertions, let modifications):
print("Insertions: \(insertions.count) Deletions:\(deletions.count) Modifications: \(modifications)")
tableView.beginUpdates()
if !modifications.isEmpty {
let modificationsSectionIndex = modifications.map({ IndexSet(integer: $0) })[0]
// This reloads the 'Mum' section header, but I want to find out the row index of the modified child
tableView.reloadSections(modificationsSectionIndex, with: .automatic)
}
tableView.endUpdates()
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
}
}
Allow me to approach this at a high level with simplified code - I think that will make the solution more clear.
Answer: Change your logic. Instead of observing the Parent
class looking for changes in Children
, observe the Children
class who know who their Parent
is.
Here's the setup using Persons and Dogs, which is analogous to a parent->child relationship
class PersonClass: Object {
@Persisted var name = ""
@Persisted var dogList = RealmSwift.List<DogClass>()
}
class DogClass: Object {
@Persisted var name = ""
@Persisted(originProperty: "dogList") var linkedPersons: LinkingObjects<PersonClass>
}
There are persons who have dogs (similar to your setup) so Realm will look like this
person0
dog0
dog1
person2
dog3
dog4
Then, populate dogResults and attach an observer
self.dogResults = realm.objects(DogClass.self)
self.dogsToken = self.dogResults!.observe { changes in
And then here's what happens in the update (note addition for first parameter 'changedItems'
case .update(let changedItems, let deletions, let insertions, let modifications ):
section: modified
let paths = modifications.map { (index) -> IndexPath in
//the dog that was modified in the dogs results
let modifiedDog = changedItems[index]
//the owner of this dog e.g. the section header
let owner = modifiedDog.linkingPerson.first!
//the index of the person section
let sectionIndex = self.peopleResults?.index(of: owner)
//the row within the section of this modified dog
let rowIndex = owner.dogList.index(of: modifiedDog)
//the IndexPath to the dog row within the section
let path = IndexPath(item: rowIndex!, section: sectionIndex!)
print(path) //print each path
return path
}
We now have and array of index paths for the sections/rows that need to be updated. If it's never going to be multiple row updates at the same time, the map can be removed to just address one row at a time