Docs Menu
Docs Home
/ /
Atlas Device SDKs

Quick Start

On this page

  • Before You Begin
  • Import the SDK
  • Define Your Object Model
  • Open a Database
  • Create, Read, Update, and Delete Objects
  • Create
  • Read and Filter
  • Update
  • Delete
  • Watch for Changes
  • Close a Database
  • Add Device Sync
  • Prerequisites
  • Initialize the App
  • Authenticate a User
  • Open a Synced Database
  • Read, Write, and React to Changes
  • Next: Check out Demos and Example Projects

This quick start demonstrates how to use Atlas Device SDK. If you'd prefer a guided quick start experience using a template app, check out our Device Sync tutorials.

Before you can get started, you must Install the SDK.

Tip

Atlas Device SDK was previously named Realm. The SDK library names still reflect Realm naming. To import Atlas Device SDK, you import Realm.

Make the C++ SDK available in your code by including the cpprealm/sdk.hpp header in the translation unit where you want to use it:

Add the following line to the top of your source files to use the SDK:

Tip

Standalone Dart or Flutter Project?

This quick start contains information for using the SDK with a standalone Dart project. The package that you import, and the way you create object models, differs if you are using the SDK in a Flutter project. For a quick start using Flutter, refer to Quick Start with Flutter.

Import the package into any files where you use it.

At the top of your source files where you want to use the SDK, add the following line:

Note

Using this Quick Start with KMP

If you're following this quick start in a fresh Kotlin Multiplatform (KMP) template project, you can copy and paste the snippets into the Greeting.greeting() method in the commonMain module.

At the top of your source files where you want to use the SDK, add the appropriate imports. For this quick start, we use these imports:

Tip

If your app uses SwiftUI, check out the SwiftUI Quick Start.

Near the top of any Swift file that uses the SDK, add the following import statement:

At the top of your source files where you want to use the SDK, add the following line:

#include <cpprealm/sdk.hpp>
using Realms;
import 'package:realm_dart/realm.dart';
import Realm, { ObjectSchema } from "realm";
// The documentation does not currently have this code example in Kotlin.
// Please refer to the other languages or related pages for example code.
import RealmSwift
import Realm, { ObjectSchema } from "realm";

You can define your object model directly in code. If you want to use Device Sync, your client object models also require a matching schema in Atlas. For more details, refer to Model Data with Device Sync.

This quick start includes ownerId, which is used when we add Device Sync in a later step. You can remove this property if you are not using Device Sync.

Important

Define SDK Models within the realm namespace

The C++ SDK requires you to define the object models that you want to store in the database within the realm namespace.

Important

Inheritance

All SDK objects inherit from the IRealmObject, IEmbeddedObject, or IAsymmetricObject interface and must be declared partial classes.

In versions of the .NET SDK v10.18.0 and earlier, objects derive from RealmObject, EmbeddedObject, or AsymmetricObject base classes. This approach to SDK model definition is still supported, but does not include new features such as the nullability annotations. These base classes will be deprecated in a future SDK release. You should use the interfaces for any new classes that you write and should consider migrating your existing classes.

The following code shows how to define an object model for an Item object. In this example, we have marked the Id field as the Primary Key and marked the Status property as optional. We've also chosen to use the MapTo attribute; properties will be stored in lower case on the server, but can use .NET-friendly casing on our property names when using Device Sync.

Your application's data model defines the structure of data stored within the database. You can define your application's data model via Dart classes in your application code with an SDK object schema. You then have to generate the RealmObjectBase class that's used within your application.

For more information, refer to Define an Object Model.

Create a Model Class

Add an SDK model class. Give your class a private name (starting with _), such as a file car.dart with a class _Car.

Generate an SDK Object Class

Generate a RealmObject class Car from the data model class _Car:

dart run realm_dart generate

Running this creates a Car class in a car.realm.dart file located in the directory where you defined the model class. This Car class is public and part of the same library as the _Car data model class. The generated Car class is what's used throughout your application.

Watch for Changes to the Model (Optional)

You can watch your data model class to generate a new Car class whenever there's a change to _Car:

