Model Data with Device Sync - Swift SDK
On this page
Realm Object Models
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 { true) var _id = ObjectId() (primaryKey: var name = "" var age = 0 } class Person: Object { true) var _id = ObjectId() (primaryKey: var name = "" }
JSON Schema
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.
{ "title": "Dog", "bsonType": "object", "required": [ "_id" ], "properties": { "_id": { "bsonType": "objectId" }, "name": { "bsonType": "string" }, "age": { "bsonType": "long" } } }
{ "title": "Person", "bsonType": "object", "required": [ "_id" ], "properties": { "_id": { "bsonType": "objectId" }, "name": { "bsonType": "string" } } }
Realm Relationships
To-One Relationship
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 { true) var _id = ObjectId() (primaryKey: var name = "" var age = 0 } class Person: Object { true) var _id = ObjectId() (primaryKey: var name = "" // To-one relationship var dog: Dog? }
JSON Schema
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.
{ "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
.
To-Many Relationship
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 { true) var _id = ObjectId() (primaryKey: var name = "" var age = 0 } class Person: Object { true) var _id = ObjectId() (primaryKey: var name = "" // To-many relationship - a person can have many dogs var dogs: List<Dog> }
JSON Schema
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.
{ "title": "Person", "bsonType": "object", "required": [ "_id" ], "properties": { "_id": { "bsonType": "objectId" }, "name": { "bsonType": "string" }, "dogs": { "bsonType": "array", "items": { "bsonType": "objectId" } } } }
Inverse Relationship
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 { true) var _id = ObjectId() (primaryKey: var name = "" var age = 0 // The backlink to the `Person` who has this `Dog`. "dogs") var person: LinkingObjects<Person> (originProperty: } class Person: Object { true) var _id = ObjectId() (primaryKey: var name = "" // To-many relationship - a person can have many dogs var dogs: List<Dog> }
JSON Schema
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.
{ "title": "Dog", "bsonType": "object", "required": [ "_id" ], "properties": { "_id": { "bsonType": "objectId" }, "name": { "bsonType": "string" }, "age": { "bsonType": "long" } } }
Embedded Object Models
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 { true) var id = 0 (primaryKey: var name = "" // To-many relationship - a person can have many dogs var dogs: List<Dog> // Inverse relationship - a person can be a member of many clubs "members") var clubs: LinkingObjects<DogClub> (originProperty: // Embed a single object. // Embedded object properties must be marked optional. var address: Address? convenience init(name: String, address: Address) { self.init() self.name = name self.address = address } } class DogClub: Object { var name = "" var members: List<Person> // DogClub has an array of regional office addresses. // These are embedded objects. var regionalOfficeAddresses: List<Address> convenience init(name: String, addresses: [Address]) { self.init() self.name = name self.regionalOfficeAddresses.append(objectsIn: addresses) } } class Address: EmbeddedObject { var street: String? var city: String? var country: String? var postalCode: String? }
JSON Schema
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" } } } } } }
Create Object Models and Schemas
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.