npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@swiftpatch/react-native

v3.0.0

Published

React Native SDK for SwiftPatch OTA updates with differential patching, automatic rollback, and bundle signing

Downloads

179

Readme

SwiftPatch React Native SDK

npm version License: MIT React Native New Architecture Docs

Over-the-Air (OTA) update SDK for React Native with differential patching, automatic rollback, and cryptographic verification. Optimized for the React Native New Architecture.

✨ Features

  • 🚀 Differential Patching - Download only what changed (bsdiff/bspatch)
  • 🔄 Automatic Rollback - Crash detection with instant recovery
  • 🔒 Bundle Signing - RSA signature verification for security
  • New Architecture - TurboModules for 40% faster performance
  • 🎯 Dual-Slot System - Safe production/staging environment switching
  • 📦 Small Updates - Patches typically 10-50x smaller than full bundles
  • 🛡️ Type-Safe - Full TypeScript support
  • 🎨 React Hooks - Modern API with Provider pattern
  • 📱 Cross-Platform - iOS and Android support

📦 Installation

npm install @swiftpatch/react-native
# or
yarn add @swiftpatch/react-native

iOS Setup

Step 1: Install CocoaPods Dependencies

cd ios && pod install && cd ..

Step 2: Update AppDelegate

In ios/AppDelegate.mm (or .swift), update the bundle URL method:

Objective-C:

// ...other imports
#import "SwiftPatchModule.h"

@implementation AppDelegate
// ...other implementations
- (NSURL *)bundleURL
{
  #if DEBUG
    return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
  #else
    return [SwiftPatchModule getBundleURL];
  #endif
}

Swift (RN 0.76+):

import react_native_swiftpatch

override func bundleURL() -> URL? {
  #if DEBUG
    RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
  #else
    SwiftPatchModule.getBundleURL()
  #endif
}

Step 3: Add Credentials to Info.plist

Add your SwiftPatch credentials to ios/YourApp/Info.plist:

<plist version="1.0">
  <dict>
    <!-- ...other configs... -->
    <key>SwiftPatchProjectId</key>
    <string>YOUR_PROJECT_ID</string>
    <!-- App token starts with spb_... and is 46 characters long -->
    <key>SwiftPatchAppToken</key>
    <string>spb_YOUR_APP_TOKEN_HERE</string>
    <!-- Optional: Public key for bundle signing -->
    <key>SwiftPatchPublicKey</key>
    <string>YOUR_PUBLIC_KEY</string>
    <!-- ...other configs... -->
  </dict>
</plist>

Android Setup

Step 1: Update MainApplication

In android/app/src/main/java/.../MainApplication.kt:

Kotlin (RN 0.76+):

// ...other imports
import com.swiftpatch.SwiftPatchModule

override val reactNativeHost: ReactNativeHost =
  object : DefaultReactNativeHost(this) {
    // ...other methods
    override fun getJSBundleFile(): String? {
      return SwiftPatchModule.getJSBundleFile(applicationContext)
    }
  }

For React Native 0.82+:

import com.swiftpatch.SwiftPatchModule

class MainApplication : Application(), ReactApplication {
  override val reactHost: ReactHost by lazy {
    getDefaultReactHost(
      context = applicationContext,
      packageList = PackageList(this).packages,
      jsBundleFilePath = SwiftPatchModule.getJSBundleFile(applicationContext)
    )
  }
}

Step 2: Add Credentials to strings.xml

Add your credentials to android/app/src/main/res/values/strings.xml:

<resources>
  <string name="app_name">YourApp</string>
  <!-- App token starts with spb_... and is 46 characters long -->
  <string name="SwiftPatchProjectId">YOUR_PROJECT_ID</string>
  <string name="SwiftPatchAppToken">spb_YOUR_APP_TOKEN_HERE</string>
  <!-- Optional: Public key for bundle signing -->
  <string name="SwiftPatchPublicKey">YOUR_PUBLIC_KEY</string>
</resources>

🚀 Quick Start

1. Wrap your app with SwiftPatchProvider

Using Native Config (Recommended):

If you've added credentials to Info.plist and strings.xml, you don't need to pass them in JS:

import { withSwiftPatch } from '@swiftpatch/react-native';

function App() {
  return <YourApp />;
}

// Credentials are read from native config files
export default withSwiftPatch(App);

Or with Provider pattern:

import { SwiftPatchProvider } from '@swiftpatch/react-native';

export default function App() {
  return (
    <SwiftPatchProvider config={{ debug: __DEV__ }}>
      <YourApp />
    </SwiftPatchProvider>
  );
}