dart run realm_dart generate --watch

To define an SDK object type, create a schema object that specifies the type's name and properties. The type name must be unique among object types in the database.

The following code shows how to define an object model for a Task object. In this example:

  • The primaryKey is the _id of type int. Another common type used for primary keys is ObjectId.

  • The name field is required.

  • The status and owner_id fields are optional, denoted by the question mark immediately after the data type.

To define your application's data model, add a class definition to your application code. The example below illustrates the creation of an Item model that represents Todo items in a Todo list app.

This quick start includes ownerId, which is used when we add Device Sync in a later step. You can remove this property if you are not using Device Sync.

To define an SDK object type, create a schema object that specifies the type's name and properties. The type name must be unique among object types in the database.

The following code shows how to define an object model for a Task object. In this example:

  • The primaryKey is the _id of type int. Another common type used for primary keys is ObjectId.

  • The name field is required.

  • The status and owner_id fields are optional, denoted by the question mark immediately after the data type.

namespace realm {
struct Todo {
realm::primary_key<realm::object_id> _id{realm::object_id::generate()};
std::string name;
std::string status;
// The ownerId property stores the user.identifier() of a
// logged-in user. Omit this property for the non-sync example.
std::string ownerId;
};
REALM_SCHEMA(Todo, _id, name, status, ownerId);
} // namespace realm
public partial class Item : IRealmObject
{
[PrimaryKey]
[MapTo("_id")]
public ObjectId Id { get; set; } = ObjectId.GenerateNewId();
[MapTo("assignee")]
public string Assignee { get; set; }
[MapTo("name")]
public string? Name { get; set; }
[MapTo("status")]
public string? Status { get; set; }
}
car.dart
import 'package:realm_dart/realm.dart';
part 'car.realm.dart';
@RealmModel()
class _Car {
@PrimaryKey()
late ObjectId id;
late String make;
late String? model;
late int? miles;
}
class Task extends Realm.Object {
static schema = {
name: "Task",
properties: {
_id: "int",
name: "string",
status: "string?",
owner_id: "string?",
},
primaryKey: "_id",
};
}
class Item() : RealmObject {
@PrimaryKey
var _id: ObjectId = ObjectId()
var isComplete: Boolean = false
var summary: String = ""
var owner_id: String = ""
constructor(ownerId: String = "") : this() {
owner_id = ownerId
}
}
class Todo: Object {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var name: String = ""
@Persisted var status: String = ""
@Persisted var ownerId: String
convenience init(name: String, ownerId: String) {
self.init()
self.name = name
self.ownerId = ownerId
}
}
class Task extends Realm.Object<Task> {
_id!: number;
name!: string;
status?: string;
owner_id?: string;
static schema: ObjectSchema = {
name: "Task",
properties: {
_id: "int",
name: "string",
status: "string?",
owner_id: "string?",
},
primaryKey: "_id",
};
}

When you open a database, you must specify a db_config. You can optionally open a database at a specific path, or provide a sync_config to open a synced database.

Open a database with either the Realm.GetInstance() or Realm.GetInstanceAsync() method. Which method you use depends entirely on if and how you are using asynchronous patterns in your app. The following code shows how to use GetInstance():

Use the Configuration class to define the specifics of the database instance, including schema and whether the database is non-synced or synced.

Pass your configuration to the database constructor to generate an instance of that database:

To open a database, pass a Realm.BaseConfiguration object to Realm.open().

Use RealmConfiguration.create() to open a database using default parameters. Pass your configuration to the factory constructor to generate an instance of that database.

You can optionally define additional RealmConfiguration details, such as name, location, schema version, and more.

In a non-synced database, the simplest option to open the database is to use the default database with no configuration parameter, as shown in the example below.

You can also specify a Realm.Configuration parameter to open a database at a specific file URL, in-memory, or with a subset of classes.

To open a database, pass a Realm.BaseConfiguration object to Realm.open().

