Fingerprint Update Strategy

The "Fingerprint" update strategy in hot-updater leverages Expo's @expo/fingerprint library to track and manage changes in your project's native code. This mechanism significantly enhances the stability of app updates by preventing the delivery of JavaScript bundles incompatible with the native code.

Why Choose Fingerprint Strategy?

The Fingerprint strategy ensures OTA update compatibility by preventing incompatible JavaScript bundles from being delivered to your users. It automatically detects when native code changes would break compatibility and blocks unsafe OTA deployments.

Fingerprint vs App Version Strategy

  • Fingerprint Strategy (Recommended): Automatically detects native compatibility changes and prevents incompatible OTA updates
  • App Version Strategy: Manually specify version targets, but no automatic compatibility checking

Choose Fingerprint if you want automatic protection against incompatible updates. Choose App Version if you prefer manual control over update targeting.

How It Works: Real Development Scenarios

Scenario 1: JavaScript/UI Changes ✅ OTA Update

// ✅ These changes are safe for OTA updates
// - Updating component logic
// - Changing styles and layouts  
// - Adding new screens
// - Modifying business logic
// - Updating API calls

Result: Fingerprint stays the same → OTA update deployed safely

Scenario 2: Native Code Changes ⚠️ Native Rebuild Required

# ⚠️ These changes require native rebuild
# - Installing new native modules (react-native-camera)
# - Updating React Native version
# - Modifying native iOS/Android code
# - Changing build configurations

Result: Fingerprint changes → OTA deployment blocked → Native rebuild needed

The Safety Net

When you try to deploy an OTA update but the fingerprint has changed:

$ pnpm hot-updater deploy
 Fingerprint mismatch detected!
💡 Native code has changed. You need to:
1. Run: pnpm hot-updater fingerprint create
2. Rebuild and resubmit your app
3. Then deploy OTA updates

Essential Workflow: Before App Store Submission

Step 1: Generate Fingerprint Before Native Build

Before building your app for the App Store, always generate the fingerprint:

npm
pnpm
yarn
npx hot-updater fingerprint create

This command:

  • Calculates a hash representing your native code state
  • Updates ios/YourApp/Info.plist with the fingerprint
  • Updates android/app/src/main/res/values/strings.xml with the fingerprint

Step 2: Build and Submit to App Store

Now build your native app with the embedded fingerprint and submit to App Store.

Step 3: Deploy OTA Updates Safely

After your app is live, you can deploy JavaScript-only updates:

npm
pnpm
yarn
npx hot-updater deploy

The system automatically ensures only compatible updates reach your users.

Quick Setup

1. Enable Fingerprint Strategy

Fingerprint is the default strategy, but you can explicitly set it:

hot-updater.config.ts
import { defineConfig } from "hot-updater";

export default defineConfig({
  updateStrategy: "fingerprint", // This is the default
});

2. Include Additional Files (Optional)

If you have extra files that affect native compatibility:

hot-updater.config.ts
import { defineConfig } from "hot-updater";

export default defineConfig({
  updateStrategy: "fingerprint",
  fingerprint: {
    extraSources: [
      "config/native-config.json", // Custom native configurations
      "scripts/post-build.sh",     // Build scripts that affect native code
    ],
    debug: true, // See detailed fingerprint calculation info
  },
});

Client-Side Configuration withgetUpdateSource

When using the fingerprint strategy in your React Native application, you need to configure the getUpdateSource function with the updateStrategy set to "fingerprint". This ensures that the client app sends the correct fingerprint hash when checking for updates.

App.tsx
import { HotUpdater, getUpdateSource } from "@hot-updater/react-native";
import { View, Text } from "react-native";

function App() {
  return (
    <View>
      <Text>Hello World</Text>
    </View>
  );
}

export default HotUpdater.wrap({
  source: getUpdateSource("https://your-update-server.com/api/update-check", {
    updateStrategy: "fingerprint", // Configure for fingerprint strategy
  }),
  // ... other options
})(App);

The getUpdateSource function will automatically construct the correct endpoint URL for the fingerprint strategy: GET {baseUrl}/fingerprint/:platform/:fingerprintHash/:channel/:minBundleId/:bundleId

Managing and Updating Fingerprints

It's crucial to check and, if necessary, update the fingerprint whenever changes are made to the native code.

  1. Check Current Fingerprint:

    npm
    pnpm
    yarn
    npx hot-updater fingerprint

    This command calculates the current project's fingerprint and shows whether it matches the stored value.

  2. Create a New Fingerprint: If the command above indicates a mismatch, you must generate a new fingerprint using:

    npm
    pnpm
    yarn
    npx hot-updater fingerprint create

    Important: After creating a new fingerprint, you must rebuild your app. This ensures that the updated native environment and the new fingerprint are correctly incorporated into the app binary, preventing compatibility issues with apps built using the previous fingerprint.

Advanced: Multiple App Targets

Whatfingerprint create Does Automatically

When you run pnpm hot-updater fingerprint create, it automatically:

  • Updates your main ios/YourApp/Info.plist
  • Updates your main android/app/src/main/res/values/strings.xml
  • Embeds the fingerprint hash in these files

When You Need Additional Configuration

Only configure additional paths if you have:

  • iOS app extensions (widgets, share extensions, watch apps)
  • Multiple Android build variants with separate string files
  • Non-standard project structure
hot-updater.config.ts
import { defineConfig } from "hot-updater";

export default defineConfig({
  updateStrategy: "fingerprint",
  platform: {
    ios: {
      infoPlistPaths: [
        // Main app (handled automatically)
        "ios/YourApp/Info.plist",
        // Additional targets you need to include
        "ios/YourAppWidget/Info.plist",
        "ios/YourAppExtension/Info.plist"
      ]
    },
    android: {
      stringResourcePaths: [
        // Main strings (handled automatically)  
        "android/app/src/main/res/values/strings.xml",
        // Additional build variants
        "android/app/src/debug/res/values/strings.xml"
      ]
    }
  }
});

💡 Most projects don't need this configuration - the default behavior handles standard React Native projects perfectly.

Fingerprint File Example (fingerprint.json)

{
  "ios": {
    "hash": "11142b9062165fa48665f5efa095dd94e9e45eb0",
    // ... other details
  },
  "android": {
    "hash": "c763ed5729a0bcccf23248ee0183ddf9016c2e6e",
    // ... other details
  }
}

Endpoint Testing

You can test if the update check endpoint, configured via the hot-updater init command, is working correctly. When using the fingerprint strategy, the endpoint format is typically:

  • GET /check-update/fingerprint/:platform/:fingerprintHash/:channel/:minBundleId/:bundleId

Example of testing with curl:

curl "https://your-update-endpoint.com/check-update/fingerprint/ios/11142b9062165fa48665f5efa095dd94e9e45eb0/production/00000000-0000-0000-0000-000000000000/00000000-0000-0000-0000-000000000001"

Request Parameter Descriptions:

  • :platform: The platform, such as ios or android.
  • :fingerprintHash: The current fingerprint hash of the client app.
  • :channel: The channel name you want to check for updates (e.g., default, staging).
  • :minBundleId: The minimum bundle ID supported by the client (this value is updated when the native app is rebuilt. If unknown for testing, you can use 00000000-0000-0000-0000-000000000000).
  • :bundleId: The client's current bundle ID.

This request allows you to verify if an update is available for a specific fingerprint hash and channel.