Let’s Give Your Realm-Powered Ionic Web App the Native Treatment on iOS and Android!
Tags: ionic, realm, javascript, react
Published: 2021-12-22 16:40:34

---
title: Let’s Give Your Realm-Powered Ionic Web App the Native Treatment on iOS and Android!
published: true
description: We can convert a existing Ionic React Web App that saves data in MongoDB Realm using Apollo GraphQL into an iOS and Android app using a couple commands, and the three apps will share the same MongoDB Realm backend. Also, we can easily add functionality to all three apps, just modifying one code base.
canonical_url: https://www.mongodb.com/developer/how-to/ionic-realm-web-app-convert-to-mobile-app/
tags: ionic, realm, javascript, react
cover_image: https://mongodb-devhub-cms.s3.us-west-1.amazonaws.com/realm_ionic_a68303dbc1.png
---

[Realm](https://realm.io/) is an open-source, easy-to-use local database that helps mobile developers to build better apps, faster. It offers a data synchronization service—MongoDB Realm Sync—that makes it simple to move data between the client and MongoDB Atlas on the back end. Using Realm can save you from writing thousands of lines of code, and offers an intuitive way to work with your data.

The Ionic team posted a fantastic article on how you can [use Ionic with Realm to build a React Web app](https://ionicframework.com/blog/ionic-react-and-realm/) quickly, taking advantage of Realm to easily persist your data in a MongoDB Atlas Database.

After [cloning the repo](https://github.com/mhartington/ionic-realm-demo) and running `ionic serve`, you'll have a really simple task management web application. You can register (using any user/password combination, Realm takes care of your onboarding needs). You can log in, have a look at your tasks, and add new tasks.

|   Login in the Web App      |   Browsing Tasks   | 
|--------------|-----------|
| ![Login window asking for user and password running in a browser. There’s a Login and Register button.](https://mongodb-devhub-cms.s3.us-west-1.amazonaws.com/ionic_native_login_web_44095db30a.png)|  ![Tasks already entered in the app, one for “Do the dishes”, another for “Finish post about Ionic / Realm” and last one “Install Ionic app running Realm in iOS”](https://mongodb-devhub-cms.s3.us-west-1.amazonaws.com/ionic_native_tasks_web_98bd9784b2.png)      | 


Let’s build on what the Ionic team created for the web, and expand it by building a mobile app for iOS and Android using one of the best features Ionic has: the _“Write Once, Run Anywhere”_ approach to coding. I’ll start with an iOS app.


## Prerequisites

To follow along this post, you’ll need five things:

* A macOS-powered computer running Xcode (to develop for iOS). I’m using Xcode 13 Beta. You don’t have to risk your sanity.
* Ionic installed. You can follow the instructions [here](https://ionicframework.com/docs/intro/cli), but TL;DR it’s `npm install -g @ionic/cli`
* Clone the [repo with the Ionic React Web App](https://docs.mongodb.com/realm/tutorial/realm-app/) that we’ll turn into mobile.
* As we need an Atlas Database to store our data in the cloud, and a Realm app to make it easy to work with Atlas from mobile, set up a Free Forever MongoDB cluster and [create and import a Realm app schema](https://docs.mongodb.com/realm/tutorial/realm-app/) so everything is ready server-side.
* Once you have your Realm app created, copy the Realm app ID from the MongoDB admin interface for Realm, and paste it into `src/App.tsx`, in the line:

`export const APP_ID = '<Put your Realm App Id here>';`

Once your `APP_ID` is set, run:

```
$ npm run build
```


## The iOS app

To add iOS capabilities to our existing app, we need to open a terminal and run:

```bash
$ ionic cap add ios
``` 

This will create the iOS Xcode Project native developers know and love, with the code from our Ionic app. I ran into a problem doing that and it was that the version of Capacitor used in the repo was 3.1.2, but for iOS, I needed at least 3.2.0. So, I just changed `package.json` and ran `npm install` to update Capacitor.

`package.json` fragment:

```
...
"dependencies": {

    "@apollo/client": "^3.4.5",
    "@capacitor/android": "3.2.2",
    "@capacitor/app": "1.0.2",
    "@capacitor/core": "3.2.0",
    "@capacitor/haptics": "1.0.2",
    "@capacitor/ios": "3.2.2",
...
```

Now we have a new `ios` directory. If we enter that folder, we’ll see an `App` directory that has a CocoaPods-powered iOS app. To run this iOS app, we need to:



* Change to that directory with `cd ios`. You’ll find an `App` directory. `cd App`
* Install all CocoaPods with `pod repo update && pod install`, as usual in a native iOS project. This updates all libraries’ caches for CocoaPods and then installs the required libraries and dependencies in your project.
* Open the generated `App.xcworkspace` file with Xcode. From Terminal, you can just type `open App.xcworkspace`.
* Run the app from Xcode.

|   Login in the iOS App      |   Browsing Tasks   | 
|--------------|-----------|
|![Login screen in iOS asking for user and password running in a browser. There’s a Login and Register button.](https://mongodb-devhub-cms.s3.us-west-1.amazonaws.com/ionic_native_login_ios_21fa1f7446.png)|  ![Tasks already entered in the iOS app, one for “Do the dishes”, another for “Finish post about Ionic / Realm” and last one “Install Ionic app running Realm in iOS”](https://mongodb-devhub-cms.s3.us-west-1.amazonaws.com/ionic_native_tasks_ios_04d29ff898.png)  |



That’s it. Apart from updating Capacitor, we only needed to run one command to get our Ionic web project running on iOS!


## The Android App

How hard can it be to build our Ionic app for Android now that we have done it for iOS? Well, it turns out to be super-simple. Just `cd` back to the root of the project and type in a terminal:

```
 ionic cap android
```

This will create the Android project. Once has finished, launch your app using:

```
ionic capacitor run android -l --host=10.0.1.81
```

In this case, `10.0.1.81` is my own IP address. As you can see, if you have more than one Emulator or even a plugged-in Android phone, you can select where you want to run the Ionic app.

![before running the Android App we can choose the Emulator where it will run.](https://mongodb-devhub-cms.s3.us-west-1.amazonaws.com/ionic_native_terminal_f3e08bace5.png)

Once running, you can register, log in, and add tasks in Android, just like you can do in the web and iOS apps.


|  Adding a task in Android      |   Browsing Tasks in Android   | 
|--------------|-----------|
|![Adding a new task screen from Android, with “New Task from Android” typed in.](https://mongodb-devhub-cms.s3.us-west-1.amazonaws.com/ionic_native_android_add_7040b32898.png)|![Tasks already entered in the Android app, one for “Do the dishes”, another for “Finish post about Ionic / Realm”, “Install Ionic app running Realm in iOS” and “New Task from Android”](https://mongodb-devhub-cms.s3.us-west-1.amazonaws.com/ionic_native_android_tasks_7ee2c9c1bc.png)|

The best part is that thanks to the synchronization happening in the MongoDB Realm app, every time we add a new task locally, it gets uploaded to the cloud to a MongoDB Atlas database behind the scenes. And **all other apps accessing the same MongoDB Realm app can show that data**! 


![thanks to Ionic and Realm, Web, iOS and Android access the same data using the same code base.](https://mongodb-devhub-cms.s3.us-west-1.amazonaws.com/ionic_native_three_apps_1a7dbfbd05.png)


## Automatically refreshing tasks

[Realm SDKs](https://docs.mongodb.com/realm/sdk/) are well known for their syncing capabilities. You change something in the server, or in one app, and other users with access to the same data will see the changes almost immediately. You don’t have to worry about invalidating caches, writing complex networking/multithreading code that runs in the background, listening to silent push notifications, etc. MongoDB Realm takes care of all that for you.

But in this example, we access data using the [Apollo GraphQL Client](https://docs.mongodb.com/realm/web/graphql-apollo-react/) for React. Using this client, we can log into our Realm app and run GraphQL Queries—although as designed for the web, we don’t have access to the hard drive to store a .realm file. It’s just a simpler way to use the otherwise awesome [Apollo GraphQL Client](https://www.apollographql.com/docs/react/) with Realm, so we don’t have synchronization implemented. But luckily, Apollo GraphQL queries can automatically refresh themselves just passing a `pollInterval` argument. I told you it was awesome. You set the time interval in milliseconds to refresh the data.

So, in `useTasks.ts`, our function to get all tasks will look like this, auto-refreshing our data every half second.

```typescript
function useAllTasksInProject(project: any) {
 const { data, loading, error } = useQuery(
   gql`
     query GetAllTasksForProject($partition: String!) {
       tasks(query: { _partition: $partition }) {
         _id
         name
         status
       }
     }
   `,
   { variables: { partition: project.partition }, pollInterval: 500 }
 );
 if (error) {
   throw new Error(`Failed to fetch tasks: ${error.message}`);
 }

 // If the query has finished, return the tasks from the result data
 // Otherwise, return an empty list
 const tasks = data?.tasks ?? [];
 return { tasks, loading };
}
```

![Now we can sync our actions. Adding a task in the Android Emulator gets propagated to the iOS and Web versions](https://mongodb-devhub-cms.s3.us-west-1.amazonaws.com/ionic_native_sync_add_task_cd8ec70ccc.gif)

## Pull to refresh

Adding automatic refresh is nice, but in mobile apps, we’re used to also refreshing lists of data just by pulling them. To get this, we’ll need to add the Ionic component `IonRefresher` to our Home component:

```html
<IonPage>
     <IonHeader>
       <IonToolbar>
         <IonTitle>Tasks</IonTitle>
         <IonButtons slot="end">
           <IonButton onClick={addNewTask}>
             <IonIcon slot="icon-only" icon={add}></IonIcon>
           </IonButton>
         </IonButtons>
       </IonToolbar>
     </IonHeader>
     <IonContent fullscreen>
       <IonRefresher slot="fixed" onIonRefresh={doRefresh}>
         <IonRefresherContent></IonRefresherContent>
       </IonRefresher>
       <IonHeader collapse="condense">
         <IonToolbar>
           <IonTitle size="large">Tasks</IonTitle>
         </IonToolbar>
       </IonHeader>
       <IonList>
         {loading ? <IonLoading isOpen={loading} /> : null}
         {tasks.map((task: any) => (
           <TaskItem key={parseInt(task._id)} {...task}></TaskItem>
         ))}
       </IonList>
     </IonContent>
   </IonPage>
```

As we can see, an `IonRefresher` component will add the pull-to-refresh functionality with an included loading indicator tailored for each platform.


```html
<IonRefresher slot="fixed" onIonRefresh={doRefresh}>
   <IonRefresherContent></IonRefresherContent>
</IonRefresher>
```

To refresh, we call `doRefresh` and there, we just reload the whole page.

```typescript
 const doRefresh = (event: CustomEvent<RefresherEventDetail>) => {
   window.location.reload(); // reload the whole page
   event.detail.complete();  // we signal the loading indicator to hide
 };
```


## Deleting tasks

Right now, we can swipe tasks from right to left to change the status of our tasks. But I wanted to also add a left to right swipe so we can delete tasks. We just need to add the swiping control to the already existing `IonItemSliding` control. In this case, we want a swipe from the _start_ of the control. This way, we avoid any ambiguities with right-to-left vs. left-to-right languages. When the user taps on the new “Delete” button (which will appear red as we’re using the _danger_ color), `deleteTaskSelected` is called.

```html
<IonItemSliding ref={slidingRef} className={'status-' + task.status}>
     <IonItem>
       <IonLabel>{task.name}</IonLabel>
     </IonItem>
     <IonItemOptions side="end">
       <IonItemOption onClick={toggleStatus}>Status</IonItemOption>
     </IonItemOptions>
     <IonItemOptions side="start">
       <IonItemOption onClick={deleteTaskSelected} color="danger">Delete</IonItemOption>
     </IonItemOptions>
   </IonItemSliding>
```

To delete the task, we use a GraphQL mutation defined in `useTaskMutations.ts`:

```typescript
const deleteTaskSelected = () => {
   slidingRef.current?.close();  // close sliding menu
   deleteTask(task);             // delete task
 };
```



![Deleting can be done with a Swipe. Here we show how it's been done in the Android Emulator and changes appear in the iOS Simulator and Web page](https://mongodb-devhub-cms.s3.us-west-1.amazonaws.com/ionic_native_delete_184d02206d.gif)

## Recap

In this post, we’ve seen how easy it is to start with an Ionic React web application and, with only a few lines of code, turn it into a mobile app running on iOS and Android. Then, we easily added some functionality to the three apps at the same time. Ionic makes it super simple to run your Realm-powered apps everywhere!

You can check out the code from this post [in this branch](https://github.com/mongodb-developer/ionic-realm-demo/tree/observe-changes) of the [repo](https://github.com/mongodb-developer/ionic-realm-demo), just by typing:

```
$ git clone https://github.com/mongodb-developer/ionic-realm-demo
$ git checkout observe-changes
```

But this is not the only way to integrate Realm in your Ionic apps. Using Capacitor and our native SDKs, we’ll show you how to use Realm from Ionic in a future follow-up post.