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 Begin
Before you can get started, you must Install the SDK.
Import 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:
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";
Define Your Object Model
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 typeint
. Another common type used for primary keys isObjectId
.The
name
field is required.The
status
andowner_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 typeint
. Another common type used for primary keys isObjectId
.The
name
field is required.The
status
andowner_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 { [ ] [ ] public ObjectId Id { get; set; } = ObjectId.GenerateNewId(); [ ] public string Assignee { get; set; } [ ] public string? Name { get; set; } [ ] public string? Status { get; set; } }
import 'package:realm_dart/realm.dart'; part 'car.realm.dart'; ()class _Car { () 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 { 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 { true) var _id: ObjectId (primaryKey: var name: String = "" var status: String = "" 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", }; }
Open a Database
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.
Create, Read, Update, and Delete Objects
Once you have opened a database, you can modify it and its objects in a write transaction block.
Create
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.
Read and Filter
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.
Update
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.
Delete
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.
Watch for Changes
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.
Close a Database
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();
Add Device Sync
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.
Prerequisites
Before you can sync device data, you must:
Enable Device Sync with Development Mode toggled to
On
.Define the rules that determine which permissions users have when using Device Sync.
Initialize the App
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.
Authenticate a User
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.
Open a Synced Database
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. 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.
Read, Write, and React to Changes
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.
Next: Check out Demos and Example Projects
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.