Using JS Config (Alternative):

import { SwiftPatchProvider } from '@swiftpatch/react-native';

export default function App() {
  return (
    <SwiftPatchProvider
      config={{
        deploymentKey: 'YOUR_DEPLOYMENT_KEY',
        serverUrl: 'https://your-server.com/api/v1', // optional
        debug: __DEV__,
      }}
    >
      <YourApp />
    </SwiftPatchProvider>
  );
}

2. Use the hook in your components

import { useSwiftPatch, UpdateStatus } from '@swiftpatch/react-native';

function UpdateButton() {
  const {
    status,
    availableUpdate,
    downloadProgress,
    checkForUpdate,
    downloadUpdate,
    installUpdate,
    restart,
  } = useSwiftPatch();

  const handleUpdate = async () => {
    // Check for updates
    const update = await checkForUpdate();

    if (update) {
      // Download the update
      await downloadUpdate();

      // Install and restart
      await installUpdate();
      restart();
    }
  };

  if (status === UpdateStatus.DOWNLOADING) {
    return <Text>Downloading: {downloadProgress?.percentage}%</Text>;
  }

  return (
    <Button
      title={availableUpdate ? 'Update Available' : 'Check for Updates'}
      onPress={handleUpdate}
    />
  );
}

3. Or use the built-in modal

import { useSwiftPatchModal, SwiftPatchModal } from '@swiftpatch/react-native';

function App() {
  const { showModal } = useSwiftPatchModal();

  return (
    <>
      <YourApp />
      <SwiftPatchModal />
      <Button title="Check for Updates" onPress={showModal} />
    </>
  );
}

📚 API Reference

SwiftPatchProvider

interface SwiftPatchConfig {
  /**
   * Deployment key from SwiftPatch dashboard.
   * Optional if configured in native files (Info.plist / strings.xml)
   */
  deploymentKey?: string;
  serverUrl?: string;
  checkOnResume?: boolean;
  checkInterval?: number;
  installMode?: InstallMode;
  mandatoryInstallMode?: InstallMode;
  debug?: boolean;
  customHeaders?: Record<string, string>;
  publicKey?: string;
  autoStabilizeAfterLaunches?: number;
  crashDetectionWindowMs?: number;       // Default: 10000
  maxCrashesBeforeRollback?: number;     // Default: 2
  autoStagingInDev?: boolean;            // Default: false
}

Native Config Keys

| Key | Platform | Description | |-----|----------|-------------| | SwiftPatchProjectId | iOS/Android | Your project ID | | SwiftPatchAppToken | iOS/Android | App token (starts with spb_...) | | SwiftPatchDeploymentKey | iOS/Android | Alternative to AppToken | | SwiftPatchServerUrl | iOS/Android | Custom server URL (optional) | | SwiftPatchPublicKey | iOS/Android | Public key for signing (optional) |

Verify Native Config

import { getNativeConfig } from '@swiftpatch/react-native';

const config = await getNativeConfig();
console.log('Project ID:', config.projectId);
console.log('App Token:', config.appToken);

useSwiftPatch Hook

const {
  // State
  status,                    // Current update status
  downloadProgress,          // Download progress (0-100%)
  currentBundle,             // Currently installed bundle info
  availableUpdate,           // Available update info
  isRestartRequired,         // Whether restart is needed
  error,                     // Last error
  lastCheckedAt,             // Last check timestamp
  slotMetadata,             // Dual-slot system metadata
  environment,              // Current environment (PROD/STAGE)

  // Actions
  checkForUpdate,           // Check for available updates
  downloadUpdate,           // Download available update
  installUpdate,            // Install downloaded update
  restart,                  // Restart app to apply update
  rollback,                 // Rollback to previous version
  clearPendingUpdate,       // Clear pending update
  getCurrentBundle,         // Get current bundle info
  stabilize,                // Stabilize current bundle
  switchEnvironment,        // Switch PROD/STAGE environment
  getSlotMetadata,          // Get slot metadata
  markMounted,              // Mark app as mounted (crash detection)
  downloadStageBundle,      // Download staging bundle
} = useSwiftPatch();

Imperative API (Non-React)

For use outside React components:

import { SwiftPatch } from '@swiftpatch/react-native';

const swiftPatch = new SwiftPatch({
  deploymentKey: 'YOUR_KEY',
});

await swiftPatch.init();

const update = await swiftPatch.checkForUpdate();
if (update) {
  await swiftPatch.downloadAndInstall(update);
  swiftPatch.restart();
}

