The Complete Realm Mobile Platform Walkthrough

RMP Walkthrough

Realm Mobile Platform Overview

The core concept of the Realm Mobile Platform is a lightweight object container called a Realm. Like a database, data in Realms can be queried and filtered, interconnected, and persisted. Unlike a traditional database, though, objects in Realms are live and fully reactive. Realms synchronize seamlessly between devices and applications, and can be accessed safely across threads.

The Realm Mobile Database is built from the ground up to run on mobile devices. Unlike a traditional database, objects in a Realm are native objects. You don’t have to copy objects out of the database, modify them, and save them back—you’re always working with the “live,” real object. If one thread or process modifies an object, other threads and processes can be immediately notified. Objects always stay in sync.

Applications built on the Realm Mobile Platform have the ability to access and create Realms on the Realm Object Server. All your application needs is the connection information for your Object Server and the URLs of the server-side Realms. Objects on the Realm Object Server will be downloaded as the application needs them–then the local copy of the Realm will be kept fully synchronized with the Object Server’s copy.

A key concept of the Realm Mobile Platform is the use of multiple Realms: your application isn’t tied to a single data source, and data sources are as easy to access as web pages. Realms offer fine-grained access control tied to user accounts. A Realm on the Object Server can be public, accessible to all users (for instance, a catalog for an online store); private, accessible to only the user who created it (for instance, a private task list, or application settings); or shared between specific users (for instance, a household shopping list).

Installing Realm Developer Edition on AWS

We provide public Amazon Machine Images (AMIs) for the Realm Mobile Platform, using hardware virtual machine (HVM) virtualization based on Ubuntu 16.04. If you’re already running an Ubuntu, Red Hat Enterprise, or CentOS AMI, you can install Realm Developer Edition from Realm’s PackageCloud repository.

Installing the Developer Edition AMI

From the EC2 console, click AMIs under Images in the sidebar, and search for one of the following images based on your preferred region.

Region Image AMI ID
ap-northeast-1 (Tokyo) ami-a03233c7
eu-west-1 (Ireland) ami-aecfdec8
us-east-1 (N. Virginia) ami-8a62289c

Select the image and lick Launch. On the next screen, “Choose an Instance Type,” select an instance type. You can leave the default, t2.micro, selected to stay on the free tier, although we recommend m3.medium. Click Review and Launch.

image2

Finally, click Launch. You’ll be prompted to select an existing public/private key pair for SSH access or to create a new one. After the instance is launched, click View Instance. The dashboard will now show your running instance with its public IP address.

While the Realm instance has port 22 (SSH) open by default, it does not have Object Server-specific ports open. You’ll definitely need to open port 9080 (Realm Dashboard and client access), and may also need 7800 (sync workers), 27810 (backup workers), and if if you’re using statsd for monitoring, 8125. Click on the instance’s security group and click on Edit inbound rules. Add a Custom TCP rule for port 9080, allowing 0.0.0.0/0 for all IPs, or alternatively an IP address range to limit connections to. You can add other rules for other important ports, or you can specify a range of ports in one rule (e.g., 22-9999), although doing so is less secure than opening only specific ports.

image1

Once this rule is in place, you should be able to visit <ip-address>:9080 in your browser and launch the Realm Dashboard.

Installing on an Existing Linux AMI Instance

Realm maintains package repositories through a service called PackageCloud; installing and starting Realm Object Server takes just a few steps.

  1. SSH into your existing instance.

  2. Install the Object Server from Realm’s PackageCloud repository. On Ubuntu, type:

    curl -s https://packagecloud.io/install/repositories/realm/realm/script.deb.sh | sudo bash
     sudo apt-get update
     sudo apt-get install realm-object-server-developer

    On RHEL/Centos, type:

    curl -s https://packagecloud.io/install/repositories/realm/realm/script.rpm.sh | sudo bash
     sudo yum -y install realm-object-server-developer
  3. Start the Realm Object Server. On RHEL/Centos version 6, type:

    sudo chkconfig realm-object-server on
     sudo service realm-object-server start

    On Ubuntu or RHEL/Centos version 7, type:

    sudo systemctl enable realm-object-server
     sudo systemctl start realm-object-server

