To define a Realm object, derive a class from Object. The models that you define each map to their own collection in MongoDB Atlas. In this example, both Dog
and Person
would map to separate collections in MongoDB Atlas.
class Dog: Object { @Persisted(primaryKey: true) var _id = ObjectId() @Persisted var name = "" @Persisted var age = 0}class Person: Object { @Persisted(primaryKey: true) var _id = ObjectId() @Persisted var name = ""}
When you have a corresponding schema in Atlas App Services, Device Sync automatically converts the Realm Swift data types to BSON as it syncs to Atlas. When the client device syncs the data down from App Services via Device Sync, the SDK converts BSON back to Swift objects. If you query for these objects by accessing MongoDB Atlas data directly instead of using Device Sync to sync the data, you get the BSON data types. MongoDB Data Access does not automatically map to the Swift object equivalents.
Dog Schema in App Services
{ "title": "Dog", "bsonType": "object", "required": [ "_id" ], "properties": { "_id": { "bsonType": "objectId" }, "name": { "bsonType": "string" }, "age": { "bsonType": "long" } }}
Person Schema in App Services
{ "title": "Person", "bsonType": "object", "required": [ "_id" ], "properties": { "_id": { "bsonType": "objectId" }, "name": { "bsonType": "string" } }}
Using the objects in the example above, consider a case where a Person
can have one Dog
. We can add a dog
property to our Person
model that is an optional link to a Dog
object. To-one relationships must be optional.
class Dog: Object { @Persisted(primaryKey: true) var _id = ObjectId() @Persisted var name = "" @Persisted var age = 0}class Person: Object { @Persisted(primaryKey: true) var _id = ObjectId() @Persisted var name = "" @Persisted var dog: Dog?}
In the App Services schema, we see the new property translates to a field dog
. The field is not in the required
array because it is an optional property. Its type is an objectId
which links to a specific Dog
object in the separate Dog
collection. The objectId is the primary key of the Dog
model, so it's the field that links the two objects.
Person with To-One Relationship to Dog
{ "title": "Person", "bsonType": "object", "required": [ "_id" ], "properties": { "_id": { "bsonType": "objectId" }, "dog": { "bsonType": "objectId" }, "name": { "bsonType": "string" } }}
The Dog
schema doesn't change. Because this is a to-one relationship, it's a one-way relationship; the Dog
has no relationship back to Person
.
Using the objects in the example above, consider a case where a Person
can have many dogs. We can add a dogs
property to our Person
model that is a list of Dog
objects. If the person has no dogs, this is an empty list. As the person gets dogs, we can create new dog objects and append them to the person's dogs
list.
class Dog: Object { @Persisted(primaryKey: true) var _id = ObjectId() @Persisted var name = "" @Persisted var age = 0}class Person: Object { @Persisted(primaryKey: true) var _id = ObjectId() @Persisted var name = "" @Persisted var dogs: List<Dog>}
In the App Services schema, we see the new property translates to a field dogs
. The type of this field is an array, the items in the array are of type objectId
. This is because we defined the primary key on our Dog
model as an objectId
. This field is an array of the primary keys of all of the Dog
objects related to the Person
object.
Person with To-Many Relationship to Dog
{ "title": "Person", "bsonType": "object", "required": [ "_id" ], "properties": { "_id": { "bsonType": "objectId" }, "name": { "bsonType": "string" }, "dogs": { "bsonType": "array", "items": { "bsonType": "objectId" } } }}
Using the objects in the example above, consider a case where the Dog
object has an inverse relationship to the Person
object.
class Dog: Object { @Persisted(primaryKey: true) var _id = ObjectId() @Persisted var name = "" @Persisted var age = 0 @Persisted(originProperty: "dogs") var person: LinkingObjects<Person>}class Person: Object { @Persisted(primaryKey: true) var _id = ObjectId() @Persisted var name = "" @Persisted var dogs: List<Dog>}
In the App Services schema, we see that the person
property that represents the inverse relationship to a Person
from our Dog
model is not present. You can't directly set the value of an inverse relationship, and that relationship does not exist in Atlas. However, Realm derives and updates those relationships for you in the client application based on your Realm object model.
Dog with Inverse Relationship to Person
{ "title": "Dog", "bsonType": "object", "required": [ "_id" ], "properties": { "_id": { "bsonType": "objectId" }, "name": { "bsonType": "string" }, "age": { "bsonType": "long" } }}
When you define an embedded object with the Realm Swift SDK, you derive a class from EmbeddedObject. You can reference an embedded object type from parent object types in the same way as you would define a relationship:
class Person: Object { @Persisted(primaryKey: true) var id = 0 @Persisted var name = "" @Persisted var dogs: List<Dog> @Persisted(originProperty: "members") var clubs: LinkingObjects<DogClub> @Persisted var address: Address? convenience init(name: String, address: Address) { self.init() self.name = name self.address = address }}class DogClub: Object { @Persisted var name = "" @Persisted var members: List<Person> @Persisted var regionalOfficeAddresses: List<Address> convenience init(name: String, addresses: [Address]) { self.init() self.name = name self.regionalOfficeAddresses.append(objectsIn: addresses) }}class Address: EmbeddedObject { @Persisted var street: String? @Persisted var city: String? @Persisted var country: String? @Persisted var postalCode: String?}
Embedded objects map to embedded documents in the parent type's schema. This behavior differs from regular Realm objects, which map to their own MongoDB collection.
{ "title": "Person", "bsonType": "object", "required": ["id"], "properties": { "id": { "bsonType": "int" }, "name": { "bsonType": "string" }, "dogs": { "bsonType": "array", "items": { "bsonType": "objectId" } }, "address": { "title": "Address", "bsonType": "object", "properties": { "street": { "bsonType": "string" }, "city": { "bsonType": "string" }, "country": { "bsonType": "string" }, "postalCode": { "bsonType": "string" } } } }}
{ "title": "DogClub", "bsonType": "object", "required": ["_id", "name", "addresses"], "properties": { "_id": "objectId", "name": { "bsonType": "string" }, "members": { "bsonType": "array", "items": { "bsonType": "objectId" } }, "addresses": { "bsonType": "array", "items": { "title": "Address", "bsonType": "object", "properties": { "street": { "bsonType": "string" }, "city": { "bsonType": "string" }, "country": { "bsonType": "string" }, "postalCode": { "bsonType": "string" } } } } }}
To map between SDK object models and BSON data in Atlas, you must have a schema in App Services that matches your SDK object model. There are a few ways you can generate matching schema and object models:
Create object models in the client application, and generate App Services schemas from them.
Create schemas in App Services, and generate Realm object models from them.
If you are developing a new client application, you likely want to iterate on the data model in the client application. Enable Development Mode in App Services to have the backend infer and update the schema based on your client object models. Development Mode does not work with breaking schema changes, so you must remove the existing schema from the server when you make breaking changes to the SDK data model.
If you are developing a client application that works with data that already exists in Atlas, you can generate a schema from that data. Then, you can generate SDK object models from the server-side schema.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4