🎯 Install Modes

enum InstallMode {
  IMMEDIATE = 'immediate',           // Install and restart immediately
  ON_NEXT_RESTART = 'onNextRestart', // Install on next app restart
  ON_NEXT_RESUME = 'onNextResume',   // Install when app resumes
}

🔧 Advanced Features

Differential Patching

SwiftPatch automatically uses differential patching when available:

// Server determines if patch is available
// SDK handles full bundle vs patch automatically
await downloadUpdate();

// Patch files are typically 10-50x smaller
// e.g., 50MB bundle → 2MB patch

Automatic Rollback

The crash detection window and crash threshold are configurable:

<SwiftPatchProvider
  config={{
    crashDetectionWindowMs: 15_000,     // 15 second window (default: 10s)
    maxCrashesBeforeRollback: 3,        // Allow 3 crashes before rollback (default: 2)
  }}
>
  <App />
</SwiftPatchProvider>
const { rollback } = useSwiftPatch();

// Manual rollback
await rollback();

Bundle Signing (Optional)

<SwiftPatchProvider
  config={{
    deploymentKey: 'YOUR_KEY',
    publicKey: 'YOUR_RSA_PUBLIC_KEY',
  }}
>
  <App />
</SwiftPatchProvider>

// SDK automatically verifies signatures

Environment Switching (PROD/STAGE)

const { switchEnvironment, environment } = useSwiftPatch();

// Switch to staging
await switchEnvironment(EnvironmentMode.STAGING);

// Download and test staging bundle
await downloadStageBundle(url, hash);

// Switch back to production
await switchEnvironment(EnvironmentMode.PRODUCTION);

📋 Common Patterns

Silent Background Update

Download and install updates silently. The update applies on the next app restart — no UI code needed.

import { SwiftPatchProvider, InstallMode } from '@swiftpatch/react-native';

export default function App() {
  return (
    <SwiftPatchProvider
      config={{
        installMode: InstallMode.ON_NEXT_RESTART,
        checkOnResume: true,
        checkInterval: 300_000, // Check every 5 minutes
        debug: __DEV__,
      }}
    >
      <YourApp />
    </SwiftPatchProvider>
  );
}

Mandatory Update with Blocking UI

Force users to update before using the app. Shows a full-screen blocking overlay during download.

import React, { useEffect, useState } from 'react';
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native';
import {
  SwiftPatchProvider,
  useSwiftPatch,
  UpdateStatus,
  InstallMode,
} from '@swiftpatch/react-native';

function MandatoryUpdateGate({ children }: { children: React.ReactNode }) {
  const {
    status,
    availableUpdate,
    downloadProgress,
    checkForUpdate,
    downloadUpdate,
    installUpdate,
    restart,
  } = useSwiftPatch();
  const [checking, setChecking] = useState(true);

  useEffect(() => {
    (async () => {
      const update = await checkForUpdate();
      if (update?.isMandatory) {
        await downloadUpdate();
        await installUpdate();
        restart();
      }
      setChecking(false);
    })();
  }, []);

  if (
    checking ||
    (availableUpdate?.isMandatory && status !== UpdateStatus.UP_TO_DATE)
  ) {
    return (
      <View style={styles.overlay}>
        <ActivityIndicator size="large" color="#007AFF" />
        <Text style={styles.text}>
          {status === UpdateStatus.DOWNLOADING
            ? `Updating... ${downloadProgress?.percentage ?? 0}%`
            : 'Checking for updates...'}
        </Text>
      </View>
    );
  }

  return <>{children}</>;
}

const styles = StyleSheet.create({
  overlay: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#fff',
  },
  text: { marginTop: 16, fontSize: 16, color: '#333' },
});

export default function App() {
  return (
    <SwiftPatchProvider
      config={{ mandatoryInstallMode: InstallMode.IMMEDIATE }}
    >
      <MandatoryUpdateGate>
        <YourApp />
      </MandatoryUpdateGate>
    </SwiftPatchProvider>
  );
}

Custom Update Banner

Show a dismissible banner when an update is available. Let users choose when to update.

