Navigation

Quick Start

Overview

This page contains information to quickly get Realm Database integrated into your app. Before you begin, ensure you have:

Check Out the Tutorial

This page contains only the essential information that you need to set up a MongoDB Realm application. If you prefer to follow a guided tutorial that shows you step-by-step how to set up a working app, check out the Android Tutorial where you’ll build a mobile app that connects to the Task Tracker backend.

Import Realm

Near the top of any Kotlin file that uses Realm, add the following import statement:

import io.realm.Realm
import io.realm.Realm;

Initialize Realm

Before you can use Realm in your app, you must initialize the Realm library. Your application should initialize Realm just once each time the application runs.

To initialize the Realm library, provide an Android context to the Realm.init() static function. You can provide an Activity, Fragment, or Application context for initialization with no difference in behavior. You can initialize the Realm library in the onCreate() method of an application subclass to ensure that you only initialize Realm once each time the application runs.

Realm.init(this) // `this` is a Context, typically an Application or Activity
Realm.init(this); // `this` is a Context, typically an Application or Activity

Register Your Application Subclass in the Android Manifest

If you create your own Application subclass, you must add it to your application’s AndroidManifest.xml to execute your custom application code. Setting the android.name property of your manifest’s application definition instantiates your Application subclass before any other class when the process for your application is created.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.mongodb.example">

   <application
      android:name=".MyApplicationSubclass"
      ...
   />
</manifest>

Initialize the App

To use MongoDB Realm features such as authentication and sync, you must access your Realm app using your Realm app ID. You can find your Realm app ID in the Realm UI.

val appID = "<your app ID>" // replace this with your App ID
val app: App = App(AppConfiguration.Builder(appID)
      .build())
String appID = "<your app ID>"; // replace this with your App ID
App app = new App(new AppConfiguration.Builder(appID)
      .build());

Android Studio Errors?

If Android Studio does not recognize the Realm, App, or AppConfiguration types, there could be a problem with the your Gradle build configuration. To fix the issue, try:

  • Cleaning your project with Build > Clean Project
  • Rebuilding your project based on your updated build.gradle file with Build > Rebuild Project
  • Revisiting the Install the Android SDK guide to make sure that you installed the dependencies correctly.

Define Your Object Model

Your application’s object model controls the data that you can store within Realm Database and synchronize to and from MongoDB Realm.

If have not enabled Realm Sync or you enabled Sync with Development Mode in the Realm UI, you can define your object model directly in code.

Note

If you have enabled Sync but turned off Developer Mode, you can copy and paste the object model definitions that Realm generated for you from the SDKs tab in the Realm UI. You must re-enable Developer Mode if you want to make changes to the object model definition from client side code. See Configure Your Data Model.

import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import io.realm.annotations.Required
import org.bson.types.ObjectId

enum class TaskStatus(val displayName: String) {
   Open("Open"),
   InProgress("In Progress"),
   Complete("Complete"),
}

open class Task(_name: String = "Task", project: String = "My Project") : RealmObject() {
   @PrimaryKey var _id: ObjectId = ObjectId()
   var _partition: String = project
   var name: String = _name

   @Required
   var status: String = TaskStatus.Open.name
   var statusEnum: TaskStatus
      get() {
         // because status is actually a String and another client could assign an invalid value,
         // default the status to "Open" if the status is unreadable
         return try {
            TaskStatus.valueOf(status)
         } catch (e: IllegalArgumentException) {
            TaskStatus.Open
         }
      }
      set(value) { status = value.name }
}
package com.mongodb.javarealmsnippets.model;

import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
import io.realm.annotations.Required;
import org.bson.types.ObjectId;

public class Task extends RealmObject {
    @PrimaryKey
    private ObjectId _id = new ObjectId();
    private String _partition = "My Project";
    private String name = "Task";

    @Required
    private String status = TaskStatus.Open.name();

    public void setStatus(TaskStatus status) {
        this.status = status.name();
    }

    public String getStatus() {
        return this.status;
    }

    public ObjectId get_id() {
        return _id;
    }