auto config = realm::db_config();
auto realm = realm::db(std::move(config));
var realm = Realm.GetInstance();
final config = Configuration.local([Car.schema]);
final realm = Realm(config);
class Task extends Realm.Object {
static schema = {
name: "Task",
properties: {
_id: "int",
name: "string",
status: "string?",
owner_id: "string?",
},
primaryKey: "_id",
};
}
const realm = await Realm.open({
schema: [Task],
});
val config = RealmConfiguration.create(schema = setOf(Item::class))
val realm: Realm = Realm.open(config)
// Open the local-only default realm
let realm = try! Realm()
class Task extends Realm.Object<Task> {
_id!: number;
name!: string;
status?: string;
owner_id?: string;
static schema: ObjectSchema = {
name: "Task",
properties: {
_id: "int",
name: "string",
status: "string?",
owner_id: "string?",
},
primaryKey: "_id",
};
}
const realm = await Realm.open({
schema: [Task],
});

For more information, refer to Configure & Open a Database File.

Once you have opened a database, you can modify it and its objects in a write transaction block.

To instantiate a new object and add it to the database in a write block:

auto todo = realm::Todo{.name = "Create my first todo item",
.status = "In Progress"};
realm.write([&] { realm.add(std::move(todo)); });
var testItem = new Item
{
Name = "Do this thing",
Status = ItemStatus.Open.ToString(),
Assignee = "Aimee"
};
await realm.WriteAsync(() =>
{
realm.Add(testItem);
});
// Or
var testItem2 =
await realm.WriteAsync(() =>
{
return realm.Add<Item>(new Item
{
Name = "Do this thing, too",
Status = ItemStatus.InProgress.ToString(),
Assignee = "Satya"
});
}
);
final car = Car(ObjectId(), 'Tesla', model: 'Model S', miles: 42);
realm.write(() {
realm.add(car);
});
// The documentation does not currently have this code example in JavaScript.
// Please refer to the other languages or related pages for example code.
realm.writeBlocking {
copyToRealm(Item().apply {
summary = "Do the laundry"
isComplete = false
})
}
let todo = Todo(name: "Do laundry", ownerId: user.id)
try! realm.write {
realm.add(todo)
}
const allTasks = realm.objects(Task);
// Add a couple of Tasks in a single, atomic transaction.
realm.write(() => {
realm.create(Task, {
_id: 1,
name: "go grocery shopping",
status: "Open",
});
realm.create(Task, {
_id: 2,
name: "go exercise",
status: "Open",
});
});
const task1 = allTasks.find((task) => task._id == 1);
const task2 = allTasks.find((task) => task._id == 2);
realm.write(() => {
// Modify an object.
task1!.status = "InProgress";
// Delete an object.
realm.delete(task2);
});

For more information, refer to Create Objects.

To retrieve a results collection of all objects of a given type in the database:

auto todos = realm.objects<realm::Todo>();
var allItems = realm.All<Item>();
final cars = realm.all<Car>();
final myCar = cars[0];
print('My car is ${myCar.make} ${myCar.model}');
// The documentation does not currently have this code example in JavaScript.
// Please refer to the other languages or related pages for example code.
// all items in the realm
val items: RealmResults<Item> = realm.query<Item>().find()
// Get all todos in the realm
let todos = realm.objects(Todo.self)
// Query for specific obect using primary key.
const specificTask = realm.objectForPrimaryKey(Task, 0);
// Query realm for all instances of the "Task" type.
const tasks = realm.objects(Task);
// Filter for all tasks with a status of "Open".
const openTasks = tasks.filtered("status = 'Open'");
// Sort tasks by name in ascending order.
const tasksByName = tasks.sorted("name");

For more information, refer to Read Objects.

To filter that same results collection:

auto todosInProgress = todos.where(
[](auto const& todo) { return todo.status == "In Progress"; });
var openItems = realm.All<Item>()
.Where(i => i.Status == "Open");
final cars = realm.query<Car>('make == "Tesla"');
// The documentation does not currently have this code example in JavaScript.
// Please refer to the other languages or related pages for example code.
// items in the realm whose name begins with the letter 'D'
val itemsThatBeginWIthD: RealmResults<Item> =
realm.query<Item>("summary BEGINSWITH $0", "D")
.find()
// todo items that have not been completed yet
val incompleteItems: RealmResults<Item> =
realm.query<Item>("isComplete == false")
.find()
let todosInProgress = todos.where {
$0.status == "InProgress"
}
print("A list of all todos in progress: \(todosInProgress)")
// The documentation does not currently have this code example in TypeScript.
// Please refer to the other languages or related pages for example code.