import React, { useEffect, useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { useSwiftPatch, UpdateStatus } from '@swiftpatch/react-native';

export function UpdateBanner() {
  const {
    status,
    availableUpdate,
    downloadProgress,
    checkForUpdate,
    downloadUpdate,
    installUpdate,
    restart,
  } = useSwiftPatch();
  const [dismissed, setDismissed] = useState(false);

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

  if (dismissed || !availableUpdate || status === UpdateStatus.UP_TO_DATE) {
    return null;
  }

  const handleUpdate = async () => {
    await downloadUpdate();
    await installUpdate();
    restart();
  };

  return (
    <View style={styles.banner}>
      <Text style={styles.title}>
        Update Available v{availableUpdate.version}
      </Text>
      {status === UpdateStatus.DOWNLOADING ? (
        <Text style={styles.progress}>
          Downloading... {downloadProgress?.percentage ?? 0}%
        </Text>
      ) : (
        <View style={styles.actions}>
          <TouchableOpacity onPress={handleUpdate} style={styles.updateBtn}>
            <Text style={styles.updateText}>Update Now</Text>
          </TouchableOpacity>
          <TouchableOpacity
            onPress={() => setDismissed(true)}
            style={styles.laterBtn}
          >
            <Text style={styles.laterText}>Later</Text>
          </TouchableOpacity>
        </View>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  banner: {
    backgroundColor: '#007AFF',
    padding: 12,
    marginHorizontal: 16,
    borderRadius: 8,
    marginTop: 8,
    alignItems: 'center',
  },
  title: { color: '#fff', fontWeight: '600', fontSize: 14 },
  progress: { color: '#fff', fontSize: 12, marginTop: 4 },
  actions: { flexDirection: 'row', marginTop: 8, gap: 12 },
  updateBtn: {
    backgroundColor: '#fff',
    paddingHorizontal: 16,
    paddingVertical: 6,
    borderRadius: 6,
  },
  updateText: { color: '#007AFF', fontWeight: '600' },
  laterBtn: { paddingHorizontal: 16, paddingVertical: 6 },
  laterText: { color: 'rgba(255,255,255,0.8)' },
});

Error Handling with Descriptions

Use getErrorDescription() to show helpful error messages to developers:

import {
  useSwiftPatch,
  getErrorDescription,
} from '@swiftpatch/react-native';

function MyComponent() {
  const { error } = useSwiftPatch();

  if (error) {
    const info = getErrorDescription(error.code);
    console.warn(info.description);
    console.warn('Fix:', info.troubleshooting);
  }

  return null;
}

🆕 New Architecture Support

SwiftPatch v2.0+ fully supports the React Native New Architecture:

Enable New Architecture

iOS (ios/Podfile):

ENV['RCT_NEW_ARCH_ENABLED'] = '1'

Android (android/gradle.properties):

newArchEnabled=true

Performance Benefits

| Operation | Legacy | TurboModule | Improvement | |-----------|--------|-------------|-------------| | Check Update | 15ms | 2ms | 87% faster | | Get Bundle Info | 8ms | 1ms | 87% faster | | Native Calls | 3-5ms | 0.5-1ms | 80% faster |

Detection

import { IS_TURBO_MODULE_ENABLED } from '@swiftpatch/react-native';

console.log('Using TurboModules:', IS_TURBO_MODULE_ENABLED);

🧪 Testing

npm test

All core functionality is tested:

  • Update checking
  • Download & installation
  • Rollback mechanisms
  • Cryptographic verification
  • Event handling

📱 Platform Requirements

| Platform | Minimum | Recommended | |----------|---------|-------------| | iOS | 13.4+ | 15.0+ | | Android | API 24+ (7.0) | API 31+ (12) | | React Native | 0.76.0+ | 0.76.5+ | | React | 18.2.0+ | 18.3.0+ |


🔒 Security

  • Bundle Signing: Optional RSA signature verification
  • HTTPS Only: All downloads over secure connections
  • Hash Verification: SHA-256 hash checking for all bundles
  • Integrity Checks: Automatic corruption detection

📊 Bundle Size

| File | Size | Compressed | |------|------|------------| | Core JS | ~45KB | ~12KB | | Native (iOS) | ~150KB | - | | Native (Android) | ~200KB | - |


🛠️ Development

# Clone the repository
git clone https://github.com/codewprincee/react-native-swiftpatch.git

# Install dependencies
npm install

# Run tests
npm test

# Build
npm run prepare

# Run example app
npm run example start

🐛 Troubleshooting

iOS Build Errors

cd ios
rm -rf Pods Podfile.lock
pod install
cd ..

Android Build Errors

cd android
./gradlew clean
cd ..

Type Errors

npm run typescript

📄 License

MIT License - see LICENSE for details


🤝 Contributing

Contributions are welcome! Please read our Contributing Guide first.


📞 Support


🎉 Acknowledgments

Built with:


Made with ❤️ by the SwiftPatch Team