Skip to content

riteshshukla04/react-native-nitro-ota

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

44 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

react-native-nitro-ota

Still in Alpha and will have issues

⚑️ High-performance Over-The-Air (OTA) updates for React Native - Powered by Nitro Modules

Download, unzip, and apply JavaScript bundle updates at runtime without going through the App Store or Play Store review process.

✨ Features

  • πŸš€ Native Performance - Built with Nitro Modules for maximum speed
  • 🧡 Off JS Thread - All operations run on different threads, keeping your JS thread free
  • 🌐 Server Agnostic - Works with any CDN, S3, GitHub Releases, or custom server
  • πŸ“¦ Automatic Bundle Management - Handles download, extraction, and cleanup
  • πŸ”’ Version Control - Built-in version checking and management

πŸ“¦ Installation

npm install react-native-nitro-ota react-native-nitro-modules
# or
yarn add react-native-nitro-ota react-native-nitro-modules

Note: react-native-nitro-modules is required as this library relies on Nitro Modules.

πŸ“± Platform-Specific Setup (Required!)

Android: Native Bundle Loading

In your MainApplication.kt, add the bundle path loader:

import com.facebook.react.ReactApplication
import com.facebook.react.ReactNativeHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.margelo.nitro.nitroota.core.getStoredBundlePath

class MainApplication : Application(), ReactApplication {

  override val reactNativeHost: ReactNativeHost =
      object : DefaultReactNativeHost(this) {
        
        override fun getPackages(): List<ReactPackage> =
            PackageList(this).packages
        
        override fun getJSMainModuleName(): String = "index"
        
        override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
        
        // πŸ”₯ Load OTA bundle if available, otherwise use default
        override fun getJSBundleFile(): String? {
          return getStoredBundlePath(this@MainApplication)
        }
      }
}

If using modern React host:

import com.facebook.react.ReactHost
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.margelo.nitro.nitroota.core.getStoredBundlePath

class MainApplication : Application(), ReactApplication {

  override val reactHost: ReactHost by lazy {
    getDefaultReactHost(
      context = applicationContext,
      packageList = PackageList(this).packages,
      jsBundleFilePath = getStoredBundlePath(applicationContext)
    )
  }
}

iOS: NitroOtaBundleManager

  1. Add to your Podfile:
pod 'NitroOtaBundleManager', :path => '../node_modules/react-native-nitro-ota'
  1. Install pods:
cd ios && pod install
  1. Update AppDelegate.swift:
import UIKit
import React
import NitroOtaBundleManager

class AppDelegate: UIResponder, UIApplicationDelegate {
    
    override func bundleURL() -> URL? {
        #if DEBUG
        return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
        #else
        // Check for OTA bundle
        if let bundlePath = NitroOtaBundleManager.shared.getStoredBundlePath() {
            let bundleURL = URL(fileURLWithPath: bundlePath)
        
            return bundleURL
        }
        
        // Fallback to default bundle
        return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
        #endif
    }
}

πŸš€ Quick Start

Option 1: GitHub OTA (Easiest! πŸ”₯)

Use the githubOTA helper to point directly to a GitHub repository:

import { githubOTA, OTAUpdateManager } from 'react-native-nitro-ota';

// Configure GitHub URLs
const { downloadUrl, versionUrl } = githubOTA({
  githubUrl: 'https://github.com/your-username/your-ota-repo',
  otaVersionPath: 'ota.version',  // optional, defaults to 'ota.version'
  ref: 'main'  // optional, defaults to 'main'
});

// Create update manager
const otaManager = new OTAUpdateManager(downloadUrl, versionUrl);

// Check for updates
const hasUpdate = await otaManager.checkForUpdates();
if (hasUpdate) {
  // Download update
  await otaManager.downloadUpdate();
  otaManager.reloadApp()
  console.log('Update downloaded! Restart app to apply.');
}

// Get current version
const currentVersion = otaManager.getVersion();
console.log('Current OTA version:', currentVersion);

Option 2: Custom Server/CDN

1. Download and Apply OTA Update

import { NitroOta } from 'react-native-nitro-ota';

// Download and unzip bundle from any server
const updatePath = await NitroOta.downloadAndUnzipFromUrl(
  'https://your-cdn.com/bundles/latest.zip',
  'https://your-cdn.com/bundles/version.txt' // optional version file
);

console.log('Update downloaded to:', updatePath);

// Restart app to apply update
// Use your preferred restart method or RN's DevSettings

2. Check for Updates

// Check if a new version is available
const hasUpdate = await NitroOta.checkForUpdates(
  'https://your-cdn.com/bundles/version.txt'
);

if (hasUpdate) {
  console.log('New version available!');
  // Download and apply update
}

3. Get Current Version

const currentVersion = NitroOta.getStoredOtaVersion();
console.log('Current OTA version:', currentVersion);

3. Get Current Version

 NitroOta.reloadApp();

πŸ“ Understanding the ota.version File

The ota.version file is a simple text file that contains your current bundle version. The version can be anything - numbers, strings, or even creative identifiers like "apple", "orange", "winter2024", or "bugfix-v3".

Important: There's no "greater than" or "less than" logic. The library simply checks if currentVersion !== newVersion. This means you can use any naming scheme that makes sense for your workflow - just ensure each new update has a different version string than the previous one.

Creating the version file:

echo "1.0.0" > ota.version
# or
echo "apple" > ota.version
# or
echo "$(date +%Y%m%d)" > ota.version  # Use current date as version

πŸ“¦ Creating and Uploading Bundles

Follow these steps to generate and distribute your OTA bundle:

1. Generate the JavaScript Bundle

Run the following commands to create a production-ready bundle and assets for your platform:

For Android

npx react-native bundle \
  --platform android \
  --dev false \
  --entry-file index.js \
  --bundle-output android/App-Bundles/index.android.bundle \
  --assets-dest android/App-Bundles

For iOS

npx react-native bundle \
  --platform ios \
  --dev false \
  --entry-file index.js \
  --bundle-output ios/App-Bundles/index.jsbundle \
  --assets-dest ios/App-Bundles

Result:
Your bundles and assets will be generated in android/App-Bundles/ or ios/App-Bundles/ respectively.


2. Package the Bundle

After generating the bundle, compress the entire output folder (including the assets) into a single zip file:

# For Android
cd android && zip -r App-Bundles.zip App-Bundles

# For iOS
cd ios && zip -r App-Bundles.zip App-Bundles

3. Distribute the Bundle

Upload the zipped bundle file to your backend, CDN, or preferred file hosting service so that your app can download it for OTA updates.


πŸ”‘ Real-World Example

In the Jellify App:

This keeps OTA releases well organized and accessible for deployment.

🀝 Contributing

See CONTRIBUTING.md for development workflow and guidelines.

πŸ“„ License

MIT