For more information about the SDK query engines, refer to Filter Data.

To modify an object, update its properties in a write transaction block:

auto todoToUpdate = todosInProgress[0];
realm.write([&] { todoToUpdate.status = "Complete"; });
var id = ObjectId.GenerateNewId();
var item1 = new Item
{
Id = id,
Name = "Defibrillate the Master Oscillator",
Assignee = "Aimee"
};
// Add a new person to the realm. Since nobody with the existing Id
// has been added yet, this person is added.
await realm.WriteAsync(() =>
{
realm.Add(item1, update: true);
});
var item2 = new Item
{
Id = id,
Name = "Fluxify the Turbo Encabulator",
Assignee = "Aimee"
};
// Based on the unique Id field, we have an existing person,
// but with a different name. When `update` is true, you overwrite
// the original entry.
await realm.WriteAsync(() =>
{
realm.Add(item2, update: true);
});
// item1 now has a Name of "Fluxify the Turbo Encabulator"
// and item2 was not added as a new Item in the collection.
realm.write(() {
car.miles = 99;
});
// The documentation does not currently have this code example in JavaScript.
// Please refer to the other languages or related pages for example code.
// change the first item with open status to complete to show that the todo item has been done
realm.writeBlocking {
findLatest(incompleteItems[0])?.isComplete = true
}
// All modifications to a realm must happen in a write block.
let todoToUpdate = todos[0]
try! realm.write {
todoToUpdate.status = "InProgress"
}
// The documentation does not currently have this code example in TypeScript.
// Please refer to the other languages or related pages for example code.

For more information, refer to Update Objects.

To delete an object from the database:

realm.write([&] { realm.remove(specificTodo); });
realm.Write(() =>
{
realm.Remove(myItem);
});
realm.Write(() =>
{
realm.RemoveAll<Item>();
});
realm.write(() {
realm.delete(car);
});
// The documentation does not currently have this code example in JavaScript.
// Please refer to the other languages or related pages for example code.
// delete the first item in the realm
realm.writeBlocking {
val writeTransactionItems = query<Item>().find()
delete(writeTransactionItems.first())
}
// All modifications to a realm must happen in a write block.
let todoToDelete = todos[0]
try! realm.write {
// Delete the Todo.
realm.delete(todoToDelete)
}
// The documentation does not currently have this code example in TypeScript.
// Please refer to the other languages or related pages for example code.

For more information, refer to Delete Objects and Property Values.

You can watch a database, a collection, or an object for changes:

