Skip to main content

Implementing OTA Updates

1. Setting Up CodePush

Property Description Values
appVersion Current version of the app "1.0.0"
deploymentKey Deployment key for the CodePush server "your-deployment-key"
serverURL URL of the CodePush server "https://codepush.appcenter.ms"

Example: Initializing CodePush

import codePush from "react-native-codepush";

let codePushOptions = {
  appVersion: "1.0.0",
  deploymentKey: "your-deployment-key",
  serverURL: "https://codepush.appcenter.ms"
};

codePush.sync(codePushOptions);

2. Implementing Silent Updates

Property Description Default Notes
updateUrl URL to fetch the update manifest None Required for silent updates
checkFrequency How often to check for updates (in minutes) 60 Set to 0 to disable automatic checks
autoUpdate Whether to apply updates silently false Set to true for silent updates
updateMode Update behavior: 'immediate', 'onNextResume', or 'onNextRestart' 'immediate' Controls when updates are applied

Example: Silent Update Configuration

const config = {
  updateUrl: 'https://myapp.com/updates/latest',
  autoUpdate: true,
  checkFrequency: 30,
  updateMode: 'immediate'
};

// Apply silent updates
const updateManager = new UpdateManager(config);
updateManager.start();
Note: Silent updates require the app to be built with the react-native-code-push or similar library.
Warning: Silent updates can cause app instability if not properly tested. Always validate updates in a staging environment before deploying to production.

3. Handling Mandatory Updates

Property Description Default Notes
mandatory Indicates whether the update must be installed immediately false When set to true, the app will not allow users to skip the update
updateUrl URL to download the update package null Required if mandatory is true
version Version of the update null Used for tracking and logging purposes

Example: Mandatory Update Configuration

{
  "mandatory": true,
  "updateUrl": "https://example.com/updates/app-update-v2.0.zip",
  "version": "2.0.0"
}
Note: Mandatory updates should be used sparingly to avoid frustrating users. Ensure that the update includes critical security fixes or essential functionality improvements before marking it as mandatory.
Warning: If an update is marked as mandatory, the app will block access to the main UI until the update is installed. Make sure that users are informed about the necessity of the update and its impact on their experience.

4. Using Expo Updates

Example: Configuring Expo Updates

{
  "expo": {
    "updates": {
      "url": "https://exp.host/@your-app-username/your-app-name"
    },
    "assetBundlePatterns": [
      "**/*"
    ]
  }
}
Property Description Default Notes
url URL to the update manifest None Required for OTA updates
enabled Whether to enable OTA updates true Can be disabled for testing
checkAutomatically How often to check for updates ON_LOAD Options: ON_LOAD, ON_ERROR, NEVER
Note: Updates are only available for apps published to Expo Go or a standalone app built with expo build.

Example: Checking for Updates

import * as Updates from 'expo-updates';

async function checkForUpdate() {
  try {
    const update = await Updates.checkForUpdateAsync();
    if (update.isAvailable) {
      await Updates.fetchUpdateAsync();
      await Updates.reloadAsync();
    }
  } catch (error) {
    console.error(error);
  }
}

Update Flow

  • User opens app
  • App checks for update at configured URL
  • If update exists, downloads and applies it
  • App reloads with new code

5. Managing Update Rollbacks

Property Description Default Notes
rollback Enables or disables automatic rollback on failed updates false Set to true to enable automatic rollback
maxRollbackAttempts Maximum number of rollback attempts before giving up 3 Used to prevent infinite rollback loops
rollbackTimeout Time (in ms) to wait before attempting a rollback 5000 Allows time for app to stabilize before rollback

Example: Configuring Rollback Behavior

const config = {
  rollback: true,
  maxRollbackAttempts: 3,
  rollbackTimeout: 5000
};

// Apply to OTA update manager
updateManager.setConfig(config);
Note: Rollback behavior is handled by the OTA update manager and must be configured before updates are applied.

Rollback Process Overview

  • Update fails during installation or runtime
  • OTA manager checks rollback flag
  • If enabled, attempts to restore previous stable version
  • Tracks maxRollbackAttempts to prevent infinite loops
  • Uses rollbackTimeout to ensure app stability before rollback

Manual Rollback Workflow

  1. Identify failed update version
  2. Fetch previous stable version from server
  3. Install and validate previous version
  4. Update local state to reflect rollback
  5. Notify user of successful rollback

6. Testing OTA Updates

Example: Testing OTA Update Flow

import { Platform } from 'react-native';
import { checkForUpdateAsync, fetchUpdateAsync, reloadAsync } from 'expo-updates';

const testOTAUpdate = async () => {
  try {
    // Check for available update
    const update = await checkForUpdateAsync();
    
    if (update.isAvailable) {
      console.log('Update available:', update);
      
      // Fetch the update
      await fetchUpdateAsync();
      
      // Reload to apply update (only works in standalone app)
      if (Platform.OS !== 'web') {
        await reloadAsync();
      }
    } else {
      console.log('No update available');
    }
  } catch (error) {
    console.error('OTA update test failed:', error);
  }
};

testOTAUpdate();
Method Description Platform Support Notes
checkForUpdateAsync() Checks if an update is available iOS / Android Returns update info or null if no update
fetchUpdateAsync() Downloads the update package iOS / Android Must be called before reload
reloadAsync() Applies and reloads the app with new update iOS / Android Only works in standalone apps, not dev mode
Note: OTA updates only work in standalone apps (built with EAS Build or Expo Go). In development mode, updates are not applied.
Warning: Ensure all tests are run in a production-like environment to simulate real user conditions.