    public void set_id(ObjectId _id) {
        this._id = _id;
    }

    public String get_partition() {
        return _partition;
    }

    public void set_partition(String _partition) {
        this._partition = _partition;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Task(String _name, String project) {
        this._partition = project;
        this.name = _name;
    }

    public Task() {}

    public enum TaskStatus {
        Open("Open"),
        InProgress("In Progress"),
        Complete("Complete");

        String displayName;
        TaskStatus(String displayName) {
            this.displayName = displayName;
        }
    }
}

Authenticate a User

When you have enabled anonymous authentication in the Realm UI, users can immediately log into your app without providing any identifying information:

val credentials: Credentials = Credentials.anonymous()

var user: User? = null
app.loginAsync(credentials) {
   if (it.isSuccess) {
      Log.v("QUICKSTART", "Successfully authenticated anonymously.")
      user = app.currentUser()

      // interact with MongoDB Realm via user object here

   } else {
      Log.e("QUICKSTART", "Failed to log in. Error: ${it.error}")
   }
}
Credentials credentials = Credentials.anonymous();

app.loginAsync(credentials, it -> {
      if (it.isSuccess()) {
         Log.v("QUICKSTART", "Successfully authenticated anonymously.");
         user = app.currentUser();

         // interact with MongoDB Realm via user object here

      } else {
         Log.e("QUICKSTART", "Failed to log in. Error: " + it.getError().toString());
      }
});

Realm provides many additional ways to authenticate, register, and link users.

Open a Realm

Once you have enabled Realm Sync and authenticated a user, you can open a synced realm. Use the SyncConfiguration to control the specifics of how your application synchronizes data with MongoDB Realm, including the partition, whether to wait for an initial batch of data to sync, and more.

val user: User? = app.currentUser()
val partitionValue: String = "myPartition"
val config = SyncConfiguration.Builder(user!!, partitionValue)
               .build()

val realm: Realm = Realm.getInstance(config)
User user = app.currentUser();
String partitionValue = "myPartition";
SyncConfiguration config = new SyncConfiguration.Builder(user, partitionValue)
                                 .build();

Realm realm = Realm.getInstance(config);

See also

Sync Data

Create, Read, Update, and Delete Objects

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

To create a new Task, instantiate the Task class and add it to the realm in a write block:

val task : Task = Task("New Task", partitionValue)
realm.executeTransaction { transactionRealm ->
   transactionRealm.insert(task)
}
Task task = new Task("New Task", partitionValue);
realm.executeTransaction( transactionRealm -> {
   transactionRealm.insert(task);
});

You can retrieve a live collection of all tasks in the realm:

// all tasks in the realm
val tasks : RealmResults<Task> = realm.where<Task>().findAll()
// all tasks in the realm
RealmResults<Task> tasks = realm.where(Task.class).findAll();

You can also filter that collection using a filter:

// you can also filter a collection
val tasksThatBeginWithN : List<Task> = tasks.where().beginsWith("name", "N").findAll()
val openTasks : List<Task> = tasks.where().equalTo("status", TaskStatus.Open.name).findAll()
// you can also filter a collection
List<Task> tasksThatBeginWithN = tasks.where().beginsWith("name", "N").findAll();
List<Task> openTasks = tasks.where().equalTo("status", TaskStatus.Open.name()).findAll();

To modify a task, simply update its properties in a write transaction block:

// all modifications to a realm must happen inside of a write block
realm.executeTransaction { transactionRealm ->
   val innerOtherTask : Task = transactionRealm.where<Task>().equalTo("_id", otherTask._id).findFirst()!!
   innerOtherTask.status = TaskStatus.Complete.name
}
// all modifications to a realm must happen inside of a write block
realm.executeTransaction (transactionRealm -> {
   Task innerOtherTask = transactionRealm.where(Task.class).equalTo("_id", otherTask.get_id()).findFirst();
   innerOtherTask.setStatus(TaskStatus.Complete);
});

Finally, you can delete a task:

val yetAnotherTask: Task = tasks.get(0)!!
val yetAnotherTaskId: ObjectId = yetAnotherTask._id
// all modifications to a realm must happen inside of a write block
realm.executeTransactionAsync { transactionRealm ->
   val innerYetAnotherTask : Task = transactionRealm.where<Task>().equalTo("_id", yetAnotherTaskId).findFirst()!!
   innerYetAnotherTask.deleteFromRealm()
}
Task yetAnotherTask = tasks.get(0);
ObjectId yetAnotherTaskId = yetAnotherTask.get_id();
// all modifications to a realm must happen inside of a write block
realm.executeTransactionAsync(transactionRealm -> {
   Task innerYetAnotherTask = transactionRealm.where(Task.class).equalTo("_id", yetAnotherTaskId).findFirst();
   innerYetAnotherTask.deleteFromRealm();
});

Watch for Changes

You can watch a realm, collection, or object for changes with the addChangeListener() method.

tasks.addChangeListener(OrderedRealmCollectionChangeListener<RealmResults<Task>> { collection, changeSet ->
   val deletions = changeSet.deletionRanges
   // if keeping parallel arrays in sync, handle deletions in reverse to avoid changing indices after removals
   for (i in deletions.indices.reversed()) {
      val range = deletions[i]
      Log.v("QUICKSTART", "Deleted range: ${range.startIndex} to ${range.startIndex + range.length}")
   }

   val insertions = changeSet.insertionRanges
   for (range in insertions) {
      Log.v("QUICKSTART", "Inserted range: ${range.startIndex} to ${range.startIndex + range.length}")
   }

   val modifications = changeSet.changeRanges
   for (range in modifications) {
      Log.v("QUICKSTART", "Updated range: ${range.startIndex} to ${range.startIndex + range.length}")
   }
})
tasks.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults<Task>>() {
   @Override
   public void onChange(RealmResults<Task> collection, OrderedCollectionChangeSet changeSet) {
      OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges();
      for (OrderedCollectionChangeSet.Range range : deletions) {
         Log.v("QUICKSTART", "Deleted range: " + range.startIndex + " to " + (range.startIndex + range.length));
      }
      OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges();
      for (OrderedCollectionChangeSet.Range range : insertions) {
         Log.v("QUICKSTART", "Inserted range: " + range.startIndex + " to " + (range.startIndex + range.length));
      }

      OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges();
      for (OrderedCollectionChangeSet.Range range : modifications) {
         Log.v("QUICKSTART", "Updated range: " + range.startIndex + " to " + (range.startIndex + range.length));
      }
   }
});

