Skip to content

gupta-shrinath/react-native-simple-ota

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

20 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

react-native-simple-ota

NPM 0.2.1 Github

A lightweight OTA (Over-The-Air) update solution for React Native apps. It allows you to update your JavaScript code without going through App Store or Play Store releases.


πŸš€ Concepts

  • OTA (Over-The-Air) Updates: Updates pushed remotely to the app without requiring a new version from the App Store / Play Store.
  • JS Bundle: The compiled version of your React Native JavaScript code. It can generated using the following commands.
    • Android: react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res
    • iOS: react-native bundle --platform ios --dev false --entry-file index.js --bundle-output ios/main.jsbundle
  • ⚠️ Note:
    • react-native-simple-ota only updates JavaScript code, not native modules.
    • Works only in release builds; in debug mode, Metro bundler is used instead.

🧩 Design Overview

  • react-native-simple-ota stores and loads custom JS bundles (OTA updates) from the local device storage.
  • OTA updates are applied on the next app launch.
  • You control when and how to download OTA updates by implementing a custom OtaUpdateProvider. This ensures full flexibility for caching, version control, and download logic.
  • On app launch, the library simply redirects the JS bundle loader to your downloaded OTA file (if available).
  • react-native-simple-ota works with React Native Old & New Architecture and Expo Bare Work Flow.

Recommendations

  • Host your JS Bundles on to CDNs for faster delivery.
  • Host a compressed version of the JavaScript bundle, as these files can be quite large.
  • Validate hash of JS Bundle after download to avoid corruption.
  • Take a look at the example project for reference.

πŸ“¦ Installation

npm install react-native-simple-ota

Integration

Step 1: Define implementation of OTAUpdateProvider

import {
  getBundleVersion,
  type OtaUpdate,
  type OtaUpdateProvider,
} from 'react-native-simple-ota';


class MyOtaUpdateProvider implements OtaUpdateProvider {
  isUserApplicableForUpdate(): boolean {
    return true;
  }

  async getOtaUpdate(): Promise<OtaUpdate | null> {
    try {
      const bundleInfo: BundleInfo = await this.getUpdate();
      const update: OtaUpdate = {
        bundleVersion: bundleInfo.bundle_version,
        bundlePath: bundlePath,
      };
      return update;
    } catch (e: any) {
    }
    return null;
  }
}

Step 2: Initialize (recommended in index.js or App.tsx)

import { init } from 'react-native-simple-ota';

init(new MyOtaUpdateProvider());

Step 3: Perform OTA update check & Save the OTA JS bundle path

import {
  applyOTAIfApplicable,
} from 'react-native-simple-ota';

// ...

useEffect(() => {
   applyOTAIfApplicable();
}, []);

Step 4: Native Side Integration

Note: Though the below file are in Kotlin and Swift they can be used in Java and Objective C respectively

  • Android The Application class might not exist in your Android code so you'll have to create it at myapp/android/app/src/main/java and add it in manifest file.
import android.app.Application
import dev.droid.simpleota.ReactNativeSimpleOtaModule

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 getJSBundleFile(): String? {
          return ReactNativeSimpleOtaModule.getJSBundleFile(this@MainApplication) ?: super.getJSBundleFile()
        }
        override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG

        override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
        override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
      }
}      
  • iOS
import UIKit
import React
import React_RCTAppDelegate
import ReactAppDependencyProvider
import ReactNativeSimpleOta

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?

  var reactNativeDelegate: ReactNativeDelegate?
  var reactNativeFactory: RCTReactNativeFactory?

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    let delegate = ReactNativeDelegate()
    let factory = RCTReactNativeFactory(delegate: delegate)
    delegate.dependencyProvider = RCTAppDependencyProvider()

    reactNativeDelegate = delegate
    reactNativeFactory = factory

    window = UIWindow(frame: UIScreen.main.bounds)

    factory.startReactNative(
      withModuleName: "SimpleOtaExample",
      in: window,
      launchOptions: launchOptions
    )

    return true
  }
}

class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
  override func sourceURL(for bridge: RCTBridge) -> URL? {
    self.bundleURL()
  }

  override func bundleURL() -> URL? {
  #if DEBUG
      return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
  #else
    if let customBundlePath = ReactNativeSimpleOta.getJSBundleFile() {
          return URL(fileURLWithPath: customBundlePath)
    }
    return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
  #endif
  }
}

About

A simple ota solution for React Native apps

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published