swiftrealmrealm-cocoa

Creating a LinkingObjects property in RealmSwift by using multiple-level properties


According to the Realm documentation for v0.102.0, this is how you create an inverse relationship:

Person

class Person: Object {
    // ... other property declarations
    let dogs = List<Dog>()
}

Dog (v1)

class Dog: Object {
    // ... other property declarations
    let owners = LinkingObjects(fromType: Person.self, property: "dogs")
}

Suppose that we have another class, called DogFood, and we want to create an inverse relationship called buyers that tracks which instances of Person have a Dog that eats that instance of DogFood. We could try the following:

Dog (v2)

class Dog: Object {
    // ... other property declarations
    let favoriteFoods = List<DogFood>
    let owners = LinkingObjects(fromType: Person.self, property: "dogs")
}

DogFood

class DogFood: Object {
    // ... other property declarations
    let buyers = LinkingObjects(fromType: Person.self, property: "dogs.favoriteFoods")
}

However, this throws the following error: Property 'dogs.favoriteFoods' declared as origin of linking objects property 'buyers' does not exist.

Is there another way to achieve the same effect?


Solution

  • This can be achieved with a filter, with the caveat that the DogFood object we need to query about needs to be part of a Realm.

    The code would go as follows:

    class DogFood: Object {
        // ... other property declarations
        var items: Results<Person> {
            if let realm = self.realm {
                return realm.objects(Person).filter(NSPredicate(format: "ANY dogs.favoriteFoods.id == %@", self.id))
            } else {
                return RealmSwift.List<Person>().filter("1 != 1")
            }
        }
    }
    

    The property realm is of type Realm? and comes into the picture through inheritance from Object. It points towards the realm that a particular Object instance is in and returns nil if not assigned to one. If that is the case, we force the property items to return an empty set of Results<Person>.