Log Out

Once logged in, you can log out:

user.logOutAsync {
   if (it.isSuccess) {
      Log.v("QUICKSTART", "Successfully logged out.")
   } else {
      Log.e("QUICKSTART", "Failed to log out. Error: ${it.error}")
   }
}
user.logOutAsync( result -> {
   if (result.isSuccess()) {
      Log.v("QUICKSTART", "Successfully logged out.");
   } else {
      Log.e("QUICKSTART", "Failed to log out. Error: " + result.getError().toString());
   }
});

Complete Example

Run the complete example, by replacing the appId with your realm app ID.

MainActivity.kt
package com.mongodb.realm.quickstart

import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import io.realm.annotations.Required
import org.bson.types.ObjectId

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.util.Log
import io.realm.OrderedRealmCollectionChangeListener

import io.realm.Realm
import io.realm.RealmResults
import io.realm.kotlin.where
import io.realm.mongodb.App
import io.realm.mongodb.AppConfiguration

import io.realm.mongodb.Credentials
import io.realm.mongodb.User
import io.realm.mongodb.sync.SyncConfiguration


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Realm.init(this) // context, usually an Activity or Application
        val appID = "<your app ID>" // replace this with your App ID
        val app: App = App(AppConfiguration.Builder(appID)
            .build())

        val credentials: Credentials = Credentials.anonymous()

        app.loginAsync(credentials) {
            if (it.isSuccess) {
                Log.v("QUICKSTART", "Successfully authenticated anonymously.")
                val user: User? = app.currentUser()

                val partitionValue: String = "My Project"

                val config = SyncConfiguration.Builder(user!!, partitionValue)
                                .build()

                val realm: Realm = Realm.getInstance(config)

                val task : Task = Task("New Task", partitionValue)
                realm.executeTransaction { transactionRealm ->
                    transactionRealm.insert(task)
                }

                // all tasks in the realm
                val tasks : RealmResults<Task> = realm.where<Task>().findAll()

                // you can also filter a collection
                val tasksThatBeginWithN : List<Task> = tasks.where().beginsWith("name", "N").findAll()
                val openTasks : List<Task> = tasks.where().equalTo("status", TaskStatus.Open.name).findAll()

                val otherTask: Task = tasks[0]!!

                tasks.addChangeListener(OrderedRealmCollectionChangeListener<RealmResults<Task>> { collection, changeSet ->
                    // process deletions in reverse order if maintaining parallel data structures so indices don't change as you iterate
                    val deletions = changeSet.deletionRanges
                    for (i in deletions.indices.reversed()) {
                        val range = deletions[i]
                        Log.v("QUICKSTART", "Deleted range: ${range.startIndex} to ${range.startIndex + range.length - 1}")
                    }

                    val insertions = changeSet.insertionRanges
                    for (range in insertions) {
                        Log.v("QUICKSTART", "Inserted range: ${range.startIndex} to ${range.startIndex + range.length - 1}")
                    }

                    val modifications = changeSet.changeRanges
                    for (range in modifications) {
                        Log.v("QUICKSTART", "Updated range: ${range.startIndex} to ${range.startIndex + range.length - 1}")
                    }
                })

                // all modifications to a realm must happen inside of a write block
                realm.executeTransaction { transactionRealm ->
                    val innerOtherTask : Task = transactionRealm.where<Task>().equalTo("_id", otherTask._id).findFirst()!!
                    innerOtherTask.status = TaskStatus.Complete.name
                }

                val yetAnotherTask: Task = tasks.get(0)!!
                val yetAnotherTaskId: ObjectId = yetAnotherTask._id
                // all modifications to a realm must happen inside of a write block
                realm.executeTransactionAsync { transactionRealm ->
                    val innerYetAnotherTask : Task = transactionRealm.where<Task>().equalTo("_id", yetAnotherTaskId).findFirst()!!
                    innerYetAnotherTask.deleteFromRealm()
                }

                realm.close()
            } else {
                Log.e("QUICKSTART", "Failed to log in. Error: ${it.error}")
            }
        }

    }
}

