Creating and Opening Realms

Key to Realm Platform is the concept of a synchronized Realm. This guide discusses how to get started with a synced Realm.

The Default Synced Realm

Every instance of the Realm Object Server has a single default Realm upon creation. The purpose of the default synchronized Realm is to simplify getting started and allow your application data to link into the master list of authenticated users.

It is important to note that the default synced Realm is only compatible with query-based sync, but query-based sync is no longer recommended. Therefore, you should move off the default Realm as soon as possible and use Full sync.

In addition to your application data, the server also includes other internal classes (prefaced with __) used in its management and the master list of authenticated users, represented by the __User class. The internal data is managed by permissions restricting its access--you can learn more about how the permission system works later, for now just know this internal data exists.

Interacting with the default synced Realm has built-in APIs in the Realm SDKs:

Swift
Objective-C
Java
Javascript
.Net

The default synced Realm is provided via an automatic SyncConfiguration that can be accessed from the logged in SyncUser.

For example, to log in and then asynchronously open the default synced Realm:

SyncUser.logIn(with: credentials, server: serverURL) { user, error in
if let user = user {
Realm.Configuration.defaultConfiguration = user.configuration()
Realm.asyncOpen() { realm in
// ...
}
}
}

The default synced Realm is provided via an automatic RLMSyncConfiguration that can be accessed from the logged in RLMSyncUser.

For example, to log in, then asynchronously open the default synced Realm:

[RLMSyncUser logInWithCredentials:credentials
authServerURL:serverURL
onCompletion:^(RLMSyncUser *user, NSError *error) {
if (user) {
RLMRealmConfiguration *config = [user configuration];
[RLMRealm asyncOpenWithConfiguration:config
callbackQueue:dispatch_get_main_queue()
callback:^(RLMRealm *realm, NSError *error) {
if (realm) {
// ...
}
}];
}
}];

The default synced Realm is provided via an automatic SyncConfiguration that can be accessed from the logged in SyncUser.

For example, to log in and open the default synced Realm:

SyncCredentials credentials = getCredentials();
String url = getUrl();
SyncUser.login(credentials, url, new SyncUser.Callback<SyncUser>() {
@Override
public void onSuccess(SyncUser user) {
SyncConfiguration config = user.getDefaultConfiguration();
Realm realm = Realm.getInstance(config);
// Use Realm
}
@Override
public void onError(ObjectServerError error) {
// Handle error
}
});

The default synced Realm is provided via the default configuration which can be accessed from the logged in Realm.Sync.User.

When using query-based sync with Javascript, you are required to pass in a schema for your Realm.

For example, to log in and open the default synced Realm:

Realm.Sync.User.login(server, username, password)
.then((user) => {
let config = user.createConfiguration();
config.schema = [Schema];
Realm.open(config).then((realm) => {
// ...
});
})

The default synced Realm is provided via an automatic QueryBasedSyncConfiguration, which will use the current logged in user from User.Current and the server URL used to authenticate.

For example, to log in, then asynchronously open the default synced Realm with the current user:

var user = await User.LoginAsync(credentials, serverUrl);
RealmConfiguration.DefaultConfiguration = new QueryBasedSyncConfiguration();
var realm = await Realm.GetInstanceAsync();

If you are working with multiple users, you can pass in the specific user as well:

var user = await User.LoginAsync(credentials, serverUrl);
RealmConfiguration.DefaultConfiguration = new QueryBasedSyncConfiguration(user: user);
var realm = await Realm.GetInstanceAsync();

Defining your own Synced Realm

Often times you will want to create multiple Realms within your application for the best possible performance. Specifically, defining your own synced Realm allows you to choose which type of sync you'd like to use (query-based or full) and the associated Realm URL (i.e. a more meaningful endpoint like /user-settings rather than a generic /default )

A common use-case for multiple Realms is to isolate data that is user-specific. A hypothetical chat application might use one synchronized Realm for public chats, another synchronized Realm storing user data, yet another synchronized Realm for a "master channel list" that's read-only to non-administrative users, and a local Realm for persisted settings on that device. Realms are lightweight, and your application can be using several at one time. (On mobile platforms, there are some resource constraints, but up to a dozen open at once should be no issue.)

It is also important to note that architecting around multiple Realms is a great way to improve performance as a larger number of concurrently connected users will increase sync latency associated with a single Realm. The type of sync being used plays a role in how much performance is affected by user count as well as how many Realms you may choose to architect around. Generally, you want to reduce the the number of writers to a single realm to increase performance.

