Realm DotNet SDK incompatible with Swift SDK using Backlinks

Last week I wrote this question regarding Backlinks in the DotNet-SDK Two-Way Relationship

Here the Code again just for reference:

public class Owner : RealmObject
{
    [PrimaryKey, MapTo("id")]
    public int ID { get; init; }
    
    [MapTo("name"), Required]
    public string Name { get; set; }

    [MapTo("dogs"), Backlink(nameof(Dog.Owner))]
    public IQueryable<Dog> Dogs { get; }
}

public class Dog : RealmObject
{
    [PrimaryKey, MapTo("id")]
    public int ID { get; init; }
    
    [MapTo("name"), Required]
    public string Name { get; set; }
    
    [MapTo("owner")]
    public Owner Owner { get; set; }
}

While the provided Code works in DotNet, we are having trouble to get the database-file to work properly in swift.

In dotnet I can access an Owner from a Dog and vice versa.

If we try to load the realm-file into swift using the documentation https://www.mongodb.com/docs/realm/sdk/swift/examples/define-a-realm-object-model/#define-an-inverse-relationship-property we cannot get the Backlink to work.

Either we receive an error:

ERROR Migration is required due to the following errors:
- Property 'Dog.Owner' has been removed
- Property 'Owner.Dogs' has been added

Or this error, if we try to open the database in read-only mode:

ERROR The following changed cannot be made in read-only schema mode:
- Property 'Owner.Dogs' has been added

Our Swift-Code looks like this:

class Owner: Object {
    @Persisted(primaryKey: true) var id: Int
    @Persisted(indexed: true) var name: String
    @Persisted var Dogs: List<Dog>
}

class Dog: Object {
    @Persisted(primaryKey: true) var id: Int
    @Persisted(indexed: true) var name: String
    @Persisted(originProperty: "Dogs") var owner: LinkingObjects<Owner>
}

static var config: Realm.Configuration = {
    var migrationBlock: MigrationBlock = { migration, oldSchemaVersion in
    }
    var config = Realm.Configuration(schemaVersion: 1, migrationBlock: migrationBlock)
    config.objectTypes = [
        Owner.self,
        Dog.self]
    config.fileURL = realmFile.url
    config.readOnly = true
    return config
}()

The provided SchemaVersion 1 is the same as provided, when the database is created in dotnet.
Is there something we are missing or are both sdks just not compatible in this case?

If any more code is needed to help me with my problem I will provide it.

It looks like you’ve mapped your dotnet properties to be lowercased, but your Swift classes still have the capital “D” for the Dogs property.

We first tried using the mapped name in swift (lowercase) and then tried using the uppercase Version. Both yielded the exact same errors.

You also need to wipe the local state as you make changes to the schema as the realm file gets persisted to disk.

@Ian_Ward could you please elaborate what you mean?

We are not sure what you mean by wiping the local state.

The issue is that you’re using the opposite properties for linking objects in C# and Swift. In the C# code, Owner.Dogs is the collection of linking objects, whereas in Swift, you’ve marked Dog.Owner as linking objects.

Thanks for the reply. However I am kind of confused.
We built it according to the documentation, where in the DotNet SDK the Backlink is set on User.Tasks, which in our case would be Owner.Dogs (as it is), while the Realm SDK sets the LinkingObjects on Task.assignee, which for us would be Dog.Owner if I am not mistaken.

So as far as I can tell we did according to the documentation.

I think there is some confusion about what exactly linking objects/backlinks represent. They’re essentially a computed property aggregating all objects that link to the current one via a specified property. In your example, the .NET model has them on the Owner object. So you’re asking “give me all Dog objects where Dog.Owner is set to me”. In your Swift model, on the other hand, you have it on the Dog model and you’re asking “give me all Owner objects where the Owner.Dogs list contains me”. Those are different ways of achieving the same thing, but they are still different.

In the C# case you have exactly one Owner for each Dog. At the same time, one owner may own multiple dogs as any number of dogs can have their Owner property set to the same object.

In the Swift case, one owner may have many dogs in their Dogs list. This also means that many owners may own the same dog - you can add the same Dog object to multiple Owner.Dogs lists. This also means that the same dog can exist multiple times in a single owner’s list of dogs - you can add it as many times as you want. With the Dog.owner property you can get the list of all Owner objects that have the dog in their Dogs lists. So this is inherently modeling a many-to-many relationship while the C# example is modeling a many-to-one.

To fix your immediate issue, change your models to match the C# ones:

class Owner: Object {
    @Persisted(primaryKey: true) var id: Int
    @Persisted(indexed: true) var name: String
    @Persisted(originProperty: "owner") var dogs: LinkingObjects<Dog>
}

class Dog: Object {
    @Persisted(primaryKey: true) var id: Int
    @Persisted(indexed: true) var name: String
    @Persisted var owner: Owner
}
3 Likes

Thank you so much for the solution and the nice explanation.
It seems to be working now!

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.