auto token = specificTodo.observe([&](auto&& change) {
try {
if (change.error) {
rethrow_exception(change.error);
}
if (change.is_deleted) {
std::cout << "The object was deleted.\n";
} else {
for (auto& propertyChange : change.property_changes) {
std::cout << "The object's " << propertyChange.name
<< " property has changed.\n";
}
}
} catch (std::exception const& e) {
std::cerr << "Error: " << e.what() << "\n";
}
});
// Observe realm notifications.
realm.RealmChanged += (sender, eventArgs) =>
{
// The "sender" object is the realm that has changed.
// "eventArgs" is reserved for future use.
// ... update UI ...
};
// Listen for changes on whole collection
final characters = realm.all<Character>();
final subscription = characters.changes.listen((changes) {
changes.inserted; // Indexes of inserted objects.
changes.modified; // Indexes of modified objects.
changes.deleted; // Indexes of deleted objects.
changes.newModified; // Indexes of modified objects after accounting for deletions & insertions.
changes.moved; // Indexes of moved objects.
changes.results; // The full List of objects.
changes.isCleared; // `true` after call to characters.clear(); otherwise, `false`.
});
// Listen for changes on RealmResults.
final hobbits = fellowshipOfTheRing.members.query('species == "Hobbit"');
final hobbitsSubscription = hobbits.changes.listen((changes) {
// ... all the same data as above
});
// Define the collection notification listener.
const listener = (tasks, changes) => {
// Update UI in response to deleted objects.
changes.deletions.forEach((index) => {
// Deleted objects cannot be accessed directly,
// but we can update a UI list, etc. knowing the index.
console.log(`A task was deleted at the ${index} index.`);
// ...
});
// Update UI in response to inserted objects.
changes.insertions.forEach((index) => {
const insertedTasks = tasks[index];
console.log(`insertedTasks: ${JSON.stringify(insertedTasks, null, 2)}`);
// ...
});
// Update UI in response to modified objects.
// `newModifications` contains an index to the modified object's position
// in the collection after all deletions and insertions have been applied.
changes.newModifications.forEach((index) => {
const modifiedTask = tasks[index];
console.log(`modifiedTask: ${JSON.stringify(modifiedTask, null, 2)}`);
// ...
});
};
// Observe collection notifications.
tasks.addListener(listener);
// flow.collect() is blocking -- run it in a background context
val job = CoroutineScope(Dispatchers.Default).launch {
// create a Flow from the Item collection, then add a listener to the Flow
val itemsFlow = items.asFlow()
itemsFlow.collect { changes: ResultsChange<Item> ->
when (changes) {
// UpdatedResults means this change represents an update/insert/delete operation
is UpdatedResults -> {
changes.insertions // indexes of inserted objects
changes.insertionRanges // ranges of inserted objects
changes.changes // indexes of modified objects
changes.changeRanges // ranges of modified objects
changes.deletions // indexes of deleted objects
changes.deletionRanges // ranges of deleted objects
changes.list // the full collection of objects
}
else -> {
// types other than UpdatedResults are not changes -- ignore them
}
}
}
}
// Retain notificationToken as long as you want to observe
let notificationToken = todos.observe { (changes) in
switch changes {
case .initial: break
// Results are now populated and can be accessed without blocking the UI
case .update(_, let deletions, let insertions, let modifications):
// Query results have changed.
print("Deleted indices: ", deletions)
print("Inserted indices: ", insertions)
print("Modified modifications: ", modifications)
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
}
}
// Define the collection notification listener.
const listener = (
tasks: Realm.Collection<Task>,
changes: Realm.CollectionChangeSet
) => {
// Update UI in response to deleted objects.
changes.deletions.forEach((index) => {
// Deleted objects cannot be accessed directly,
// but we can update a UI list, etc. knowing the index.
console.log(`A task was deleted at the ${index} index.`);
// ...
});
// Update UI in response to inserted objects.
changes.insertions.forEach((index) => {
const insertedTasks = tasks[index];
console.log(`insertedTasks: ${JSON.stringify(insertedTasks, null, 2)}`);
// ...
});
// Update UI in response to modified objects.
// `newModifications` contains an index to the modified object's position
// in the collection after all deletions and insertions have been applied.
changes.newModifications.forEach((index) => {
const modifiedTask = tasks[index];
console.log(`modifiedTask: ${JSON.stringify(modifiedTask, null, 2)}`);
// ...
});
};
// Observe collection notifications.
tasks.addListener(listener);

For more information about reacting to changes, including details about unregistering the listener to stop watching and free up resources, refer to React to Changes.

To close a database and release all underlying resources, call db::close(). Closing the database invalidates any remaining objects.

The database instance implements IDisposable to ensure native resources are freed up. You should dispose of a database object immediately after use, especially on background threads. The simplest way to do this is by declaring the database object with a using statement, or wrapping the code that interacts with a database in a using (...) statement:

config = new PartitionSyncConfiguration("myPart", user);
using (var realm = Realm.GetInstance(config))
{
var allItems = realm.All<Item>();
}

If you require a database object to be shared outside of a single method, you can manage its state manually. Call the Dispose() method to release the reference:

Once you've finished working with a database, close it to prevent memory leaks.

Call the realm.close() method when done with a database instance to avoid memory leaks.

To close a database and release all underlying resources, call realm.close(). The close() method blocks until all write transactions on the database have completed.

