A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://www.mongodb.com/docs/atlas/app-services/tutorial/react-native/ below:

Tutorial: Atlas Device Sync for React Native - Atlas App Services

Estimated time to complete: 30 minutes, depending on your experience with React Native

You can use the Realm React Native SDK and @realm/react to build a mobile application with React Native. This tutorial walks you through how to build your own app that uses Flexible Sync.

For this tutorial, we'll start with a pre-built TypeScript template application to see how everything fits together.

The app is a pre-built template that includes a working React Native application (frontend) and its corresponding App Services App configuration files (backend).

The template app is a basic to-do list application that lets users do various things to manage their tasks:

After you've got the template app running, you will add a new priority field to the existing Item model and update the Flexible Sync subscription to only show items within a range of priorities. This example illustrates how you might adapt the template app for your own needs. You would not necessarily make this change given the current structure of the template app.

The template app provides a toggle that simulates the device being in an offline mode. This toggle lets you quickly test Device Sync functionality, emulating the user having no internet connection. However, you would likely remove this toggle in a production application.

Note Check Out the Quick Start

If you prefer to explore on your own rather than follow a guided tutorial, check out the React Native Quick Start. It includes copyable code examples and the essential information that you need to set up a React Native app with Atlas Device Sync.

This tutorial is based on the React Native SDK Flexible Sync Template App named react-native.todo.flex. We start with the default app and build new features on it.

To learn more about the Template Apps, see Template Apps.

If you don't already have an Atlas account, sign-up to deploy a Template App.

Follow the procedure described in the Create an App Services App guide, and select Create App from Template. Select the Real-time Sync template. This creates an App Services App pre-configured to use with one of the Device Sync template app clients.

After you create a template app, the UI displays a modal labeled Get the Front-end Code for your Template. This modal provides instructions for downloading the template app client code as a .zip file or using App Services CLI to get the client.

After selecting the .zip or App Services CLI method, follow the on-screen instructions to get the client code. For this tutorial, select the JavaScript (React Native) client code.

Note

The default Windows ZIP utility may show the .zip file as empty. If you encounter this, use one of the third-party zip programs that are available.

The appservices apps create command sets up the backend and creates a React Native template app for you to use as a base for this tutorial.

Run the following command in a terminal window to create an app named "MyTutorialApp" that is deployed in the US-VA region with its environment set to "development" (instead of production or QA).

appservices app create \  --name MyTutorialApp \  --template react-native.todo.flex \  --deployment-model global \  --environment development

The command creates a new directory in your current path with the same name as the value of the --name flag.

You can fork and clone a GitHub repository that contains the Device Sync client code. The React Native client code is available at https://github.com/mongodb/template-app-react-native-todo.

If you use this process to get the client code, you must create a template app to use with the client. Follow the instructions at Create a Template App to use the Atlas App Services UI, App Services CLI, or Admin API to create a Device Sync template app.

Use the following steps to get the template app up and running on your computer:

In your terminal, go to the directory that contains the client code. If you created the app with the App Services CLI, go to MyTutorialApp/react-native.todo.flex. Otherwise, go to the root of your downloaded or cloned project. Then run following commands to navigate to install the app's dependencies:

To build and run the app on an iOS device or simulator, install the additional iOS dependencies with CocoaPods.

At this point, you should have a fully functional React Native app that can run on iOS, Android, or both.

If you encounter an error or otherwise have issues, make sure that your React Native environment is set up correctly. Refer to the official React Native development environment setup guide. Follow all the steps for your Development OS and Target OS.

To make sure that everything works on iOS, build the app and run it in an iOS simulator:

To make sure that everything works on Android:

  1. Start an Android emulator. See Run apps on the Android Emulator for details on how to do this.

  2. Build the app on the emulator:

    One common error is Error: spawn ./gradlew EACCES. This means the project file permissions aren't what they need to be. On MacOS, you can fix this by entering chmod 755 android/gradlew in your terminal.

When the build completes, you should have a functional app running on your simulator. In the app, register a new account and test the features:

If you connect to your Atlas Cluster and query the todo.Item collection you can see your App's data. As long as the React Native app is not in offline mode, new data and changes in the app automatically sync to the todo.Item collection.

Similarly, any changes in the collection automatically sync down to the React Native app. Try changing an item's completion status in your cluster - the React Native app will automatically update with the new value whenever a network connection is available.

Now that you have the template app running let's dive into the code to see what we're working with.

The template app includes a fully configured App Services App in the backend directory. It has a unique appId value in atlasConfig.json that client applications use to connect.

It also includes the following pre-defined configurations:

The React Native app is a fully-configured mobile client that can run on iOS and Android devices.

The app uses the @realm/react library. The library includes React hooks and components that streamline working with your Atlas backend and Realm database.