Lastly, you’ll need to make sure that port 9080 is open on your AMI instance, as this is used for Realm clients and the Dashboard. You may also need 7800 (sync workers), 27810 (backup workers), and if if you’re using statsd for monitoring, 8125. Click on the instance’s security group and click on Edit inbound rules. Add a Custom TCP rule for port 9080, allowing 0.0.0.0/0 for all IPs, or alternatively an IP address range to limit connections to. You can add other rules for other important ports, or you can specify a range of ports in one rule (e.g., 22-9999), although doing so is less secure than opening only specific ports.

Once this port is open, you should be able to visit <ip-address>:9080 in your browser and launch the Realm Dashboard.

The Realm Dashboard

Once your AMI is set up, visit the Dashboard in your browser (at <ip-address>:9080). On your first access, you’ll be prompted to create an admin user by supplying an email address and password.

The Dashboard gives you the following features:

  • Dashboard: System status, including real-time displays of data rate in/out, bytes in/out, open connections, and open Realms
  • Realms: Paths, permissions, and owners of Realms synced to this Object Server, with the ability to “drill down” and display the models and contents of individual Realms
  • Users: User information and management for this Object Server, including granting and removing administrative privileges
  • Functions: Management and entry of Realm Functions
  • Logs: System logs for the Object Server, with selectable detail level

Building an iOS App with RealmLoginKit

The Multi-User Tasks Tutorial walks you through building a multi-user version of RealmTasks, our canonical demo app. The tutorial will show you how to:

  • Setup a new Realm-based project from scratch using Cocoapods
  • How to adopt and setup a free Realm utility module called RealmLoginKit which allows you to easily created multi-user ready applications with almost zero coding
  • How to create a simple Realm-based Task Manager that can interoperate with the fully-featured RealmTasks that comes with the Realm Mobile Platform distribution

In order to successfully complete the tutorial, you will need a Macintosh running macOS 10.12 or later, as well as a copy of Xcode 8.2.3 or later.

For full instructions and source code, go to our GitHub repository for the tutorial:

https://github.com/realm-demos/realm-MultiUserTasksTutorial

Reactive Server-Side Features With Realm Functions

Now that you’ve got the Realm Object Server set up, we can start writing custom server-side features with Realm Functions.

Adding Your First Function

First, open up your web dashboard by typing in the IP address you used above. Then click on the Functions button on the left. That’ll show you a list of all your Functions. Let’s make a new one! Press “Create new Function,” and you’ll see the screen below:

New function

Here, the first field is for the title of the Function—think of it as a filename that’ll help you differentiate between the different Functions you’ll create.

Next is a field to specify which Realms to monitor for changes. Because Realms are organized in a filesystem-like structure, we write this as a path. This field also supports regular expressions, so that you can get a Function to observe changes over a range of many Realms.

The regex we want for our Function is:

^/([0-9a-f]+)/realmtasks$

This watches for changes in any user subdirectories named realmtasks. The first regex pattern — ([0-9a-f]+) — is a way to look for directories belonging to specific users, which are named after their UUID. By using this pattern, we can watch over all the realmtasks Realms, even if they all are scoped to specific users.

Once you’ve pasted that regex pattern, you can paste in the Functions JavaScript code that will be run in response to each new change event generated in those Realms. Copy and paste the following code into the code editor:

var Wit = require("node-wit").Wit;
var WIT_ACCESS_TOKEN = ""; // UPDATE WITH API TOKEN
var witClient = new Wit({accessToken: WIT_ACCESS_TOKEN});
 
