RNForge
In-App Updates

API Guide

Understand the status model, helper functions, update flows, and errors.

Capability-First Design

Capability-first means checking what the current install can do before starting an update flow. Instead of checking if (Platform.OS === 'android'), you check what the current environment supports:

import { getUpdateStatus } from '@rnforge/react-native-in-app-updates';

const status = await getUpdateStatus();

if (status.capabilities.immediate) {
  // Immediate updates are supported
}

if (status.capabilities.flexible) {
  // Flexible updates are supported
}

This approach works because:

  • iOS reports immediate and flexible update capabilities as false
  • iOS can still report store-page and version-lookup capability when App Store options are provided
  • Android installs that cannot run Play update flows report those flow capabilities as false
  • Android Play installs report the update capabilities allowed by Play Core
  • Your code works everywhere without platform checks

Typed Results, Not Exceptions

Expected states are returned as typed results, not thrown exceptions:

const status = await getUpdateStatus();

// Check if updates are supported
if (!status.supported) {
  console.log('Reason:', status.reason);
  // 'unsupported-platform' | 'unsupported-install-source' | etc.
} else if (status.updateAvailable) {
  console.log('Current:', status.currentVersion);
  console.log('Latest:', status.latestStoreVersion);
}

Exceptions are only thrown for actual errors:

  • Invalid input (e.g., missing required options)
  • Native bridge failures
  • Native layer failures
  • Unexpected runtime errors

Update Status

The UpdateStatus object is the core of the API. It tells you:

Prop

Type

Platform Support

status.supported        // boolean - are in-app updates supported?
status.reason           // string - why or why not
status.platform         // 'android' | 'ios'

Update Availability

status.updateAvailable          // boolean | null
status.currentVersion           // string - installed version
status.latestStoreVersion       // string - latest in store
status.currentBuild             // string | number - installed build
status.latestStoreBuild         // string | number - latest build in store

Capabilities

Prop

Type

status.capabilities.immediate           // boolean
status.capabilities.flexible            // boolean
status.capabilities.storePage           // boolean
status.capabilities.latestVersionLookup // boolean
status.capabilities.installStateListener // boolean

Allowed Flows

Even if a capability is supported, it might not be allowed right now:

Prop

Type

status.allowed.immediate  // boolean - can start immediate update now?
status.allowed.flexible   // boolean - can start flexible update now?

Install Status

For flexible updates, track the installation lifecycle:

status.installStatus
// 'unknown' | 'pending' | 'downloading' | 'downloaded' |
// 'installing' | 'installed' | 'failed' | 'canceled' | 'unsupported'

Platform-Specific Details

// Android details (only on Android)
status.android?.packageName
status.android?.playCore?.availableVersionCode
status.android?.playCore?.updatePriority

// iOS details (only on iOS)
status.ios?.bundleIdentifier
status.ios?.appStoreId
status.ios?.appStore?.version
status.ios?.appStore?.releaseNotes

Immediate Updates

Immediate updates show a blocking UI that forces the user to update before continuing:

import {
  getUpdateStatus,
  startImmediateUpdate,
  canStartImmediateUpdate
} from '@rnforge/react-native-in-app-updates';

const status = await getUpdateStatus();

if (canStartImmediateUpdate(status)) {
  try {
    const result = await startImmediateUpdate();
    // Update completed or app restarted
  } catch (error) {
    // Handle error (invalid-input, bridge-error, native-error, unexpected)
    console.error('Update failed:', error);
  }
}

When to use immediate updates:

  • Critical security fixes
  • Breaking API changes
  • Mandatory feature updates

Flexible Updates

Flexible updates download in the background while the user continues using the app:

Prop

Type

import {
  getUpdateStatus,
  startFlexibleUpdate,
  completeFlexibleUpdate,
  addInstallStateListener,
  canStartFlexibleUpdate,
} from '@rnforge/react-native-in-app-updates';

const status = await getUpdateStatus();

if (canStartFlexibleUpdate(status)) {
  let subscription: { remove: () => void } | undefined;

  subscription = addInstallStateListener((event) => {
    if (event.installStatus === 'downloading') {
      const progress = event.progress ?? 0;
      console.log(`Downloaded: ${(progress * 100).toFixed(1)}%`);
    }

    if (event.installStatus === 'downloaded') {
      console.log('Update downloaded, installing');
      void completeFlexibleUpdate().finally(() => {
        subscription?.remove();
      });
    }
  });

  try {
    const result = await startFlexibleUpdate();
    console.log('Flexible update result:', result.reason);
  } catch (error) {
    subscription?.remove();
    throw error;
  }
}