Testing OTA Update Workflow

  1. Build and deploy an update to a test channel (e.g., test)
  2. Ensure the app is configured to check for updates on launch
  3. Run the app in a standalone build (not Expo Go)
  4. Verify that checkForUpdateAsync() detects the new version
  5. Call fetchUpdateAsync() and reloadAsync()
  6. Confirm the app reloads with updated code

7. Implementing Update UI

Example: Basic Update UI Component

import React, { useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

const UpdateUI = ({ onUpdate, onCancel }) => {
  const [progress, setProgress] = useState(0);

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Update Available</Text>
      <Text style={styles.message}>A new version is ready to install.</Text>
      <View style={styles.progressBar}>
        <View
          style={[styles.progressFill, { width: `${progress}%` }]}
        />
      </View>
      <Text style={styles.progressText}>{progress}%</Text>
      <View style={styles.buttonContainer}>
        <Button title="Install Now" onPress={onUpdate} />
        <Button title="Later" onPress={onCancel} color="#ccc" />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 20,
    backgroundColor: '#fff',
    borderRadius: 10,
    alignItems: 'center',
  },
  title: {
    fontSize: 18,
    fontWeight: 'bold',
    marginBottom: 10,
  },
  message: {
    fontSize: 14,
    marginBottom: 20,
    textAlign: 'center',
  },
  progressBar: {
    width: '100%',
    height: 10,
    backgroundColor: '#eee',
    borderRadius: 5,
    marginBottom: 10,
  },
  progressFill: {
    height: '100%',
    backgroundColor: '#4CAF50',
    borderRadius: 5,
  },
  progressText: {
    fontSize: 12,
    color: '#666',
    marginBottom: 20,
  },
  buttonContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    width: '100%',
  },
});

export default UpdateUI;
Property Description Default Value
onUpdate Callback function to initiate the update installation null
onCancel Callback function to cancel the update null
progress Current progress of the update in percentage (0-100) 0
Note: The progress bar should be updated in real-time during the update process to provide feedback to the user.
Warning: Ensure that the update UI is displayed only when an update is actually available to avoid misleading users.

8. Configuring Update Policies

Policy Description Default Value Notes
updatePolicy Defines when and how updates are applied Background Can be Background, Immediate, or Manual
checkFrequency How often to check for updates EveryLaunch Options: EveryLaunch, Weekly, Monthly
minBackgroundFetchInterval Minimum time between background fetches (in seconds) 30 Only applies to Background policy
autoUpdate Whether to automatically download and apply updates true Set to false for manual control

Example: Configuring Update Policies in Code

const config = {
  updatePolicy: 'Background',
  checkFrequency: 'EveryLaunch',
  minBackgroundFetchInterval: 30,
  autoUpdate: true
};

// Apply config to your OTA update client
Note: Policies can be overridden at runtime or via remote configuration.

9. Monitoring Update Success Rates

Example: Basic Success Rate Tracking

import { AppState } from 'react-native';

const updateSuccessTracker = {
  totalUpdates: 0,
  successfulUpdates: 0,
  failedUpdates: 0,
  lastUpdateStatus: null,
};

const trackUpdateSuccess = (status) => {
  updateSuccessTracker.totalUpdates += 1;
  updateSuccessTracker.lastUpdateStatus = status;

  if (status === 'success') {
    updateSuccessTracker.successfulUpdates += 1;
  } else if (status === 'failure') {
    updateSuccessTracker.failedUpdates += 1;
  }
};

// Example usage with a hypothetical OTA update library
const handleUpdate = async () => {
  try {
    const result = await checkForUpdates();
    if (result.updateAvailable) {
      const updateResult = await downloadAndInstallUpdate();
      trackUpdateSuccess(updateResult ? 'success' : 'failure');
    }
  } catch (error) {
    trackUpdateSuccess('failure');
  }
};
Metric Description Formula Threshold
Success Rate Percentage of successful OTA updates (successfulUpdates / totalUpdates) * 100 ≥ 95%
Failure Rate Percentage of failed OTA updates (failedUpdates / totalUpdates) * 100 ≤ 5%
Update Latency Average time taken to download and install update avg(downloadTime + installTime) < 30s
Note: Success rate tracking should be implemented at both the client and server level to ensure accurate reporting.
Warning: If the success rate drops below 95%, consider implementing rollback mechanisms or alerting system administrators.

Key Metrics to Track

  • Success Rate: Measures the reliability of OTA updates.
  • Failure Rate: Identifies potential issues with update distribution.
  • Update Latency: Helps optimize the update delivery process.

10. Handling Update Errors

Error Type Description Handling Strategy Recovery Options
Network Timeout Update download fails due to network issues. Retry with exponential backoff or notify user. Retry, fallback to cached version, or manual update.
Download Failure Update file corrupted or incomplete. Validate checksums before installation. Re-download, restore from previous version.
Installation Failure Update fails to install due to system constraints. Log error and revert to previous version. Manual update, system reboot, or rollback.
Signature Mismatch Update does not match expected signature. Reject update and report error to server. Retry with valid update, notify admin.

Example: Error Handling with Expo Updates

import { updateAsync, shouldRollBack } from 'expo-updates';

async function handleUpdateError(error) {
  console.error('Update failed:', error);
  if (shouldRollBack()) {
    await updateAsync(); // Rollback to previous version
  }
}
Note: Always log update errors for debugging and monitoring purposes.
Warning: Never allow a failed update to crash the app without proper error recovery.

Was this article helpful?