The app contains some configuration files and directories, but you can ignore those unless you want to customize the app. For this tutorial, you should be familiar with the React components in the source/ directory:

File Name

Description

ItemSchema.tsx

The Item class, including its object data model. We import this class in AppWrapper.tsx to include it in the app's overall Realm schema.

AppWrapper.tsx

This is the root component for the app. It functions as a wrapper component and contains all of the @realm/react providers. This is where you configure your realm and your connection to your Atlas backend.

App.tsx

Most of the app's functionality is contained in this component and its children. Because the @realm/react providers are wrapped around this component, it can access an instance of your Atlas backend, user objects, and interact with the Realm database.

WelcomeView.tsx

The user registration and login form that users see when they first open the app.

ItemListView.tsx

The main to-do list app that users interact with after they log in. It queries for Item Realm objects and displays them in a list. It also includes the code to create new Item objects and store them in Realm.

CreateToDoPrompt.tsx

A UI form that lets us enter data for new Item objects. The code that actually creates new objects is in ItemListView.tsx.

LogoutButton.tsx

A reusable button that logs out an authenticated user.

OfflineModeButton.tsx

A reusable button that simulates an offline mode by pausing and resuming the current Realm syncSession.

Now that you're more familiar with what's already provided in the template app, let's write some code to implement a new feature.

For this tutorial, we'll add a new priority property to the Item objects. This will let us organize to-dos by how important they are and allow us to focus only on the most important ones.

We want to allow a small number of named priority levels, and we want to easily be able sort the levels. To do this, we'll use a helper function to define an enum object that maps a set of ordered level names to and from an integer that represents their priority.

Add the following code directly under the import statements in source/ItemSchema.tsx:

function createEnum(arr) {  arr.forEach((p, i) => arr[p] = i);  return arr;}export const Priority = createEnum([  "Severe",  "High",  "Medium",  "Low",])

The priority levels in the enum are ordered from most important to least. The corresponding index value for each level increases from the most important, Priority[0], to the least important, Priority[3]. This means that a higher priority level (meaning more important) has a lower index value.

Now we have an enum that defines the possible values of the priority field. However, we still have to define the priority field in the Item class.

Add the following lines to your code in source/ItemSchema.tsx to add priority to the Item data model:

export class Item extends Realm.Object<Item> {  _id!: BSON.ObjectId;  isComplete!: boolean;  summary!: string;  owner_id!: string;  priority!: string;  static schema: Realm.ObjectSchema = {    name: 'Item',    primaryKey: '_id',    properties: {            _id: {type: 'objectId', default: () => new BSON.ObjectId()},            isComplete: {type: 'bool', default: false},      summary: 'string',      owner_id: 'string',      priority: {                type: 'int',        default: Priority.High      },    },  };}
Note Why Didn't This Break Sync

At this point, your React Native Item model and its corresponding schema in your App Services App no longer agree. That's okay!

Adding a property to a Realm object is not a breaking change and therefore does not require a client reset. The template app has Development Mode enabled, so changes to the client Realm object are reflected in the server-side schema. For more information, see Development Mode and Update Your Data Model.

Your app's data model now includes a priority for each Item object. Let's update the app UI so that you can choose a priority value when you add a new to-do to the list.

First, we'll install an external library to implement the priority picker component. Run the following in your terminal inside of your project root:

npm install @react-native-picker/picker

If you're building for iOS, make sure to link the associated Cocoapods after you've installed the package:

Tip

You may need to rebuild your app after installing. To do so, stop the bundler for your project and then run the build command:

Now that the package is fully installed, let's update the new to-do creation prompt component to use the picker.

Add the following imports to the top of source/CreateToDoPrompt.tsx:

import {Picker} from '@react-native-picker/picker';import {Priority} from './ItemSchema';

Then, modify the CreateToDoPrompt component:

source/CreateToDoPrompt.tsx

type Props = {  onSubmit(args: {summary: string; priority: string;}): void;};export function CreateToDoPrompt(props: Props): React.ReactElement<Props> {  const {onSubmit} = props;  const [summary, setSummary] = useState('');  const [priority, setPriority] = useState(Priority.High);  return (    <View style={styles.modalWrapper}> <Text h4 style={styles.addItemTitle}> Add To-Do Item </Text> <Input placeholder="What do you want to do?" onChangeText={(text: string) => setSummary(text)} autoCompleteType={undefined} /> <Picker style={{width: '80%'}} selectedValue={priority} onValueChange={value => setPriority(value)}> {Priority.map(priority => ( <Picker.Item key={priority} label={priority} value={Priority[priority]} /> ))} </Picker> <Button title="Save" buttonStyle={styles.saveButton} onPress={() => onSubmit({summary, priority})} /> </View>  );}

In source/ItemListView.tsx, modify the createItem() function to accept and use priority:

const createItem = useCallback(  ({summary, priority}: {summary: string, priority: string}) => {    realm.write(() => {      return new Item(realm, {        summary,        owner_id: user?.id,        priority      });    });  },  [realm, user],);

Then, modify the create to-do submission handler to accept the priority level and pass it to createItem():

<CreateToDoPrompt  onSubmit={({summary, priority}) => {    setShowNewItemOverlay(false);    createItem({summary, priority});  }}/>

Finally, modify the list item template to render the to-do's priority before the summary:

<ListItem  key={`${item._id}`}  bottomDivider  topDivider  hasTVPreferredFocus={undefined}  tvParallaxProperties={undefined}>  <Text>{item.priority}</Text>  <ListItem.Title style={styles.itemTitle}> {item.summary} </ListItem.Title>  <ListItem.Subtitle style={styles.itemSubtitle}> {item.owner_id === user?.id ? '(mine)' : ''} </ListItem.Subtitle>  <ListItem.CheckBox checked={item.isComplete} checkedColor={COLORS.primary} iconType="material" checkedIcon="check-box" uncheckedIcon="check-box-outline-blank" onPress={() => toggleItemIsComplete(item._id)} />  <Button type="clear" onPress={() => deleteItem(item._id)} icon={ <Icon type="material" name="clear" size={12} color="#979797" tvParallaxProperties={undefined} /> } /></ListItem>

Your app should now allow users to set a priority level for new to-do items.

Rebuild the app and open it. Add some new to-do items to confirm that you can choose a priority level and that the list displays each to-do's priority.

The Device Sync protocol uses a flexible model where each sync client uses a standard RQL query to choose a subset of application data and then subscribes to the subset. This automatically pulls the latest version of all data in the subset to the device and syncs changes to the data between devices.

For example, the template app you're using has the following built-in subscription to items that the current user owns:

realm.subscriptions.update(mutableSubs => {  mutableSubs.removeByName(itemSubscriptionName);  mutableSubs.add(    realm.objects(Item).filtered(`owner_id == "${user?.id}"`),    {name: ownItemsSubscriptionName},  );});

You can customize the subscription during runtime to sync only the data that your app needs. Let's add a feature to demonstrate how.

For this tutorial, we'll add a button that lets us toggle between two modes: one where the app syncs all to-do items and another where it only syncs important ones with a priority of High or Severe.

First, add a useState() hook to the ItemListView component to keep track of the current mode:

const [showImportantOnly, setShowImportantOnly] = useState(false);

Then, add a new button that toggles the mode to the bottom of the to-do list, after <ListItem>:

<Button  title={showImportantOnly ? 'Show All' : 'Show Important Only'}  buttonStyle={{    ...styles.addToDoButton,    backgroundColor: showImportantOnly ? '#00A35C' : '#FFC010',  }}  onPress={() => setShowImportantOnly(showImportantOnly => !showImportantOnly)}/>

At this point, the app can switch modes in the UI, but we haven't done anything else so the modes are functionally identical. Let's update the sync subscription to only sync data relevant to the current mode.

In the first useEffect of the ItemListView component, add code that checks the current mode and appends an additional priority filter to the query if the showImportantOnly mode is active:

useEffect(() => {  if (showAllItems) {    realm.subscriptions.update(mutableSubs => {      mutableSubs.removeByName(ownItemsSubscriptionName);      mutableSubs.add(realm.objects(Item), {name: itemSubscriptionName});    });  } else if (showImportantOnly) {    realm.subscriptions.update(mutableSubs => {      mutableSubs.removeByName(itemSubscriptionName);      mutableSubs.add(        realm.objects(Item).filtered(`owner_id == "${user?.id}" && priority <= 1`),        {name: ownItemsSubscriptionName},      );    });  } else {    realm.subscriptions.update(mutableSubs => {      mutableSubs.removeByName(itemSubscriptionName);      mutableSubs.add(        realm.objects(Item).filtered(`owner_id == "${user?.id}"`),        {name: ownItemsSubscriptionName},      );    });  }}, [realm, user, showAllItems, showImportantOnly]);
Important

Don't forget to add showImportantOnly to the list of dependencies in the second argument of useEffect.

Your app is now set up to modify its sync subscription based on the current mode.

Rebuild and run the app to make sure everything works. You should be able to create, complete, and delete to-do items as well as toggle between viewing all items and only important items.

Tip Changing Subscriptions with Developer Mode Enabled

In this tutorial, when you change the subscription and query on the priority field for the first time, the field is automatically added to the Device Sync Collection Queryable Fields. This occurs because the template app has Development Mode enabled by default. If Development Mode was not enabled, you would have to manually add the field as a queryable field to use it in a client-side Sync query.

For more information, refer to Queryable Fields.

Note Share Feedback

How did it go? Use the Rate this page widget at the bottom right of the page to rate its effectiveness. Or file an issue on the GitHub repository if you had any issues.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4