Search code examples
swiftrealm

RealmCollectionChange modified parameter has multiple indexes when only one object is modified


Issue: When a single object is modified in Realm, the modified parameter in a RealmCollectionChange is passed multiple indexes

Details: Given a Person object which has a list of Dog Objects and a Dog object that has an owner property of type Person

class Dog: Object {
    @objc dynamic var name = ""
    @objc dynamic var owner: Person?
}

class Person: Object {
    @objc dynamic var name = ""
    let dogs = List<Dog>()
}

then a single person is created with two dogs

let person0 = Person()
person0.personName = "Leroy"

let dog0 = Dog()
dog0.name = "Spot"
dog0.owner = person0

let dog1 = Dog()
dog1.name = "Rex"
dog1.owner = person0

person0.dogs.append(dog0)
person0.dogs.append(dog1)

//write person0 to Realm which creates the person and two dogs

and the observe function

func doObserve() {
    let realm = try! Realm()
    let results = realm.objects(Dog.self)

    notificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in

        switch changes {
        case .initial:
        case .update(_, let deletions, let insertions, let modifications):
            for index in deletions {
                print("deleted object at index: \(index)")
            }

            for index in insertions {
                print("inserted object at index: \(index)")
            }

            for index in modifications {
                print("modified object at index: \(index)")
                let object = results[index]
                print(object) //prints dog0 and dog1 when dog1 is mod
            }
        case .error(let error):
            fatalError("\(error)")
        }
    }
}

then, if the Realm file is opened with RealmBrowser (or using code in the App) and Dog1 (Rex) name is changed to Fluffy, the modified parameter within the RealmCollectionChange contains two indexes, 0 and 1 even though only dog1 (index 1) was modified.

Is this behavior correct and if so, is there another option to just get the (index to the) object that was modified?

(or am I totally missing something obvious?)


Solution

  • When you modify dog1, dog0.owner.dogs[1] has changed, so Realm reports that dog0 is modified as well.

    There's currently no way to manually specify how deep of a path you want Realm to check for changes to an object, but you may be able to sidestep this behavior by changing Person's List<Dog> to LinkingObjects<Dog>, as inverse relationships are not followed when checking for changes. This also would remove the need to set the relationship in two places.