module.exports = function(change_event) {
    var realm = change_event.realm;
    var changes = change_event.changes.Task;
    var taskIndexes = changes.modifications;
    console.log("Change detected: " + changes);
    // Get the task objects to processes
    var tasks = realm.objects("Task");
    for (var i = 0; i < taskIndexes.length; i++) {
        var taskIndex = taskIndexes[i];
        // Retrieve the task object from the Realm with index
        var task = tasks[taskIndex];
        console.log("New task received: " + change_event.path);
        // get date from wit.ai
        // node-wit: https://github.com/wit-ai/node-wit
        witClient.message(task.text, {}).then((data) => {
            console.log("Response received from wit: " + JSON.stringify(data));
            var dateTime = data.entities.datetime[0];
            if (!dateTime) {
                console.log("Couldn't find a date.");
                return;
            }
            console.log("Isolated calculated date: " + dateTime.value);
            // to write the date, we'll have to add a date property on the client and migrate it
            realm.write(function() {
                task.date = new Date(dateTime.value);
            });
        })
        .catch(console.error);
    }
};

The structure of this code is pretty straightforward. Whenever one or a series of changes in the observed Realms is synced to the Realm Object Server, this Function receives a changeEvent object. It will contain the indices of any of the objects that changed — and specifies whether they were created, updated, or deleted. However, since it just contains the indices of the changed objects and not the objects themselves, you have to retrieve the objects themselves:

...
var realm = change_event.realm;
var changes = change_event.changes.Task; // changes just for the Task model
var taskIndexes = changes.modifications; // we’re looking for updates, not creations or deletes
// Get the task objects to processes
var tasks = realm.objects("Task"); // this is the list of ALL objects of that model type
for (var i = 0; i < taskIndexes.length; i++) {
    var taskIndex = taskIndexes[i]; // here we get the current index
    // Retrieve the task object from the Realm with index
    var task = tasks[taskIndex]; // finally, we retrieve the task object from the Realm
}
...

Now, before this code will run, you’ll have to follow the instructions beginning here to get your Wit.ai access token, as well as configure your Wit.ai instance to watch for datetimes embedded in the text of the Task object. Once you finish step 3 at that link, you can just paste in the access token at the top of your Function, in the WIT_ACCESS_TOKEN variable.

Finally, we have to install the node-wit package. Open your shell and add your Realm Mobile Platform folder to your path:

cd ENTER/THE/PATH/TO/YOUR/RMP/FOLDER
PATH=.prefix/bin:$PATH npm install node-wit

With your access token in place and the package installed, it’s time to save and run the function. Press the ‘play’ button in the top right, and you’ll see the Enabled indicator pop up on the left:

Enabled

To see what’s happening in the Function and to debug it, you can scroll down this page and see the console.

Screen Shot 2017-06-23 at 4.12.43 PM

Now you’re ready to turn on your iPhone app again, add a task like “Buy groceries at 5pm tomorrow” or “Go for a run in 20 minutes,” and see as it’s processed by the Function, logged in the console, and resolved into a reminder property that updates automatically in the app, below the text of the task.

Simulator Screen Shot Jun 23, 2017, 4.21.33 PMFuncti

That’s it! You’ve written your first Function. Maybe your next Function might schedule Push Notifications to these tasks, based on the reminder property we’ve filled in here? You can really build as many server-side features as you’d like, just by writing JavaScript.

Upgrading to Professional Edition

First, you’ll have to uninstall the existing edition you have installed. For platform-specific upgrade instructions to Professional Edition, click here.

Why upgrade? While Realm Functions is available to all users of the Realm Mobile Platform, your PE or EE license also enables you to use the Realm Sync binding for Node.js outside of the Realm Object Server, enabling the Event Handling, Data Access and Data Connector features.

You could use it to enable distributed event handling that responds to data changes in a more performant way, or you could use it to interact with the Realm Object Server from your existing servers, enabling you to push data to the Realm Object Server instead of simply reacting to data that arrives on the Realm Object Server.

To enable the Event Handling, Data Access, and Data Connector features that the Node.js binding makes possible, you must enable these APIs by including your access token in your Node.js application. (This token is sent to you via email when you sign up for a trial or purchase the Professional or Enterprise editions.) Near the start of your Node.js application, include your token and make a setAccessToken call to the Object Server:

