AeroPush

SDK

Installation

Five steps: install the package, wire the iOS and Android bundle hooks, initialise in JavaScript, and verify. Autolinking handles the rest — there’s no manual native linking.

Note
First time here? Create an account & get your app key before you start — you’ll need the apk_live_… key in step 4.
Note
Debug builds always load from Metro. Everything below only affects release builds, so test OTA against a release build.

1. Install the package

npm install react-native-aeropush

The package is a New Architecture Turbo Module. React Native Codegen generates the native interface automatically the next time you build.

2. iOS setup

2.1 — Install pods

shell
cd ios && pod install

Autolinking picks up the podspec and generates AeropushSpec. No manual linking.

2.2 — Bridging header

The bundle hook calls the SDK’s Objective-C++ launcher from Swift, so the app target needs a bridging header. Create ios/<AppName>/<AppName>-Bridging-Header.h:

<AppName>-Bridging-Header.h
#import "Aeropush.h"

Then point Build Settings → Objective-C Bridging Header at it for both the Debug and Release configurations. If you already have a bridging header, just add the import.

2.3 — Bundle hook

Override bundleURL() so release builds boot from the staged OTA bundle. Aeropush.bundleURL() runs the launch-counter check (auto-rollback) and returns nil when no OTA bundle is active.

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

  override func bundleURL() -> URL? {
#if DEBUG
    RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
    // Prefer the active OTA bundle; fall back to the embedded binary bundle.
    Aeropush.bundleURL()
      ?? Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
  }
}

3. Android setup

Autolinking registers the Turbo Module. The only manual step is the bundle hook in MainApplication.kt. AeropushModule.getJSBundleFile(context) runs the launch-counter check and returns null when no OTA bundle is active (falling back to the embedded assets://index.android.bundle).

import com.aeropush.AeropushModule

class MainApplication : Application(), ReactApplication {
  override val reactHost: ReactHost by lazy {
    getDefaultReactHost(
      context = applicationContext,
      packageList = PackageList(this).packages,
      // AeroPush OTA hook: boot from the staged bundle when one is active.
      jsBundleFilePath =
        if (BuildConfig.DEBUG) null
        else AeropushModule.getJSBundleFile(applicationContext),
    )
  }
}

4. JavaScript setup

Initialise once at module scope (before first render), sync on launch, and wrap your app in the crash boundary. Get your appKey from the dashboard.

App.tsx
import AeroPush, { AeroPushBoundary, InstallMode } from 'react-native-aeropush';

// 1. Initialise before the component tree renders.
AeroPush.init({ appKey: 'YOUR_APP_KEY', channel: 'production' });

// 2. Check for + stage updates (e.g. on launch).
AeroPush.sync({ installMode: InstallMode.ON_NEXT_RESTART });

// 3. Crash guard: a render crash marks the bundle failed; a successful
//    mount marks it healthy (resets the native launch counter).
export default function App() {
  return (
    <AeroPushBoundary>
      <YourApp />
    </AeroPushBoundary>
  );
}
Important
If you don’t use AeroPushBoundary, call AeroPush.markBundleHealthy() yourself after a successful mount — otherwise the native launch counter rolls a healthy bundle back after 3 launches.

5. Verify

Make a release build, then publish a bundle and relaunch:

shell
export AEROPUSH_APP_KEY=apk_live_…
npx aeropush release --channel production

The app picks up the new bundle on its next cold launch. Next, see the full SDK API or the CLI reference.

Optional: Metro banner

Wrap your Metro config to print a small AeroPush banner whenever the dev server starts — a handy confirmation that AeroPush is wired in, and a reminder that OTA only affects release builds. It returns your config unchanged.

metro.config.js
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const { withAeroPush } = require('react-native-aeropush/metro');

const config = {};

module.exports = withAeroPush(mergeConfig(getDefaultConfig(__dirname), config));

On npx react-native start you’ll see:

▲ AeroPush v0.1.22 · over-the-air updates active
  Debug builds load from Metro — OTA updates apply to release builds.
  Docs: https://aeropush.tech/docs
Note
Pass withAeroPush(config, { banner: false }) or set AEROPUSH_NO_BANNER=1 to silence it.