Unlike the other SDKs, there is no need to manually close a database in Swift or Objective-C. When a database goes out of scope and is removed from memory due to ARC, the database is automatically closed.

Call the realm.close() method when done with a database instance to avoid memory leaks.

realm.close();
realm.Dispose();
realm.close();
// The documentation does not currently have this code example in JavaScript.
// Please refer to the other languages or related pages for example code.
realm.close()
// The Swift SDK does not currently support this API.
// Close the realm.
realm.close();

If you want to sync data across devices with the SDK, you can enable Device Sync in Atlas. For more information on Device Sync, refer to Atlas Device Sync in the App Services documentation.

Before you can sync device data, you must:

The SDK uses an App to connect to Atlas, manage users, and report certain types of errors.

To use App Services features such as authentication and sync, access your App Services App using your App ID. You can find your App ID in the App Services UI.

To initialize the App connection:

auto appConfig = realm::App::configuration();
appConfig.app_id = APP_ID;
auto app = realm::App(appConfig);
app = App.Create(myRealmAppId);
final app = App(AppConfiguration(APP_ID));
// The documentation does not currently have this code example in JavaScript.
// Please refer to the other languages or related pages for example code.
val app = App.create(YOUR_APP_ID)
let app = App(id: APP_ID) // Replace APP_ID with your Atlas App ID
// Initialize your App.
const app = new Realm.App({
id: "<yourAppId>",
});

For more details about configuring and initializing the App connection, refer to Connect to Atlas.

This quick start uses anonymous authentication to log in users without requiring them to provide any identifying information. After authenticating the user, you can open a database for that user.

auto user = app.login(realm::App::credentials::anonymous()).get();
var user = await app.LogInAsync(Credentials.Anonymous());
final loggedInUser = await app.logIn(Credentials.anonymous());
// The documentation does not currently have this code example in JavaScript.
// Please refer to the other languages or related pages for example code.
val credentials = Credentials.anonymous()
val user = app.login(credentials)
do {
let user = try await app.login(credentials: Credentials.anonymous)
print("Successfully logged in user: \(user)")
await openSyncedRealm(user: user)
} catch {
print("Error logging in: \(error.localizedDescription)")
}
// Initialize your App.
const app = new Realm.App({
id: "<yourAppId>",
});
// Authenticate an anonymous user.
const anonymousUser = await app.logIn(Realm.Credentials.anonymous());

The SDK provides additional ways to authenticate, register, and link users. For other authentication providers, refer to Authenticate Users.

Once you have enabled Device Sync and authenticated a user, you can create a sync_configuration object and open the database. You can then add a Sync subscription that determines what data the database can read and write.

Once you have enabled Device Sync and authenticated a user, you can open a synced database. Use a FlexibleSyncConfiguration object to define the specifics of how your application synchronizes data with App Services. Then, add a Sync subscription that determines what data the user can read and write.

Once you have enabled Device Sync and authenticated a user, create a Configuration.flexibleSync() object. Then, pass the configuration to Realm() to open an instance of the database. The synced database must have a different Configuration.path from other opened non-synced databases.

final config = Configuration.flexibleSync(loggedInUser, [Todo.schema]);
final realm = Realm(
config,
);

Now create a subscription to synchronize data with Atlas using Device Sync. Add the subscription within the SubscriptionSet.update() callback function.

The update block callback function includes a MutableSubscriptionSet() object as an argument. Use MutableSubscriptionSet.add() to add a new subscription.

After you have initialized your App, authenticated a user, and defined your object model, you can create a SyncConfiguration.

To open a synced database, call Realm.open(). Pass in a BaseConfiguration object, which must include the sync property defining a SyncConfiguration object. In the SyncConfiguration, you must include include a user and set flexible: true.

Additionally, you need at least one subscription before you can read from or write to the database. Use Configuration.sync.initialSubscriptions to define the initial subscription set when the database file is first opened.

Once you have initialized your Atlas App Services App, authenticated a user, and defined your object model, you can create a SyncConfiguration.