var token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...";
Realm.Sync.setAccessToken(token);

Alternatively, you could load the token from disk:

var fs = require('fs')
RealmSync.setAccessToken(fs.readFileSync('./access-token', 'utf-8'));

Create an Event Handler

To use Realm Event Handling, you’ll need to create a small Node.js application.

Create a directory to place the server files, then create a file named package.json. This JSON file is used by Node.js and NPM, its package manager, to describe an application and specify external dependencies.

You can create this file interactively by using npm init. You can also fill in a simple skeleton file yourself using your text editor:

{
    "name": "MyApp",
    "version": "0.0.1",
    "main": "index.js",
    "author": "Your Name",
    "description": "My Cool Realm App",
    "dependencies": {
        "realm": "file:realm-1.0.0-professional.tgz"
    }
}

We specify the downloaded archive of the Realm Mobile Platform as a file: dependency; that archive should be in the same directory as the package.json file. (Make sure the filename specified in package.json is the filename that’s actually on disk!)

If you have other dependencies for your application, specify them in the dependencies section of this file.

After the package.json file is configured properly, type:

npm install

to download, unpack and configure all the modules and their dependencies.

Your event handler will need to access the Object Server with administrative privileges, so you’ll need to get the Object Server’s admin token. Under Linux, view the token with:

cat /etc/realm/admin_token.base64

On macOS, the token is stored in the realm-object-server folder, inside the Realm Mobile Platform folder. Navigate to that folder and view the token:

cd path-to/realm-mobile-platform
cat realm-object-server/admin_token.base64

Now that you’ve set up everything you need, you can create the file where the event handling code lives. A sample index.js file might look something like this:

var Realm = require('realm');
 
var ACCESS_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...";
 
// Unlock Professional Edition APIs
Realm.Sync.setAccessToken(ACCESS_TOKEN); 
 
// Insert the Realm admin token here
//   Linux:  cat /etc/realm/admin_token.base64
//   macOS:  cat realm-object-server/admin_token.base64
var ADMIN_TOKEN = 'ADMIN_TOKEN';
 
// the URL to the Realm Object Server
var SERVER_URL = 'realm://127.0.0.1:9080';
 
// The regular expression you provide restricts the observed Realm files to only the subset you
// are actually interested in. This is done in a separate step to avoid the cost
// of computing the fine-grained change set if it's not necessary.
var NOTIFIER_PATH = '^/.*/private$';
 
// The handleChange callback is called for every observed Realm file whenever it
// has changes. It is called with a change event which contains the path, the Realm,
// a version of the Realm from before the change, and indexes indication all objects
// which were added, deleted, or modified in this change
function handleChange(changeEvent) {
  // Extract the user ID from the virtual path, assuming that we're using
  // a filter which only subscribes us to updates of user-scoped Realms.
  var matches = changeEvent.path.match(/^\/([0-9a-f]+)\/private$/);
  var userId = matches[1];
 
  var realm = changeEvent.realm;
  var coupons = realm.objects('Coupon');
  var couponIndexes = changeEvent.changes.Coupon.insertions;
 
  for (var couponIndex of couponIndexes) {
    var coupon = coupons[couponIndex];
    if (coupon.isValid !== undefined) {
      var isValid = verifyCouponForUser(coupon, userId);
      // Attention: Writes here will trigger a subsequent notification.
      // Take care that this doesn't cause infinite changes!
      realm.write(function() {
        coupon.isValid = isValid;
      });
    }
  }
}
 
// create the admin user
var adminUser = Realm.Sync.User.adminUser(adminToken);
 
// register the event handler callback
Realm.Sync.addListener(SERVER_URL, adminUser, NOTIFIER_PATH, 'change', handleChange);
 
console.log('Listening for Realm changes');

This example listens for changes to a user-specific private Realm at the virtual path /~/private (the tilde represents a wildcard). It will look for updated Coupon objects in these Realms, verify their coupon code if it wasn’t verified yet, and write the result of the verification into the isValid property of the Coupon object.