When to use flexible updates:

  • Non-critical feature updates
  • Performance improvements
  • UI enhancements
  • When you want to avoid interrupting the user

Store Page Fallback

If in-app updates aren't supported, you can still open the store page:

import { openStorePage, canOpenStorePage, getUpdateStatus } from '@rnforge/react-native-in-app-updates';

const storeOptions = {
  ios: {
    appStoreId: '123456789',
    country: 'us',
  },
};

const status = await getUpdateStatus(storeOptions);

if (canOpenStorePage(status)) {
  try {
    await openStorePage(storeOptions);
    // Opens Play Store on Android or App Store on iOS.
  } catch (error) {
    console.error('Failed to open store:', error);
  }
}

On Android, openStorePage() can be called without options. On iOS, pass ios.appStoreId to both getUpdateStatus() and openStorePage() so the status result can report the store-page capability correctly.

Error Handling

Async functions that call the native module can throw InAppUpdatesError. Helper functions only read an UpdateStatus object.

CodeMeaning
invalid-inputInvalid options or arguments.
bridge-errorReact Native bridge communication failed.
native-errorNative layer reported a failure.
unexpectedAny other unexpected failure.
import { InAppUpdatesError } from '@rnforge/react-native-in-app-updates';

try {
  await startImmediateUpdate();
} catch (error) {
  if (error instanceof InAppUpdatesError) {
    switch (error.code) {
      case 'invalid-input':
        // You passed invalid options
        console.error('Invalid input:', error.message);
        break;

      case 'bridge-error':
        // React Native bridge communication failed
        console.error('Bridge error:', error.message);
        break;

      case 'native-error':
        // Native layer reported an error
        console.error('Native error:', error.message);
        if (error.android) {
          console.error('Android details:', error.android);
        }
        break;

      case 'unexpected':
        // Something unexpected happened
        console.error('Unexpected error:', error.message);
        break;
    }
  } else {
    // Not an InAppUpdatesError
    throw error;
  }
}

Helper Functions

The API provides helper functions to check status before acting:

import {
  getUpdateStatus,
  isUpdateAvailable,
  canStartImmediateUpdate,
  canStartFlexibleUpdate,
  canCompleteFlexibleUpdate,
  canOpenStorePage,
  supportsInstallStateListener,
} from '@rnforge/react-native-in-app-updates';

const status = await getUpdateStatus();

if (isUpdateAvailable(status)) {
  console.log('Update available');
}

if (canStartImmediateUpdate(status)) {
  console.log('Can start immediate update');
}

if (canStartFlexibleUpdate(status)) {
  console.log('Can start flexible update');
}

if (canCompleteFlexibleUpdate(status)) {
  console.log('Can complete flexible update');
}

if (canOpenStorePage(status)) {
  console.log('Can open store page');
}

if (supportsInstallStateListener(status)) {
  console.log('Install state listener is supported');
}

These helpers check multiple conditions (support, capability, availability, allowed) so you don't have to.

Platform Notes

Android

  • Requires Google Play Store (sideloaded apps report unsupported-install-source)
  • Requires Google Play Services (devices without report play-core-unavailable)
  • APK expansion files are not supported (apk-expansion-files-unsupported)
  • Immediate updates may not be allowed during certain app states
  • Flexible updates require calling completeFlexibleUpdate() to install

iOS

  • Immediate and flexible update flows are not supported
  • Store page opening requires ios.appStoreId
  • Version lookup uses the App Store lookup API when ios.appStoreId is provided
  • getUpdateStatus() can return missing-app-store-id when App Store lookup options are missing
  • Use openStorePage() as the update mechanism

Common Patterns

Check for Update on App Start

import { getUpdateStatus, isUpdateAvailable } from '@rnforge/react-native-in-app-updates';

async function checkForUpdate() {
  const status = await getUpdateStatus();

  if (!status.supported) {
    console.log('Updates not supported:', status.reason);
    return null;
  }

  if (!isUpdateAvailable(status)) {
    console.log('App is up to date');
    return null;
  }

  return {
    current: status.currentVersion,
    latest: status.latestStoreVersion,
    releaseNotes: status.ios?.appStore?.releaseNotes,
  };
}

Progressive Update Flow

A common pattern is to try immediate updates first for critical updates, fall back to flexible for non-critical updates, and use the store page as a last resort. See App Integration for a full React component that implements this fallback chain.

Next Steps

On this page