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.
​
Before we get started we need a few things setup; the prerequisites for this project are:
​Xcode 9.0 or later.
An environment set up to run React Native applications. If you don't have that, please follow the React Native instructions to set up your environment before you do more here.
​React Native CLI​
The URL of your Realm Cloud instance (you can get it by logging in to the cloud portal and clicking the Copy Instance URL
link).
​Realm Studio (optional) -- throughout the tutorial we will test components of the sync process where directly viewing and editing the Realm Object Server is helpful.
Enabled nickname authentication provider: this project uses the deprecated nickname authentication provider, which you must opt-in to use. To opt in,
Log in to https://cloud.realm.io.
Select your instance.
Go to the "Settings" tab.
Under "Authentication", click to expand "Nickname".
Click "Enable" and accept the prompt.
If you forget to enable nickname authentication and run the app anyway, you will see the following fatal error: "Your request parameters did not validate. provider: Invalid parameter 'provider'!".
Please note that the nickname authentication provider is only intended for development purposes. If you intend to use the same instance for production, you should disable the nickname authentication provider after you are done with this sample app.
We will make a simple work management application called "ToDo", which will allow users to create "projects". Within each project users will be able to create any number of associated "tasks".
Realm works by defining a data model that configures the schema of a "realm". A realm is an instance of a Realm Mobile Database container. In this project, our client realm will connect and sync data with the centralized Realm Object Server through a process called query-based synchronization.
Navigate to your desktop or a directory your would like to save our project to. On the command line create a new React Native project. Let's name it "ToDo".
react-native init ToDo
This will create the basic application structure and a package.json
file for our dependencies.
Before we dive into adding code, it's a good idea to make sure that the environment is configured correctly and that we can successfully compile the app template we just created.
Navigate to the project directory > ios
and open ToDo.xcodeproj
. Start the new application by selecting a simulator and clicking Build/Run icon. Your new app should look like the screen below.
To use Realm Cloud in your React Native app, you'll need to add the Realm
framework to the project. This can most easily be accomplished using NPM.
Inside your project directory, add the realm
package with the command:
npm install --save realm
Next, link your project to the realm
native module:
react-native link realm
If you receive a node-pre-gyp install --fallback-to-build
error when running your app, it is possible your version of Node is incompatible with Realm. Either upgrade or downgrade your installation to the latest LTS.
From this point you can use a text editor of your choice to edit the JS files directly (we recommend VS code). In your project directory create a new folder called src
. Open the folder and create a schemas.js
file. Just like any other database system, we will need to give each property a type, and establish a primary key to reference specific objects. Copy the following models into our newly created schema.js
file:
It is important that you use the 'copy to clipboard' link in the top right of each snippet instead of highlighting and copying. This prevents formatting errors.
schemas.jsexport const Project = {name: "Project",primaryKey: "projectId",properties: {projectId: "string",owner: "string",name: "string",timestamp: "date",items: "Item[]"}};​export const Item = {name: "Item",primaryKey: "itemId",properties: {itemId: "string",body: "string",isDone: "bool",timestamp: "date"}};
Create a new javascript file called constants.js
inside thesrc
directory. Open constants.js
and paste in the code snippet below. Replace MY_INSTANCE_ADDRESS
with your own Realm Object Server address. We will access this file later when we configure our first realm.
constants.js// **** Realm Cloud Users:// **** Replace MY_INSTANCE_ADDRESS with the hostname of your cloud instance// **** e.g., "mycoolapp.us1.cloud.realm.io"// ****// ****// **** ROS On-Premises Users// **** Replace the SERVER_URL string with the fully qualified versions of// **** address of your ROS server, e.g.: "http://127.0.0.1:9080".const MY_INSTANCE_ADDRESS = "YOUR-INSTANCE-ID.cloud.realm.io"; // <- update thisexport const SERVER_URL = `https://${MY_INSTANCE_ADDRESS}`;
NOTE: The Realm Cloud Portal presents fully specified URLs (e.g., https://appname.cloud.realm.io); be sure to paste in only the host name part (e.g., appname.cloud.realm.io) into your copy of the constants.js
file.
Next we are going to create a welcome page where we will authenticate the user. This will be the page you see when the app starts up and will be used to log in to the app and connect your application to the Realm Cloud. Inside our src
directory create a new folder called components
. This will contain all of our components and pages.
Before we set out to complete our LoginForm
component, which will house our authentication logic, we need to create two other components which the LoginForm
will depend on--a custom ModalView
and a Button
.
Create a new javascript file called ModalView.js
inside of our new components
folder. We will greet the user with a modal asking for a login nickname that we will use to connect to the Realm Object Server. To do this we will need to import modals from the React Native Community:
npm install --save react-native-modal
You can read more about the dependency here.
Import React
and Component
from the 'react' library for the basic building blocks of our class. Import the Modal
component from 'react-native-modal' for our render()
method. We will also import PropTypes
for our state, thenStylesheets
, View
, Text
, and TextInput
from the 'react-native' library.
ModalView.jsimport PropTypes from "prop-types";import React, { Component } from "react";import { View, Text, TextInput } from "react-native";import Modal from "react-native-modal";import { StyleSheet } from "react-native";​const white = "white";​const styles = StyleSheet.create({content: {flexDirection: "column",backgroundColor: white,padding: 16,justifyContent: "center",alignItems: "center",borderRadius: 4},input: {width: "100%",textAlign: "center"},buttons: {flexDirection: "row",marginTop: 10}});​//component will be added laterimport { Button } from "./Button";​export class ModalView extends Component {render() {}}
When we create a ModalView
component in our LoginForm we will pass several props to it. The placeholder
and confirmLabel
will be text passed in to customize the prompt of the Modal. The error
prop will display the state error and isModalVisible
is a boolean that determines if the modal is active. Finally, handleSubmit
passes in the method to be called for submission.
We will add three methods to this class. First, componentDidUpdate
will reset the text
state when the modal becomes visible. The onChangeText
will update the text
state when the input is changed by the user. And onConfirm
will fire the handleSubmit
prop.
ModalView.js//...//import statements and stylesheet//...​export class ModalView extends Component {static propTypes = {confirmLabel: PropTypes.string,error: PropTypes.object,handleSubmit: PropTypes.func,isModalVisible: PropTypes.bool,placeholder: PropTypes.string,toggleModal: PropTypes.func};​state = {text: ""};​componentDidUpdate(prevProps) {// Reset the text state when the modal becomes visibleif (!prevProps.isModalVisible && this.props.isModalVisible) {this.setState({ text: "" });}}​render() {const {confirmLabel,error,isModalVisible,placeholder,toggleModal} = this.props;​return (<Modal isVisible={isModalVisible}><View style={styles.content}>{error && <Text>{error.message}</Text>}<TextInputstyle={styles.input}autoFocus={true}placeholder={placeholder}onChangeText={this.onChangeText}value={this.state.text}onSubmitEditing={this.onConfirm}/><View style={styles.buttons}><Button onPress={this.onConfirm}>{confirmLabel || "Confirm"}</Button>{toggleModal ? <Button onPress={toggleModal}>Cancel</Button> : null}</View></View></Modal>);}​onChangeText = text => {this.setState({ text });};​onConfirm = () => {this.props.handleSubmit(this.state.text);};}
The Button
component that we imported earlier allows the parent to pass in variable confirm prompts. It builds off the React Native Elements toolkit, so you will need to install it with:
npm install --save react-native-elementsnpm install --save react-native-vector-iconsreact-native link react-native-vector-icons
Button.jsimport PropTypes from "prop-types";import React from "react";import { StyleSheet } from "react-native";import * as ReactNativeElements from "react-native-elements";​const styles = StyleSheet.create({container: {marginLeft: 6,marginRight: 6,marginTop: 4,marginBottom: 4}});​export const Button = ({ onPress, children, style }) => (<ReactNativeElements.ButtonbackgroundColor="lightblue"buttonStyle={style}containerViewStyle={styles.container}color="black"borderRadius={4}onPress={onPress}title={children}/>);​Button.propTypes = {onPress: PropTypes.func,children: PropTypes.string,style: PropTypes.object};
Now that we have the Button
and ModalView
components set up, we can create the LoginForm
. The LoginForm
is where we will receive the input from the user and make our first authenticated connection to the Realm Object Server. The skeleton for this component looks like this:
LoginForm.jsimport React, { Component } from "react";import { View, StyleSheet } from "react-native";//We will add "react-native-router-flux in the next stepimport { Actions } from "react-native-router-flux";import Realm from "realm";​import { SERVER_URL } from "../constants";import { Project, Item } from "../schemas";​import { Button } from "./Button";import { ModalView } from "./ModalView";​const styles = StyleSheet.create({container: {flex: 1,justifyContent: "center",alignItems: "center"},buttons: {flex: 1,justifyContent: "center",alignItems: "center"}});​export class LoginForm extends Component {state = {};​render() {}}
The first method that will fire within the class is componentWillMount
. Here we will check to see if there is a user logged in, and if not we will start the login process.
LoginForm.jscomponentDidMount() {// Check if we're already authenticatedif (Realm.Sync.User.current) {this.onAuthenticated(Realm.Sync.User.current);} else {this.setState({ isModalVisible: true });}}
Our render method draws upon the two components we created earlier:
LoginForm.jsrender() {// Show the modal if the user is not authenticatedconst isAuthenticated = !!Realm.Sync.User.current;​return (<View style={styles.container}><ModalViewplaceholder="Please Enter a Username"confirmLabel="Login"isModalVisible={!isAuthenticated}handleSubmit={this.handleSubmit}error={this.state.error}/>{isAuthenticated && (<View style={styles.buttons}><Button onPress={this.onLogout}>Logout</Button><Button onPress={this.onOpenProjects}>Go to projects</Button></View>)}</View>);}
When the modal is displayed to the user, the user has input a username, and has clicked confirm, the handleSubmit
method in our class will run. It should look like this:
LoginForm.jshandleSubmit = async nickname => {try {// Reset any previous errors that might have happenedthis.setState({ error: undefined });// Attempt to authenticate towards the serverconst user = await Realm.Sync.User.registerWithProvider(SERVER_URL, {provider: "nickname",providerToken: nickname});// Hide the modalthis.setState({ isModalVisible: false });this.onAuthenticated(user);} catch (error) {this.setState({ isModalVisible: true, error });}}
Realm supports a number of authentication methods: For prototyping (or low-security applications) We've chosen the Nickname
provider for this tutorial because it is convenient in during prototyping, since it does not require a password It should not be used in a production environment. You can read about the different authentication types Realm supports here. Nickname credentials are easily constructed with:
Realm.Sync.User.registerWithProvider()
Where provider
is set to nickname
. This method registers and signs in a user to the Realm Object Server associated with the address that you added earlier. The last portion of the method then calls onAuthenticated()
.
LoginForm.jsonAuthenticated(user) {// Create a configuration to open the default Realmconst config = user.createConfiguration({schema: [Project, Item]});// Open the Realmconst realm = new Realm(config);// Navigate to the main sceneActions.authenticated({ user, realm });}
The code snippet above does three things.
It creates a default configuration object with the schema we defined in schema.js
.
It opens the realm. This creates a connection to the Realm Object Server and allow us to read and write data.
It navigates to the app's next page while passing in the reference to the logged in user
and the openrealm
.
The last two methods of the class provide options to logout or proceed to the projects
screen.
LoginForm.jsonLogout = () => {if (Realm.Sync.User.current) {Realm.Sync.User.current.logout();this.forceUpdate();}};​onOpenProjects = () => {if (Realm.Sync.User.current) {this.onAuthenticated(Realm.Sync.User.current);}};
If we were to run the application now, you would still see the React-native default screen because we have not connected any of our components to the main AppRegistry. To do that, go to the main project directory and move the App.js
file to our component
folder. Once that's done, we will need to edit the index.js
file to import App.js
from ./src/components/App
instead of ./App
. The new line should look like:
index.js//...import { App } from "./src/components/App"//...
Now we will add the react-native-router-flux
dependency to our project:
npm install --save react-native-router-flux@4.0.0-beta.31
The dependency works by creating a hierarchy of scenes, where each scene is designated a key and a component. To navigate to any scene, you simply invoke the key and the desired component will be mounted with any attached props.
Replace the current contents of the App.js
file with:
App.jsimport React from "react";import { Scene, Stack, Router } from "react-native-router-flux";​import { LoginForm } from "./LoginForm";import { ProjectList } from "./ProjectList";//import { ItemList } from "./ItemList";​export const App = () => (<Router><Scene hideNavBar={true}><Scene key="login" component={LoginForm} title="Please Login" /><Stack key="authenticated"><Scene key="projects" component={ProjectList} title="Projects" />{/* <Scene key="items" component={ItemList} title="Items" /> */}</Stack></Scene></Router>);
To test the code that we currently have we will need to create a ProjectList
. For now let's create a placeholder.
ProjectList.jsimport PropTypes from "prop-types";import React, { Component } from "react";import { View, FlatList, Text, StyleSheet } from "react-native";import { Actions } from "react-native-router-flux";import { List, ListItem } from "react-native-elements";import { v4 as uuid } from "uuid";​const projectKeyExtractor = project => project.projectId;​const styles = StyleSheet.create({placeholder: {textAlign: "center",padding: 10}});​import { ModalView } from "./ModalView";//import { SwipeDeleteable } from "./SwipeDeleteable";​export class ProjectList extends Component {static propTypes = {user: PropTypes.object,realm: PropTypes.object};state = {dataVersion: 0,isModalVisible: false};render() {return(<View><Text>User identity is: {this.props.user.identity}</Text></View>)​}}
Let's make sure everything is working correctly. Click the build/run button in Xcode. The result should look similar to this:
Now we will fill out the ProjectList, implement a process so the user can create projects, and query the realm for data.
Most of our logic for fetching the project
data will be in the componentDidMount
below. On line 5 we add a navigation button allowing the user to create a new project object. If it is toggled true
we use the same ModalView
as we did in the LoginForm
. Line 13 creates a query that will return a results object filtered by project author. On line 19 we add a listener that will notify React of changes. On line 26 we subscribe to the query we made on 13, which will tell the Realm Object Server to sync that data to our device's realm.
ProjectList.jscomponentDidMount() {const { realm } = this.props;​// Register an action to create a projectActions.refresh({rightTitle: " Create",onRight: () => {this.toggleModal();}});​// Get a result containing all projectsconst projects = realm.objects("Project").filtered("owner == $0", this.props.user.identity).sorted("timestamp", true);​// When the list of projects changes, React won't know about it because the Result object itself will not change.projects.addListener(() => {// Bump a data version counter that we'll pass to components that should update when the projects change.this.setState({ dataVersion: this.state.dataVersion + 1 });});​// Create a subscription and add a listener// Remember to remove the listener when component unmountsthis.subscription = projects.subscribe();this.subscription.addListener(this.onSubscriptionChange);​// Update the state with the projectsthis.setState({ projects });}
In our render()
method we utilize the FlatList
component, which receives our data in the project
state variable and uses the renderProject
method to create each ListItem
.
ProjectList.jsrender() {const { dataVersion, isModalVisible, projects } = this.state;return (<View>{!projects || projects.length === 0 ? (<Text style={styles.placeholder}>Create your first project</Text>) : (<List><FlatListdata={projects}extraData={dataVersion}renderItem={this.renderProject}keyExtractor={projectKeyExtractor}/></List>)}<ModalViewplaceholder="Please Enter a Project Name"confirmLabel="Create Project"isModalVisible={isModalVisible}toggleModal={this.toggleModal}handleSubmit={this.onProjectCreation}/></View>);}
Below is the code for renderProject
.
ProjectList.jsrenderProject = ({ item }) => (<SwipeDeleteablekey={item.projectId}onPress={() => {this.onProjectPress(item);}}onDeletion={() => {this.onProjectDeletion(item);}}><ListItemtitle={item.name}badge={{value: item.items.length}}hideChevron={true}/></SwipeDeleteable>);
SwipeDeleteable
is another custom component we will add after the ProjectList
.
Next we will add the following five methods:
ProjectList.jsonProjectCreation = projectName => {const { user, realm } = this.props;// Open a write transactionrealm.write(() => {// Create a projectrealm.create("Project", {projectId: uuid(),owner: user.identity,name: projectName,timestamp: new Date()});});// Reset the statethis.setState({ isModalVisible: false });};​onProjectPress = project => {const { user, realm } = this.props;Actions.items({ project, realm, user, title: project.name });};​onProjectDeletion = project => {const { realm } = this.props;// Open a write transactionrealm.write(() => {// Delete the projectrealm.delete(project);});};​onSubscriptionChange = () => {// Realm.Sync.SubscriptionState.Complete// Realm.Sync.SubscriptionState.Error};​toggleModal = () => {this.setState({ isModalVisible: !this.state.isModalVisible });}
onProjectCreation
is the process of adding data to the realm. Once the LoginForm passes the realm object as a prop, we can open a write transaction to it and begin creating objects.
onProjectPress
navigates to the items associated with a project the user has selected.
onProjectDeletion
opens a write transaction to delete data from the realm.
When the component is unmounted you will want to remove all listeners.
ProjectList.jscomponentWillUnmount() {const { projects } = this.state;if (this.subscription) {// Remove all listeners from the subscriptionthis.subscription.removeAllListeners();}if (projects) {projects.removeAllListeners();}}
The last custom component we will add is the SwipeDeletable
. Create a new SwipeDeletable.js
file in the components
folder and paste in the following code. More can be read about the panResponder
here.
SwipeDeleteable.jsimport PropTypes from "prop-types";import React, { Component } from "react";import { Animated, PanResponder } from "react-native";​export class SwipeDeleteable extends Component {static propTypes = {onDeletion: PropTypes.func.isRequired,onPress: PropTypes.func};​_panResponder = PanResponder.create({onStartShouldSetPanResponder: () => true,onPanResponderMove: (e, gestureState) => {this._offset.setValue(gestureState.dx);},onPanResponderRelease: (e, gestureState) => {if (gestureState.dx === 0 && this.props.onPress) {// We consider it a press if the user didn't panthis.props.onPress();} else {// Delete the item if the gesture is released 30% to either sideconst isDeletion = Math.abs(gestureState.dx) > this._width / 3;// If the item is deleted, complete the swipe in the correct directionconst toValue = isDeletion? gestureState.dx > 0? this._width: -this._width: 0;// Apply a sprint to the offsetAnimated.spring(this._offset, {toValue,speed: 48}).start();// Delete the item right awayif (isDeletion) {this.props.onDeletion();}}}});​_offset = new Animated.Value(0);​render() {const { children } = this.props;return (<Animated.Viewstyle={{transform: [{translateX: this._offset}]}}onLayout={this.onLayout}{...this._panResponder.panHandlers}>{children}</Animated.View>);}​onLayout = event => {this._width = event.nativeEvent.layout.width;};}
Now we will add the ItemList
. It shares several similarities with the ProjectList
, but has a few key differences.
In our project model, each Project object contains a list property called items
. The ItemList
associates item objects with the parent Project. This is done in a write transaction at the creation of each Item object.
The code for the ItemList is as follows:
ItemList.jsimport PropTypes from "prop-types";import React, { Component } from "react";import { View, FlatList, Text, StyleSheet } from "react-native";import { Actions } from "react-native-router-flux";import { List, ListItem } from "react-native-elements";import { v4 as uuid } from "uuid";​const styles = StyleSheet.create({placeholder: {textAlign: "center",padding: 10}});​const itemKeyExtractor = item => item.itemId;​import { ModalView } from "./ModalView";import { SwipeDeleteable } from "./SwipeDeleteable";​const checkedIcon = {name: "check-box",color: "#555"};​const uncheckedIcon = {name: "check-box-outline-blank",color: "#555"};​export class ItemList extends Component {static propTypes = {user: PropTypes.object,realm: PropTypes.object,project: PropTypes.object};​state = {dataVersion: 0,isModalVisible: false};​componentDidMount() {const { project } = this.props;​// Register an action to create an itemActions.refresh({title: project.name,rightTitle: " Create",onRight: () => {this.toggleModal();}});​// Get a result containing all itemsconst items = project.items.sorted("timestamp");​// When the list of items changes, React won't know about it because the Result object itself will not change.items.addListener(() => {// Bump a data version counter that we'll pass to components that should update when the items change.this.setState({ dataVersion: this.state.dataVersion + 1 });});​// No need to create a subscription here:// We assume another subscription is created for all the users projects already.​// Update the state with the itemsthis.setState({ items });}​componentWillUnmount() {const { items } = this.state;// Remove all listeners from the subscriptionif (this.subscription) {this.subscription.removeAllListeners();}// Remove all listeners from the itemsif (items) {items.removeAllListeners();}}​render() {const { dataVersion, isModalVisible, items } = this.state;return (<View>{!items || items.length === 0 ? (<Text style={styles.placeholder}>Create your first item</Text>) : (<List><FlatListdata={items}extraData={dataVersion}renderItem={this.renderItem}keyExtractor={itemKeyExtractor}/></List>)}<ModalViewplaceholder="Please enter a description"confirmLabel="Create Item"isModalVisible={isModalVisible}toggleModal={this.toggleModal}handleSubmit={this.onItemCreation}/></View>);}​renderItem = ({ item }) => (<SwipeDeleteablekey={item.itemId}onDeletion={() => {this.onDeletion(item);}}><ListItemtitle={item.body}rightIcon={item.isDone ? checkedIcon : uncheckedIcon}onPressRightIcon={() => {this.onToggleDone(item);}}/></SwipeDeleteable>);​onSubscriptionChange = () => {// Realm.Sync.SubscriptionState.Complete// Realm.Sync.SubscriptionState.Error};​toggleModal = () => {this.setState({ isModalVisible: !this.state.isModalVisible });};​onItemCreation = body => {const { realm, project } = this.props;// Open a write transactionrealm.write(() => {// Create a projectconst item = realm.create("Item", {itemId: uuid(),body,isDone: false,timestamp: new Date()});// Add the item to the projectproject.items.push(item);});// Reset the statethis.setState({ isModalVisible: false });};​onToggleDone = item => {const { realm } = this.props;// Open a write transactionrealm.write(() => {// Toggle the item isDoneitem.isDone = !item.isDone;});};​onDeletion = item => {const { realm } = this.props;// Open a write transactionrealm.write(() => {// Delete the itemrealm.delete(item);});};}
To see the entire app cycle, uncomment the Scene
components in the App.js
file and the SwipeDeletable
import in ProjectList.js
file.
Up to now nothing prevents a malicious user from seeing another user's projects by subscribing with a query that matches a broader set of Project
instances.
To address this issue we will instead use Realm's permission system to limit access to each Project
instance so only the user that created it can read, modify, or delete it.
Permissions are assigned to a collection of zero or more users named a role. Since we want a given Project
to be accessible only to a single user, we need to use a separate role for each user. We can then grant the current user's role the appropriate permissions when we create a new Project
. This will have the effect of preventing all other users from seeing or modifying that Project
instance.
By default, every logged-in user has a private role created for them. This role can be accessed at PermissionUser.role
. We'll use this role when creating new projects. a
Next we add a permissions
attribute to the Project
model. This tells Realm that we wish to configure the permissions of each Project
instance independently, and serves as the means for doing so.
export const Project = {name: "Project",primaryKey: "projectId",properties: {//...permissions: "__Permission[]"}};
The Permission
class represents the set of privileges we want to grant and the role to which they should be granted. Since we want to limit each Project
to being visible to only a single user, we will grant the current user's role the appropriate permissions when we create a new Project
. This will have the result of preventing all other users from seeing or modifying the new Project
instance.
Inside of the write transaction of ProjectList.js
's onObjectCreation()
method we will edit the currently created project's permissions.
ProjectList.jsonProjectCreation = projectName => {const { user, realm } = this.props;​// Open a write transactionrealm.write(() => {// Create a projectconst project = realm.create("Project", {// Create properties});​// Fetches the system userconst permissionUser = realm.objects("__User").filtered(`id == '${user.identity}'`)[0];​// Create and set permissionsconst permission = realm.create(Realm.Permissions.Permission, {role: permissionUser.role,canRead: true,canUpdate: true,canDelete: true,})​// Add permission to projectproject.permissions.push(permission);});
By stating that the current user's role can read, update, and delete the Project
, we prevent any other user from having access to the Project
.
By default Realm Cloud creates a set of permissive default permissions and roles. This allows getting started on developing your application without first having to worry about permissions and roles. However, these permissive default permissions should be tightened before deploying your application.
By default, all users are added to an everyone
role, and this role has access to all objects within the Realm. As part of lowering the default permissions, we will limit the access of this everyone
role.
In this demo, we lower the permissions inside of a node script that must be run by an admin user. You will need to import the constants and schemas with an ES6 converter or paste them into the script.
Class level permissions
We use permissions to limit querying to only the Project
type. This means that the only Item
objects that can be synchronized are those associated with a Project
that we have permissions to access. Additionally, we remove the ability for everyone
to change the permissions of each of our model classes.
// Authenticate User// `username` and `password` must be credentials for an Admin userRealm.Sync.User.login(SERVER_URL, username, password).then(user => {// Create a configuration to open the default Realmconst config = user.createConfiguration({schema: [Project, Item]})// Open RealmRealm.open(config).then(realm => {realm.write(() => {// This fetches the system `__Class` called `__Role` and prevents users from maliciously// adding themselves to a role.const rolePermission = realm.objects("__Class").filtered("name == '__Role'")[0].permissions[0];rolePermission.canUpdate = false;rolePermission.canCreate = false;​// This lowers the permissions on the 'everyone' role for `Project` objectsconst projectPermissions = realm.objects("__Class").filtered("name == 'Project'")[0].permissions[0];projectPermissions.canSetPermissions = false;​// This lowers the permissions on the 'everyone' role for `Item` objectsconst itemPermissions = realm.objects("__Class").filtered("name == 'Item'")[0].permissions[0];itemPermissions.canSetPermissions = false;itemPermissions.canQuery = false;​// Lock the permissions and schemaconst everyonePermission = realm.objects("__Realm")[0].permissions[0]everyonePermission.canModifySchema = false;everyonePermission.canSetPermissions = false;})})})
Finally we use permissions to prevent any non-admin users from modifying our schema or Realm -level permissions.
const everyonePermission = realm.objects("__Realm")[0].permissions[0]everyonePermission.canModifySchema = false;everyonePermission.canSetPermissions = false;
You now have a simple implementation of the query-based sync process within react-native. You can read more about the process of how syncing data works through the query-based synchronization process, including information on subscriptions, queries, and notification tokens, by following this link.
To build on the access-control concepts mentioned in this tutorial, please see this article.
If you're experiencing errors or would like to compare to the original, the source for this project can be found here.