If you have opened a non-synced database following the Open a Database section on this page, replace the RealmConfiguration with the SyncConfiguration described below.

Pass the authenticated user and the Item class to the SyncConfiguration.Builder function to create SyncConfiguration.

Important

Initial Subscriptions

You need at least one subscription before you can read from or write to the database. Use initialSubscriptions to define the initial subscription set when you first open the database file. Pass the query you wish to subscribe to and a name for the subscription to the add() function.

The example below specifies a subscription named "User's Items" with all Item objects.

Once you have enabled Device Sync and authenticated a user, you can create a Configuration object and open the database. You can then add a the Sync subscription that determines what data the database can read and write.

Once you have a database with a subscription, this example passes the database and the user to another function where you can use the database.

After you have initialized your App, authenticated a user, and defined your object model, you can create a SyncConfiguration.

To open a synced database, call Realm.open(). Pass in a BaseConfiguration object, which must include the sync property defining a SyncConfiguration object. In the SyncConfiguration, you must include include a user and set flexible: true.

Additionally, you need at least one subscription before you can read from or write to the database. Use Configuration.sync.initialSubscriptions to define the initial subscription set when the database file is first opened.

auto syncConfig = user.flexible_sync_configuration();
auto realm = realm::db(syncConfig);
// For this example, get the userId for the Flexible Sync query
auto userId = user.identifier();
auto subscriptions = realm.subscriptions();
auto updateSubscriptionSuccess =
subscriptions
.update([&](realm::mutable_sync_subscription_set& subs) {
subs.add<realm::Todo>("todos", [&userId](auto& obj) {
// For this example, get only Todo items where the ownerId
// property value is equal to the userId of the logged-in user.
return obj.ownerId == userId;
});
})
.get();
var config = new FlexibleSyncConfiguration(app.CurrentUser)
{
PopulateInitialSubscriptions = (realm) =>
{
var myItems = realm.All<Item>().Where(n => n.OwnerId == myUserId);
realm.Subscriptions.Add(myItems);
}
};
// The process will complete when all the user's items have been downloaded.
var realm = await Realm.GetInstanceAsync(config);
// Check if the subscription already exists before adding
final userTodoSub = realm.subscriptions.findByName('getUserTodos');
if (userTodoSub == null) {
realm.subscriptions.update((mutableSubscriptions) {
// server-side rules ensure user only downloads their own Todos
mutableSubscriptions.add(realm.all<Todo>(), name: 'getUserTodos');
});
}
// Initialize your App.
const app = new Realm.App({
id: "<yourAppId>",
});
// Authenticate an anonymous user.
const anonymousUser = await app.logIn(Realm.Credentials.anonymous());
// Define an object model
class Task extends Realm.Object {
static schema = {
name: "Task",
properties: {
_id: "int",
name: "string",
status: "string?",
progressMinutes: "int?",
owner_id: "string?",
dueDate: "date?",
},
primaryKey: "_id",
};
}
// Create a `SyncConfiguration` object.
const config = {
schema: [Task],
sync: {
// Use the previously-authenticated anonymous user.
user: anonymousUser,
// Set flexible sync to true to enable sync.
flexible: true,
// Define initial subscriptions to start syncing data as soon as the
// realm is opened.
initialSubscriptions: {
update: (subs, realm) => {
subs.add(
// Get objects that match your object model, then filter them by
// the `owner_id` queryable field
realm.objects(Task).filtered(`owner_id == "${anonymousUser.id}"`)
);
},
},
},
};
const realm = await Realm.open(config);
// create a SyncConfiguration
val config = SyncConfiguration.Builder(
user,
setOf(Item::class)
) // the SyncConfiguration defaults to Flexible Sync, if a Partition is not specified
.initialSubscriptions { realm ->
add(
realm.query<Item>(
"owner_id == $0", // owner_id == the logged in user
user.id
),
"User's Items"
)
}
.build()
val realm = Realm.open(config)
// Opening a realm and accessing it must be done from the same thread.
// Marking this function as `@MainActor` avoids threading-related issues.
@MainActor
func openSyncedRealm(user: User) async {
do {
var config = user.flexibleSyncConfiguration()
// Pass object types to the Flexible Sync configuration
// as a temporary workaround for not being able to add a
// complete schema for a Flexible Sync app.
config.objectTypes = [Todo.self]
let realm = try await Realm(configuration: config, downloadBeforeOpen: .always)
// You must add at least one subscription to read and write from a Flexible Sync realm
let subscriptions = realm.subscriptions
try await subscriptions.update {
subscriptions.append(
QuerySubscription<Todo> {
$0.ownerId == user.id
})
}
await useRealm(realm: realm, user: user)
} catch {
print("Error opening realm: \(error.localizedDescription)")
}
}
// Initialize your App.
const app = new Realm.App({
id: "<yourAppId>",
});
// Authenticate an anonymous user.
const anonymousUser = await app.logIn(Realm.Credentials.anonymous());
// Define an object model
class Task extends Realm.Object<Task> {
_id!: number;
name!: string;
status?: string;
progressMinutes?: string;
owner_id?: string;
dueDate?: Date;
static schema: ObjectSchema = {
name: "Task",
properties: {
_id: "int",
name: "string",
status: "string?",
progressMinutes: "int?",
owner_id: "string?",
dueDate: "date?",
},
primaryKey: "_id",
};
}
// Create a `SyncConfiguration` object.
const config: Realm.Configuration = {
schema: [Task],
sync: {
// Use the previously-authenticated anonymous user.
user: anonymousUser,
// Set flexible sync to true to enable sync.
flexible: true,
// Define initial subscriptions to start syncing data as soon as the
// realm is opened.
initialSubscriptions: {
update: (subs, realm) => {
subs.add(
// Get objects that match your object model, then filter them by
// the `owner_id` queryable field
realm.objects(Task).filtered(`owner_id == "${anonymousUser.id}"`)
);
},
},
},
};
const realm = await Realm.open(config);

