Website to PWA and mobile

In the ever-evolving landscape of mobile app development, efficiency and performance are paramount. One powerful tool that has gained significant traction in the React Native community is Realm, a modern database solution designed for mobile applications. By leveraging bundled Realm, developers can enhance their app’s performance by including static data directly within the app bundle. This article delves into the process of bundling Realm in a React Native app, providing a step-by-step guide to streamline your development workflow and optimize your application&s performance.

Introduction to Realm and Bundled Realm

Realm is a mobile database solution designed to handle complex data structures with ease and deliver high performance. Unlike traditional SQLite databases, Realm is an object-oriented database, making it a natural fit for mobile applications where data needs to be accessed and manipulated efficiently.

Bundled Realm takes this a step further by allowing developers to pre-package their database with static data directly into the app bundle. This approach not only speeds up the initial loading time of the app but also ensures that essential data is readily available without requiring network requests or additional setup after installation.

For detailed documentation on Realm and bundling Realm files, visit the MongoDB Realm Documentation.

Why Use Bundled Realm in React Native?

Integrating a bundled Realm database into your React Native app offers several advantages:

  • Performance Optimization: Pre-loading static data reduces the need for runtime data fetching, resulting in faster app launch times and smoother user experiences.
  • Offline Availability: Bundled data ensures that essential information is accessible even without an internet connection.
  • Simplified Data Management: Including static data within the app bundle simplifies the data management process, eliminating dependencies on external data sources during the initial setup.
  • Consistency: Guarantees that all users have access to the same baseline data, ensuring consistency across different devices and environments.
  • Educational Apps: Educational applications often contain a vast amount of static content, such as lessons, quizzes, and reference materials. By bundling Realm, developers can efficiently include this static data within the app, ensuring that users have immediate access to educational content without the need for additional downloads or network requests. This not only enhances the user experience by providing seamless access to resources but also simplifies the development process by reducing dependencies on external data sources. Additionally, bundled Realm allows for easy updates to educational materials, enabling educators and developers to push new content efficiently without disrupting the existing user base.

By adopting bundled Realm, developers can focus more on building dynamic features rather than managing data synchronization and retrieval processes.

Prerequisites

Before diving into the bundling process, ensure you have the following setup:

  • React Native Environment: A working React Native project setup with the necessary development tools.
  • Realm Installed: Ensure that Realm is installed and properly configured in your React Native project. You can install Realm using npm or yarn:
    yarn add realm@12
    yarn add @realm/react@0.6.2
    yarn add @realm/babel-plugin@0.2.0
    
  • Node.js: Installed on your development machine to run scripts.
  • Basic Knowledge of JavaScript: Familiarity with JavaScript for writing the bundling script.

Step-by-Step Guide to Bundling Realm

1. Setting Up the Scripts Directory

To organize your project effectively, create a scripts directory at the root of your React Native project. This directory will house all the scripts related to bundling Realm.

mkdir scripts

2. Adding JSON Data

Within the scripts directory, create a subdirectory named data to store your static JSON files. These JSON files will contain the data you want to bundle into the Realm database.

mkdir scripts/data

Add your JSON files to the scripts/data directory. For example, you might have a users.json file:

// scripts/data/tickets.json
[
  {
    "id": 1,
    "text": "Ticket 1",
    "description": "Description 1"
  },
  {
    "id": 2,
    "text": "Ticket 2",
    "description": "Description 2"
  }
]

3. Creating the Realm Bundle Script

Next, create a TypeScript file named generateRealmBundle.ts within the scripts directory. This script will read the JSON data, populate the Realm database, and generate the bundled Realm file.

// scripts/generateRealmBundle.ts

import Realm, {ObjectClass, ObjectSchema} from 'realm';

// Define your Realm schema or import it from a separate ts file
class TicketModel extends Realm.Object<TicketModel> {
  _id: Realm.BSON.ObjectId = new Realm.BSON.ObjectId();
  text!: string;
  description?: string;
  createdAt: Date = new Date();