The downside of multiple Realms is that objects in one Realm cannot link to objects in another, nor are cross-Realm queries supported. This means that when designing an application with multiple Realms, you must consider how your data is structured across the Realms and use keys to refer to objects in other Realms.

Query-Based Sync Sample

Full-Sync Realms Sample

If you intend to use fully synchronized Realms, you can do so by adjusting a parameter on the sync configuration:

Swift
Objective-C
Java
Javascript
.Net
user.configuration(realmURL: realmURL, fullSynchronization: true)
RLMRealmConfiguration *config = [RLMRealmConfiguration configurationWithUrl:realmURL fullSynchronization:YES];
SyncConfiguration config = user.createConfiguration(getUrl())
.fullSynchronization()
.build();
const config = {
sync: { url: realmUrl,
fullSynchronization: true,
},
// ...
};
var config = new FullSyncConfiguration(realmUrl, user);

If your application is using multiple Realms, you can manually configure a Query-Based synced Realm by supplying:

Query-based Sync is not recommended. For applications using Realm Sync, we recommend Full Sync. Learn more about our plans for the future of Realm Sync here.

  1. An authenticated user

  2. The Realm URL

The Realm URL uses a specific scheme: realm:// for non-secure connections and realms:// for secure connections.

For more details, see the Understanding Realm URLs and Paths section below.

To obtain an authenticated user, you must login via any of the supported authentication providers. If you are unfamiliar, see the Authentication section.

Swift
Objective-C
Java
Javascript
.Net

Realms on the Realm Object Server are using the same Realm.Configuration used to create standalone Realms, but with the syncConfiguration property on their Realm.Configuration set to a SyncConfiguration value. Synchronized realms are located by URLs.

// Create the configuration
let syncServerURL = URL(string: "realms://myinstance.cloud.realm.io/~/userRealm")!
let config = user.configuration(realmURL: syncServerURL);
// Open the remote Realm
let realm = try! Realm(configuration: config)
// Any changes made to this Realm will be synced across all devices!

The configuration values for a synced Realm cannot have an inMemoryIdentifier or fileURL configured. Setting either property will automatically nil out the syncConfiguration property (and vice-versa). The framework is responsible for managing how synchronized Realms are cached or stored on disk.

Realms on the Realm Object Server are using the same RLMRealmConfiguration that are used to create standalone Realms, but with the syncConfiguration property on their RLMRealmConfiguration set to a RLMSyncConfiguration value. Synchronized realms are located by URLs.

RLMSyncUser *user = [RLMSyncUser currentUser];
// Create the configuration
NSURL *syncServerURL = [NSURL URLWithString: @"realms://myinstance.cloud.realm.io/~/userRealm"];
RLMRealmConfiguration *config = [user configurationWithUrl:syncServerURL];
// Open the remote Realm
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
// Any changes made to this Realm will be synced across all devices!

The configuration values for a synced Realm cannot have an inMemoryIdentifier or fileURL configured. Setting either property will automatically nil out the syncConfiguration property (and vice-versa). The framework is responsible for managing how synchronized Realms are cached or stored on disk.

Realms on the Realm Object Server are created using a subclass of the normal RealmConfiguration used to create standalone Realms. This class is named SyncConfiguration and uses the same builder pattern known from normal Realms. Specifically it requires an User and an URL.

// Create the configuration
SyncUser user = SyncUser.current();
String url = "realms://myinstance.cloud.realm.io/~/userRealm";
SyncConfiguration config = user.createConfiguration(url).build();
// Open the remote Realm
Realm realm = Realm.getInstance(config);
// Any changes made to this Realm will be synced across all devices!

You open a synchronized Realm the same say as you open any other Realm. The configuration can be extended with a sync property if you need to configure the synchronization. The optional properties of sync include:

  • error - a callback for error handling/reporting

  • validate_ssl - indicating if SSL certificates must be validated

  • ssl_trust_certificate_path - a path where to find trusted SSL certificates

  • url - if no url is provided the url to the default Realm will be used.

The error handling is set up by registering a callback (error) as part of the configuration:

const user = Realm.Sync.User.current;
user.createConfiguration({
sync: { url: "realms://myinstance.cloud.realm.io/~/userRealm",
error: err => console.log(err)
},
schema: // ...
});
var realm = new Realm(config);