Upgrading to the Enterprise Edition

To upgrade, you’ll need to get an access token from Realm from email. First you’ll stop and uninstall the Realm Object Server Professional Edition, keeping the data in the Realms intact, then install the Enterprise Edition. This work can be done from the Unix shell prompt on your server.

SSH to the server running Realm Object Server, and type the following commands. (Lines beginning with # are comments and don’t need to be entered.)

# Stop the service before uninstalling
sudo systemctl stop realm-object-server

# Uninstall the Professional Edition
sudo apt-get remove realm-object-server-professional

# Set up your new Enterprise Edition token
export PACKAGECLOUD_TOKEN=<the token you received via email>

# Set up the new package repository
curl -s https://$PACKAGECLOUD_TOKEN:@packagecloud.io/install/repositories/realm/ros-enterprise/script.deb.sh | sudo bash
sudo apt-get update

# Install the Realm Object Server
sudo apt-get install realm-object-server-enterprise

# Enable and start the service
sudo systemctl enable realm-object-server
sudo systemctl start realm-object-server

You should be able to browse to the same IP address and port and view the Realm Object Server dashboard using the same admin account and view the Realms you created previously in this walkthrough.

Adding Sync Workers

The sync worker is the process that stores Realms, responds to Realm mobile clients, and merges changes with Realm’s conflict resolution algorithm. The Enterprise Edition of the Realm Object Server comes with a sync worker installed on the server in the default configuration file. In some cases you may want to separate the sync workers from the Realm Object Server, which handles the authentication and routing of incoming realm mobile client requests, for security, fault tolerance, and load separation concerns. Let’s add an additional sync worker to our architecture.

The first thing you need to do is spin up a new EC2 instance for your new sync worker. Follow the instructions at the beginning of this walkthrough and SSH to the new instance, then install the realm-sync-worker package from Packagecloud.

# Set up the Enterprise Edition token
export PACKAGECLOUD_TOKEN=<the token you received via email>

# Set up the new package repository
curl -s https://$PACKAGECLOUD_TOKEN:@packagecloud.io/install/repositories/realm/ros-enterprise/script.deb.sh | sudo bash
sudo apt-get update

# Install the Sync Worker
sudo apt-get install realm-sync-worker

# Enable and start the service
sudo systemctl enable realm-sync-worker
sudo systemctl start realm-sync-worker

Now, the sync worker is installed on a separate host. We need to edit the Realm Object Server’s configuration file to connect to the new sync worker. SSH back to the Realm Object Server’s instance, and open /etc/realm/configuration.yml in your favorite text editor.

Find the Sync section, uncomment the following lines, and add the internal DNS or IP address of your newly created server.

# /etc/realm/configuration.yml
 
sync:
  servers:
    - id: '0'
    address: '<internal DNS or IP of sync worker server>'
    port: 7800

The underlying IP address of the sync-worker can change as long as the ID of the sync worker remains the same, so in a failover scenario—bringing a new sync-worker online because an existing one has failed—you can just keep the ID value and spin up the new worker. In fact, if you use a DNS name, you could simply update your name server to point to the sync worker’s new address in the event of failure. For more information, read the Failover documentation.

Now that you’ve edited the configuration file, restart the Object Server. At the shell prompt:

systemctl restart realm-object-server

If you look at your system logs right now, you’ll see errors:

[syncProxy] internal error: Error: connect ECONNREFUSED

This is because we haven’t configured the sync worker to listen to the connection yet. We’ll do that in a moment, but there’s one other thing to do while on the Object Server’s machine: you’re going to need to copy its public key. This file is stored in /etc/realm/token-signature.pub. This file needs to match on both the sync worker and Object Server. You can copy the file using scp:

scp /etc/realm/token-signature.pub sync-worker-ip:/etc/realm/token-signature.pub

(Replace sync-worker-ip with the actual IP address or DNS name of the sync worker machine.) You can also use FTP to transfer the key, or even just copying the public key on the Object Server machine to your clipboard and pasting it into /etc/realm-token-signature.pub on the sync worker.

Now, let’s SSH back to the sync worker, and edit its configuration file, /etc/realm/sync-worker-configuration.yml, to uncomment the sync section in its network block:

# /etc/realm/sync-worker-configuration.yml
 
network:
	sync:
		listen_address: '0.0.0.0'

Note that 0.0.0.0 listens on all network interfaces; in practice, it’s more secure to specify the actual IP of the Realm Object Server.

Now, finally, restart the sync worker.

systemctl restart realm-sync-worker

You can follow this process to add more than one sync worker, spreading out the load for horizontal scaling.

Adding Backup Clients

For fault tolerance, you will want to regularly backup the realm data in the event of system failure or outage. Realm provides a continuous backup feature where each changeset synced to the sync worker is sent to the backup client.

To set up a backup client, the process is similar to setting up a sync worker:

# Set up the Enterprise Edition token
export PACKAGECLOUD_TOKEN=<the token you received via email>

# Set up the new package repository
curl -s https://$PACKAGECLOUD_TOKEN:@packagecloud.io/install/repositories/realm/ros-enterprise/script.deb.sh | sudo bash
sudo apt-get update

# Install the Sync Worker
sudo apt-get install realm-object-server-backup-client

Edit the backup client’s configuration file before starting it:

# /etc/realm/object-server-backup-client.yml
 
sync:
  servers:
    - id: '0'
    address: '<internal DNS or IP of sync worker server>'
    port: 7800

Now, start the client:

# Enable and start the service
sudo systemctl enable realm-object-server-backup-client
sudo systemctl start realm-object-server-backup-client

Finally, SSH to the sync worker and uncomment/modify its listen_address line:

# /etc/realm/sync-worker-configuration.yml
 
network:
	sync:
		listen_address: '0.0.0.0'

Restart the sync worker.

systemctl restart realm-sync-worker

The backup client now has the full data set of all Realms set to the sync worker.

As a best practice, you should prepare to fail over to the backup client and start a sync worker on it so it can take over for a sync worker. To do this, you should pre-install the sync worker on the backup client without starting it. SSH to the backup client.

sudo apt-get install realm-sync-worker

Edit the configuration of the sync worker so it starts reading Realms from the same folder that the backup client is saving them to.

# /etc/realm/sync-worker-configuration.yml
 
# change the root_path to point to the backup client folder
storage:
  root_path: '/var/lib/realm-object-server-backup-client'
 
# uncomment and correctly set the listen_address line
network:
  sync:
    listen_address: '0.0.0.0'

Now, in the event your original sync worker fails, you will be ready for failover by simply stopping the backup client process and starting the sync worker you have prepared.

sudo systemctl stop realm-object-server-backup-client
sudo systemctl start realm-sync-worker

For more details, read Failover in the Professional/Enterprise Edition documentation.

After failing over, you’ll need to start a new backup client that points to this server, perhaps triggered automatically through your tooling (e.g., an Auto Scaling Group resource that sends CloudFormation event notifications).

Congratulations — you’re done!

By now, we’ve shown you how to start and deploy the Realm Mobile Platform, integrate it into your apps to enable syncing and logins, add server-side Functions and event handling, and add additional sync workers and backup clients. We’ve covered the full depth of Enterprise Edition features, and you can return to this document whenever you need to a quick reference for your future projects. Good luck with your development work — and please feel free to contact your representative at Realm any time you need help or get stuck.

Get started with the Realm Mobile Platform!

We have what you need to build better apps, faster. Learn more about which Realm Mobile Platform solution is right for your needs and get started today!


Realm Team

Realm Team

At Realm, our mission is to help developers build better apps faster. We provide a unique set of tools and platform technologies designed to make it easy for developers to build apps with sophisticated, powerful features — things like realtime collaboration, augmented reality, live data synchronization, offline experiences, messaging, and more.

Everything we build is developed with an eye toward enabling developers for what we believe the mobile internet evolves into — an open network of billions of users and trillions of devices, and realtime interactivity across them all.