enum class TaskStatus(val displayName: String) {
    Open("Open"),
    InProgress("In Progress"),
    Complete("Complete"),
}

open class Task(_name: String = "Task", project: String = "My Project") : RealmObject() {
    @PrimaryKey var _id: ObjectId = ObjectId()
    var _partition: String = project
    var name: String = _name

    @Required
    var status: String = TaskStatus.Open.name
    var statusEnum: TaskStatus
        get() {
            // because status is actually a String and another client could assign an invalid value,
            // default the status to "Open" if the status is unreadable
            return try {
                TaskStatus.valueOf(status)
            } catch (e: IllegalArgumentException) {
                TaskStatus.Open
            }
        }
        set(value) { status = value.name }
}
Task.java
package com.mongodb.javarealmsnippets.model;

import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
import io.realm.annotations.Required;
import org.bson.types.ObjectId;

public class Task extends RealmObject {
    @PrimaryKey
    private ObjectId _id = new ObjectId();
    private String _partition = "My Project";
    private String name = "Task";

    @Required
    private String status = TaskStatus.Open.name();

    public void setStatus(TaskStatus status) {
        this.status = status.name();
    }

    public String getStatus() {
        return this.status;
    }

    public ObjectId get_id() {
        return _id;
    }

    public void set_id(ObjectId _id) {
        this._id = _id;
    }

    public String get_partition() {
        return _partition;
    }