For standalone Realms, RealmConfiguration is used to configure the options for a Realm. Synchronized Realms on the other hand, are configured using extended configuration classes called QueryBasedSyncConfiguration or FullSyncConfiguration.

The configuration ties together an authenticated user and a sync server URL. The sync server URL may contain the tilde character (“~”) which will be transparently expanded to represent the user’s unique identifier. This scheme easily allows you to write your app to cater to its individual users. The location on disk for shared Realms is managed by the framework, but can be overridden if desired.

var user = User.Current;
var serverURL = new Uri("/default", UriKind.Relative);
var configuration = new QueryBasedSyncConfiguration(serverURL, user);
var realm = Realm.GetInstance(configuration);

Defining the Schema for your Realm

When working with Realms, the first step when using a Realm is to define the object models, or schema, for the objects it will include. With Realm Platform, you can choose to create your models for the Realm in one of two ways:

In Your Application

The simplest way to get started with a synced Realm is to create your models in your client application directly. Realm provides language-specific SDKs that allow you to define a model as a regular class with regular properties. This way of defining schema is a little counterintuitive for those who are used to defining schema from the server, but allows a great deal of flexibility (especially in development).

Swift
Objective-C
Java
Javascript
.Net
import RealmSwift
// Dog model
class Dog: Object {
@objc dynamic var name = ""
@objc dynamic var owner: Person? // Properties can be optional
}
// Person model
class Person: Object {
@objc dynamic var name = ""
@objc dynamic var birthdate = Date(timeIntervalSince1970: 1)
let dogs = List<Dog>()
}

See Models in the Swift documentation for more information on supported data types and other related concepts.

#import <Realm/Realm.h>
@class Person;
// Dog model
@interface Dog : RLMObject
@property NSString *name;
@property Person *owner;
@end
RLM_ARRAY_TYPE(Dog) // define RLMArray<Dog>
// Person model
@interface Person : RLMObject
@property NSString *name;
@property NSDate *birthdate;
@property RLMArray<Dog *><Dog> *dogs;
@end
RLM_ARRAY_TYPE(Person) // define RLMArray<Person>
// Implementations
@implementation Dog
@end // none needed
@implementation Person
@end // none needed
public class Dog extends RealmObject {
private String name;
private int age;
// ... Generated getters and setters ...
}
public class Person extends RealmObject {
@PrimaryKey
private long id;
private String name;
private RealmList<Dog> dogs; // Declare one-to-many relationships
// ... Generated getters and setters ...
}

See Models in the Java documentation for more information on supported data types and other related concepts.

const Realm = require('realm');
// Define your models and their properties
const CarSchema = {
name: 'Car',
properties: {
make: 'string',
model: 'string',
miles: {type: 'int', default: 0},
}
};
const PersonSchema = {
name: 'Person',
properties: {
name: 'string',
birthday: 'date',
cars: 'Car[]', // list of cars
picture: 'data?' // optional property
}
};

See Models in the Javascript documentation for more information on supported data types and other related concepts.

// Define your models like regular C# classes
public class Dog : RealmObject
{
public string Name { get; set; }
public int Age { get; set; }
public Person Owner { get; set; }
}
public class Person : RealmObject
{
public string Name { get; set; }
public IList<Dog> Dogs { get; }
}

See Models in the .Net documentation for more information on supported data types and other related concepts.

In JavaScript, the schema for your model must be passed into the constructor as part of the configuration object. For Swift, Java, and .Net the classes defined in your application code will automatically be added to the Realm when you open it, or you can choose to define a subset of classes for the Realm within its configuration.

Schema Creation using Realm Studio

When working with a larger team or when building a cross-platform application, it can be easier to create your models in a centralized place. Realm Platform offers a desktop application, Realm Studio, through which you can connect to your cloud instance or self-hosted server and create a new synchronized Realm. You can then create new classes and add properties to the Realm.

Once you are finished creating the Realm in Studio, you can export the model definitions to any supported platform language so you can include the code in your application.

How to Open a Realm

Realms can be opened either synchronously or asynchronously. Both methods are discussed in greater detail below. A general suggestion is that it is best to open a Realm asynchronously the first time it is opened. After this, it is typically best to open synchronously. Learn more about this in the Best Practices section below.

Asynchronously Opening A Realm