For more details about opening a synced database, refer to Configure & Open a Synced Database.

For more details about Sync subscriptions, refer to Manage Sync Subscriptions.

The syntax to read, write, and watch for changes on a synced database is identical to the syntax for non-synced databases above.

However, reads and writes to a synced databases have the following additional constraints:

  • Sync permissions that determine whether users can read or write.

  • Sync subscriptions that determine what data the synced database can read or write.

In the following example, we set Sync permissions to Users can only read and write their own data, and add a property that lets us subscribe to only the current user's todos. Users can only read and write their own data.

For more information about Device Sync permissions, refer to Role-based Permissions in the App Services documentation.

In this example, we store the user.identifier() of the logged-in user in the ownerId property of the Todo item. We then use this property in subscription queries and the Device Sync permissions.

auto todo = realm::Todo{.name = "Create a Sync todo item",
.status = "In Progress",
.ownerId = userId};
realm.write([&] { realm.add(std::move(todo)); });
auto todos = realm.objects<realm::Todo>();
// The documentation does not currently have this code example in C#.
// Please refer to the other languages or related pages for example code.
// The documentation does not currently have this code example in Dart.
// Please refer to the other languages or related pages for example code.
// The documentation does not currently have this code example in JavaScript.
// Please refer to the other languages or related pages for example code.
// The documentation does not currently have this code example in Kotlin.
// Please refer to the other languages or related pages for example code.
// The documentation does not currently have this code example in Swift.
// Please refer to the other languages or related pages for example code.
// The documentation does not currently have this code example in TypeScript.
// Please refer to the other languages or related pages for example code.

While you work with local data, a background thread efficiently integrates, uploads, and downloads changesets.

Every write transaction for a subscription set has a performance cost. If you need to make multiple updates to a database object during a session, consider keeping edited objects in memory until all changes are complete. This improves sync performance by only writing the complete and updated object to your database instead of every change.

Check out the template apps to see an implementation of a platform-specific Atlas Device SDK application. Each SDK has an application that integrates Atlas Device SDK and Atlas Device Sync in a platform-idiomatic todo app.

Check out our list of curated Atlas Device SDK Example Projects to browse example applications for specific use cases and implementations.

Back

Install

Next

Model Data