  static primaryKey = '_id';

  static schema = {
    name: 'TicketModel',
    primaryKey: '_id',
    properties: {
      _id: {type: 'objectId', default: () => new Realm.BSON.ObjectId()},
      text: {type: 'string', indexed: 'full-text'},
      description: 'string?',
      createdAt: {type: 'date', default: () => new Date()},
    },
  };
}

// Define your Realm config or import it from a separate ts file
const schema: (ObjectClass | ObjectSchema)[] = [
  TicketModel,
];

const realmConfig: Realm.Configuration = {
  path: 'bundle.realm',
  schemaVersion: 1,
  schema
};
  
// run the bundle generation process
const run = async () => {
// clear existing data   
  await clearData();
// write new data  
  await writeData();
// copy the realm file to android assets  
  await copyAndroidFile();
  console.log('Realm bundling process completed successfully.');
  process.exit();
};

const writeData = async () => {
  try {
      console.log('Write data');
      const fs = await import('fs');
      const ticketsData = JSON.parse(
        fs.readFileSync('data/tickets.json', 'utf-8'),
      );
      
      // add data to realm
      const realm = await Realm.open(realmConfig);
      realm.write(() => {
        for (let i = 0; i < ticketsData.length; i++) {
          const ticketData = ticketsData[i];
          realm.create('TicketModel', {
            text: ticketData.text,
            description: ticketData.description,
          } as TicketModel);
        }
      });
      
      console.log('Realm empty:', realm.isEmpty);
      console.log('Closing realm...');
      realm.close();
      console.log('Realm closed:', realm.isClosed);
      console.log('Bundle created.');
  } catch (error) {
    console.error('Error writing data to Realm:', error);
  }
};

const copyAndroidFile = async () => {
  try {  
      console.log('Copy android file ⤴️');
      const fs = await import('fs');
      if (!fs.existsSync('bundle.realm')) {
        console.log('File not exists');
        return;
      }
    
      fs.copyFileSync(
        'bundle.realm',
        '../android/app/src/main/assets/bundle.realm',
      );
    
      console.log('Realm file copied.');
  
  } catch (error) {
    console.error('Error copying Realm file to Android assets:', error);
  }
};

const clearData = async () => {
  try {
      console.log('Clear data');
      Realm.deleteFile(realmConfig);
      console.log('Clear data completed.');
  } catch (error) {
    console.error('Error clearing Realm data:', error);
  }
};

(() => {
  run();
})();

Explanation of the Script:

  • Import Dependencies: The script imports the Realm, and fs (file system)
  • Define Realm Schema: Defines the schema for the Ticket model, specifying id as the primary key.
  • Read JSON Data: Reads the users.json file from the data directory and parses it into a JavaScript object.
  • Initialize Realm: Opens a new Realm instance with the specified schema. The deleteRealmIfMigrationNeeded flag ensures that Realm will delete and recreate the database if a migration is required (use with caution in production).
  • Write Data to Realm: Iterates over the parsed JSON data and writes each ticket object to the Realm database.
  • Export Realm File: The script saves the Realm database as bundled.realm within the scripts directory.
  • Close Realm: Closes the Realm instance to ensure that all changes are saved and resources are freed.

4. Generating the Realm Bundle File

Run the script to generate the Realm bundle file. Navigate to the scripts directory and execute the script using Node.js:

npx ts-node scripts/generateRealmBundle.ts

Upon successful execution, you should see a confirmation message:

Realm bundling process completed successfully.

5. Integrating the Realm Bundle into iOS and Android

With the bundled.realm file generated, the next step is to integrate it into both the iOS and Android parts of your React Native project.

Modify Realm Configuration:

import Realm, {ObjectClass, ObjectSchema} from 'realm';

// copy the bundled realm file to the app's default directory
Realm.copyBundledRealmFiles(); 

