Search code examples
objective-cmany-to-manyrealmrlmarrayrlmlinkingobjects

Many-to-Many Relationships in Realm using Objective-C


I have spent hours upon hours trying to figure out how to setup the models for object relationships and create/delete many-to-many relationships in Realm using Objective-C. The documentation has been less than helpful. I could use some guidance on whether to use RLMArray or RLMLinkingObjects... If I use RLMLinkingObjects to set up my to-many relationships, I cannot use addObject: to create the relationships, and I cannot find any way to delete a relationship. I am moving from a Core Data relational database and have several many-to-many relationships that I am trying to model, and this just makes no sense at all to me. The only documentation showing a many-to-many relationship shows a class relating two of its own properties. One of the properties uses RLMArray and the other uses RLMLinkingObjects. This makes no sense to me, since a many-to-many relationship is supposed to be bi-directional, and relationships should be able to be added or deleted via either side. Maybe I am thinking about this wrongly, but I am lost and would appreciate some help.


Solution

  • Many relationships in Realm are modelled with RLMArray. Every to-one and to-many relationship however creates its own corresponding inverse relationship, which is modelled by RLMLinkingObjects (the objects that are linking to this current object).

    So RLMArray is a unidirectional link to multiple objects, and RLMLinkingObjects is a unidirectional inverse link to the objects that are linking to the object. The relationship can be modified via the RLMArray, which will automatically manage its inverse relation (corresponding RLMLinkingObjects).

    If you need to modify the relationship from the target, then you can find the object with the RLMArray using a link query across the linking objects via primary key.

    import <Realm/Realm.h>
    
    @class Person;
    
    // Dog model
    @interface Dog : RLMObject
    @property NSInteger id;
    @property NSString *name;
    @property (readonly) RLMLinkingObjects *owners;
    @end
    RLM_ARRAY_TYPE(Dog) // define RLMArray<Dog>
    
    // Person model
    @interface Person : RLMObject
    @property NSInteger            id;
    @property NSString             *name;
    @property NSDate               *birthdate;
    @property RLMArray<Dog *><Dog> *dogs;
    @end
    RLM_ARRAY_TYPE(Person) // define RLMArray<Person>
    
    // Implementations
    @implementation Dog
    + (NSDictionary *)linkingObjectsProperties {
        return @{
            @"owners": [RLMPropertyDescriptor descriptorWithClass:Person.class propertyName:@"dogs"],
        };
    }
    
    + (NSString *)primaryKey {
        return @"id";
    }
    
    + (NSArray *)indexedProperties {
      return @[@"name"];
    }
    @end
    
    @implementation Person
    + (NSString *)primaryKey {
        return @"id";
    }
    
    + (NSArray *)indexedProperties {
      return @[@"name"];
    }
    @end
    

    So when you modify person.dogs in a transaction, then dog.owners will be automatically updated.

    RLMRealm *realm = [RLMRealm defaultRealm];
    [realm transactionWithBlock:^{
        Person *jim = [[Person objectsWhere:@"name == 'Jim'"] firstObject];
        // Jim is owner of Rex 
        Dog * rex = [[Dog objectsWhere:@"name == 'Rex'"] firstObject];
        [jim.dogs addObject:rex]; // <-- now rex's `owners` contains `jim`
    }];
    

    You can read more about relationships in Realm here.