    public void set_partition(String _partition) {
        this._partition = _partition;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Task(String _name, String project) {
        this._partition = project;
        this.name = _name;
    }

    public Task() {}

    public enum TaskStatus {
        Open("Open"),
        InProgress("In Progress"),
        Complete("Complete");

        String displayName;
        TaskStatus(String displayName) {
            this.displayName = displayName;
        }
    }
}
MainActivity.java
package com.mongodb.realm.quickstart

import io.realm.OrderedCollectionChangeSet;
import io.realm.OrderedRealmCollectionChangeListener;
import io.realm.RealmResults;
import org.bson.types.ObjectId;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;

import java.util.List;
import com.mongodb.javarealmsnippets.model.Task;
import io.realm.Realm;
import io.realm.mongodb.App;
import io.realm.mongodb.AppConfiguration;

import io.realm.mongodb.Credentials;
import io.realm.mongodb.User;
import io.realm.mongodb.sync.SyncConfiguration;


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Realm.init(this); // context, usually an Activity or Application
        String appID = "<your app ID>"; // replace this with your App ID
        App app = new App(new AppConfiguration.Builder(appID)
                .build());

        Credentials credentials = Credentials.anonymous();

        app.loginAsync(credentials, it -> {
            if (it.isSuccess()) {
                Log.v("QUICKSTART", "Successfully authenticated anonymously.");

                User user = app.currentUser();

                String partitionValue = "myPartition";
                SyncConfiguration config = new SyncConfiguration.Builder(user, partitionValue)
                                                .build();

                Realm realm = Realm.getInstance(config);

                Task task = new Task("New Task", partitionValue);
                realm.executeTransaction( transactionRealm -> {
                    transactionRealm.insert(task);
                });

                // all tasks in the realm
                RealmResults<Task> tasks = realm.where(Task.class).findAll();

                // you can also filter a collection
                List<Task> tasksThatBeginWithN = tasks.where().beginsWith("name", "N").findAll();
                List<Task> openTasks = tasks.where().equalTo("status", Task.TaskStatus.Open.name()).findAll();

                Task otherTask = tasks.first();

                tasks.addChangeListener(new OrderedRealmCollectionChangeListener<RealmResults<Task>>() {
                    @Override
                    public void onChange(RealmResults<Task> collection, OrderedCollectionChangeSet changeSet) {
                        OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges();
                        for (OrderedCollectionChangeSet.Range range : deletions) {
                            Log.v("QUICKSTART", "Deleted range: " + range.startIndex + " to " + (range.startIndex + range.length - 1));
                        }
                        OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges();
                        for (OrderedCollectionChangeSet.Range range : insertions) {
                            Log.v("QUICKSTART", "Inserted range: " + range.startIndex + " to " + (range.startIndex + range.length - 1));
                        }

                        OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges();
                        for (OrderedCollectionChangeSet.Range range : modifications) {
                            Log.v("QUICKSTART", "Updated range: " + range.startIndex + " to " + (range.startIndex + range.length - 1));
                        }
                    }
                });

                // all modifications to a realm must happen inside of a write block
                realm.executeTransaction (transactionRealm -> {
                        Task innerOtherTask = transactionRealm.where(Task.class).equalTo("_id", otherTask.get_id()).findFirst();
                        innerOtherTask.setStatus(Task.TaskStatus.Complete);
                });

                Task yetAnotherTask = tasks.get(0);
                ObjectId yetAnotherTaskId = yetAnotherTask.get_id();
                // all modifications to a realm must happen inside of a write block
                realm.executeTransactionAsync(transactionRealm -> {
                        Task innerYetAnotherTask = transactionRealm.where(Task.class).equalTo("_id", yetAnotherTaskId).findFirst();
                        innerYetAnotherTask.deleteFromRealm();
                });

                realm.close();
            } else {
                Log.e("QUICKSTART", it.getError().toString());
            }
        });
    }
}

Output

Running the above code should output something like the following:

Successfully authenticated anonymously.

Updated range: 0 to 1

Deleted range: 0 to 1

Successfully logged out.