// Define your Realm schema or import it from a separate ts file
class TicketModel extends Realm.Object<TicketModel> {
  _id: Realm.BSON.ObjectId = new Realm.BSON.ObjectId();
  text!: string;
  description?: string;
  createdAt: Date = new Date();

  static primaryKey = '_id';

  static schema = {
    name: 'TicketModel',
    primaryKey: '_id',
    properties: {
      _id: {type: 'objectId', default: () => new Realm.BSON.ObjectId()},
      text: {type: 'string', indexed: 'full-text'},
      description: 'string?',
      createdAt: {type: 'date', default: () => new Date()},
    },
  };
}

const schema: (ObjectClass | ObjectSchema)[] = [
  TicketModel,
];

const realmConfig: Realm.Configuration = {
  path: 'bundle.realm',
  schemaVersion: 1,
  schema,
}

const RealmContext = createRealmContext(realmConfig);

export {
  TicketModel,
  RealmContext,
  realmConfig,
  schema,
};

For iOS:

Add Realm File to Xcode:

  • Launch Xcode and open your project&ams;s .xcworkspace file.
  • Locate the bundled.realm file in the scripts directory of your project. Drag and drop the bundled.realm file into the Project Navigator pane in Xcode.
  • In the Project Navigator, select your app target. Click on the Build Phases tab in the project overview.
  • Within the Build Phases tab, find and expand the Copy Bundle Resources section. Click the + icon located at the bottom of the Copy Bundle Resources section. Click the Add Other... button.
  • Find your bundled.realm file and add it. Don&t change the default settings, then select the Finish button.

Note: For subsequent Realm bundles, these steps are not required. Once the bundled.realm file is integrated into your project, you can streamline the bundling process without repeating these steps.

For Android:

The generateRealmBundle.ts script takes care of copying the bundled.realm file to the appropriate location within your Android project. This automation ensures a seamless integration without requiring any manual intervention.

Destination Path: android/app/src/main/assets/bundle.realm

Potential Pitfalls and Considerations

While bundling Realm offers significant benefits, it&s essential to be aware of potential challenges:

  • Data Size: Bundling large datasets can increase the app&s overall size, potentially affecting download times and storage requirements on user devices. It&s crucial to optimize your JSON data and Realm schemas to include only necessary information.
  • Updates and Migrations: Managing updates to bundled data requires careful handling to ensure that existing users receive the latest data without data loss or corruption. Implementing migration strategies is vital for maintaining data integrity.
  • Platform-Specific Implementations: As demonstrated in the integration steps, handling Realm bundles requires platform-specific configurations, which can introduce complexity during development and maintenance.

Conclusion

Bundling Realm in your React Native app is a strategic approach to enhancing performance, ensuring offline accessibility, and simplifying data management. By pre-packaging static data into the app bundle, developers can deliver a seamless and efficient user experience from the moment users launch the app.

At Appelian Software, we specialize in leveraging Realm&s capabilities to streamline your mobile app development process. Our meticulous bundling process ensures that your app is equipped with the essential data it needs to perform optimally, all within a swift 2-3 month development timeline. Whether you&re a startup aiming to establish a robust digital presence or an established enterprise looking to optimize your mobile offerings, our expertise in bundled Realm solutions can propel your app to success.

Ready to elevate your React Native app with bundled Realm? Contact us today for a free consultation and discover how Appelian Software can help you transform your web solutions into dynamic mobile experiences.

Who am I?

My name is Aleksei, I am a Co-Founder at Appelian Software.

15+ years in the software development. Experienced Delivery Manager.

Appelian Software plans, designs, and develops MVPs ready to enter the market and be prepared for funding. On top of that, we help to migrate software to a new tech stack or improve the performance of the product.

React NativeRealm DatabaseBundled RealmMobileReact Native RealmStatic Data BundlingOffline Data AccessRealm Bundling ScriptJSON to RealmMobile App Performance