For most cases, we recommend opening Realms asynchronously. This ensures that the Realm has all remote data available once opened. For example, if you want to show the users a list of all available ZIP codes. Asynchronously opening a Realm uses an API that has a callback which will not return the Realm until it is fully downloaded.

Swift
Objective-C
Java
Javascript
.Net
let config = user.configuration(realmURL: realmURL))
Realm.asyncOpen(configuration: config) { realm, error in
if let realm = realm {
// Realm successfully opened, with all remote data available
} else if let error = error {
// Handle error that occurred while opening or downloading the contents of the Realm
}
}
RLMRealmConfiguration *config = [user configurationWithUrl:realmURL];
[RLMRealm asyncOpenWithConfiguration:config
callbackQueue:dispatch_get_main_queue()
callback:^(RLMRealm *realm, NSError *error) {
if (realm) {
// Realm successfully opened, with all remote data available
} else if (error) {
// Handle error that occurred while opening or downloading the contents of the Realm
}
}
// Create the configuration specifying that the Realm cannot be opened
// the first time until server data has been downloaded. This only
// blocks it from being opened the first time. After that the Realm can
// be opened immediately.
SyncConfiguration config = user.createConfiguration(url)
.waitForInitialRemoteData();
.build();
// Open the remote Realm
Realm realm = Realm.getInstanceAsync(config, new Realm.Callback() {
@Override
public void onSuccess(Realm realm) {
// Realm is ready
}
@Override
public void onError(Throwable exception) {
// Handle error
}
});
const user = Realm.Sync.User.current;
const config = user.createConfiguration({
sync: {
url: "realm://localhost:9080/~/userRealm",
error: err => console.log(err),
// The behavior to use when this is the first time opening a realm.
newRealmFileBehavior: {
type: "downloadBeforeOpen"
},
// The behavior to use when a realm file already exists locally,
// i.e. you have previously opened the realm.
existingRealmFileBehavior: {
type: "downloadBeforeOpen"
}
},
schema: // ...
});
Realm.open(config)
.then(realm => {
// ...use the realm instance here
})
.catch(error => {
// Handle the error here if something went wrong
});
var serverURL = new Uri("/default", UriKind.Relative);
var config = new QueryBasedSyncConfiguration(serverURL, user);
try
{
var realm = await Realm.GetInstanceAsync(config);
// Realm successfully opened, with all remote data available
}
catch (Exception ex)
{
// Handle error that occurred while opening or downloading the contents of the Realm
}

Synchronously Opening A Realm

To access a Realm immediately in your application, you can open a Realm "synchronously." This might be confusing since "synchronous" in this case means the API will return the Realm immediately. However, given that we are opening a synchronized Realm, the first time you open the Realm there will always be no data in it. In the background, the Realm will establish a sync session and start downloading any existing data from the server. You can attach a progress listener to track the download activity or you can subscribe to notifications to get events when the data changes.

This API is recommended when your want the user experience to not be blocked while the data is downloaded, such as displaying partial data to the user in the process.

Swift
Objective-C
Java
Javascript
.Net
// Create the configuration
let syncServerURL = URL(string: "realms://myinstance.cloud.realm.io/~/userRealm")!
let config = user.configuration(realmURL: syncServerURL))
// Open the remote Realm
let realm = try! Realm(configuration: config)
// Any changes made to this Realm will be synced across all devices!
RLMSyncUser *user = [RLMSyncUser currentUser];
// Create the configuration
NSURL *syncServerURL = [NSURL URLWithString: @"realms://myinstance.cloud.realm.io/~/userRealm"];
RLMRealmConfiguration *config = [user configurationWithUrl:syncServerURL];
// Open the remote Realm
RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
// Any changes made to this Realm will be synced across all devices!
// Create the configuration
SyncUser user = SyncUser.current();
String url = "realms://myinstance.cloud.realm.io/~/userRealm";
SyncConfiguration config = user.createConfiguration(url).build();
// Open the remote Realm
Realm realm = Realm.getInstance(config);
// Any changes made to this Realm will be synced across all devices!
const user = Realm.Sync.User.current;
const config = user.createConfiguration({
sync: { url: "realms://myinstance.cloud.realm.io/~/userRealm",
error: err => console.log(err)
},
// The behavior to use when this is the first time opening a realm.
newRealmFileBehavior: {
type: "openImmediately"
},
// The behavior to use when a realm file already exists locally,
// i.e. you have previously opened the realm.
existingRealmFileBehavior: {
type: "openImmediately"
},
schema: // ...
});
Realm.open(config)
.then(realm => {
// ...use the realm instance here
})
.catch(error => {
// Handle the error here if something went wrong
});
var user = User.Current;
var serverURL = new Uri("/default", UriKind.Relative);
var configuration = new QueryBasedSyncConfiguration(serverURL, user);
var realm = Realm.GetInstance(configuration);

If a Realm has read-only Realm level permissions, then you must asynchronously open the Realm as described in Asynchronously Opening A Realm. Opening a file-level read-only Realm without the asynchronous API will cause an error.

This behavior does not apply to query-based sync Realms that use their own access control.****

Understanding Realm URLs and Paths

Synchronized Realms exists as resources tied to a specific URL path. If you are manually opening synchronized Realms and/or splitting your data up between multiple Realms then this is important to understand.

If you are using the default synced Realm, you typically don't need to worry about this, as the Realm URL is automatically configured for you at the path: /default

Realm Scheme

The URLs for synchronized Realms use a dedicated scheme. The underlying networking is not using standard HTTP, but instead Websockets. We chose not to use the Websockets scheme ws/wss because we utilize a custom protocol and wanted to emphasize the distinction.

For unsecured synchronized connections, the scheme is realm://.

For secured synchronized connections , the schema is realms://.

Take care not to mix up the http/https scheme used in the authentication URL when logging in a user.

Path Restrictions

The Realm Object Server includes a path-level permission system. By default, it restricts the creation of Realms to scoped paths for the user. For example, if a user's ID is 12345 then this user can only create Realms at the path /12345/myRealm.

This restriction is not enforced for admin users. As a result, if you want to create a global Realm at the base path, /globalRealm the creation of the Realm must be done by an admin user.

You can learn more about the details of permissions in its dedicated section, including how to adjust these permissions to share a Realm:

~ Shorthand

When working with Realm paths you might quickly realize that it is time-consuming to keep track of the userId in a scoped-path. To simplify, Realm paths accept as shorthand the use of the tilde ~ character in exchange for the userId. For example, if you open a Realm with a user whose userId is 12345, you can use either of these URL paths to open the same Realm:

"realms://myinstance.cloud.realm.io/~/myRealm"
"realms://myinstance.cloud.realm.io/12345/myRealm"

Best Practices

Opening Realms

We always recommend on first app load to use the asyncOpen API of each respective binding to open the realm. One reason is that it is the only way to open a read-only realm but another advantage is that it is the most efficient way to download the realm since there is no client-side processing or merging that will kick in. The drawback is that the app must be online and connected to ROS in order to return a realm reference in the callback. You must also consider the case if there is a large amount of data to be downloaded - the user could sit there waiting if so. Care must be taken to make sure that the first realm can be downloaded in a reasonable amount of time. Many realms can be used on the client-side app - in fact, you should. Consider which data is necessary for the functioning of the app - this realm should be downloaded first. For instance, in a field worker app, the user should download type tables and a list of projects they could work on - once the user selects a particular project the app then opens the project realm which contains all of the relevant data associated with the project.

Once the realm is opened asynchronously, we recommend opening the realm synchronously from here on out. The merge algorithm on the client side will no longer be triggered once the schema is established. Synchronously opening the realm once the realm has been downloaded also enables an offline-first use case. We also recommend using synchronous open for all large data sets that are not contingent on the functioning of the app. For instance, in a retail app, you may want to asyncOpen the types of products, the locations of stores, and other business logic data, but the actual inventory of clothing should be synchronously downloaded in the background by the Realm process while the UI updates continuously.

Offline for First App Run

If the app must be started in offline mode use case we recommend starting the app with a non-synced realm because a synced realm requires a valid sync user which can only be obtained by online successful communication with ROS. Once the app comes online and is able to obtain a valid sync user and open a permitted synced realm, the data that is contained within the non-synced realm should be transferred to the synced realm via user code script.

Using Load Balancers

We strongly recommend utilizing a load balancer if hosting your own Realm Object Server. If you are doing this, it is important to point your auth and Realm URLs at the load balancer (rather than directly at ROS). This is to ensure the smoothest possible experience if backend components are migrated in the future. If you are using Realm Cloud, simply use the URL you have been given.