AppForShare

How to share a React Native build with testers

· 6 min read

How to share a React Native build with testers

Getting a React Native build onto a tester's phone is two separate jobs: a release APK from Gradle for Android, and a signed IPA from Xcode for iPhone. Neither is hard, but each has one step that trips people up. On Android it is signing. On iOS it is the export method. This guide walks through both, plus the Expo EAS route, and ends with a single link your testers can open on either platform.

Debug builds are not test builds

The build you run with npx react-native run-android or from Xcode in Debug loads its JavaScript from Metro on your machine. Send that to a tester and the app opens to a red screen that says it is unable to load the script, because there is no Metro server on their network. A shareable build has to be a release build, where the JavaScript bundle (and Hermes bytecode, if you use Hermes) is packed inside the app.

Android: a release APK with Gradle

Build it

cd android
./gradlew assembleRelease

The file lands at android/app/build/outputs/apk/release/app-release.apk. That APK is what you share. The other common task, ./gradlew bundleRelease, makes an .aab for Google Play. Phones cannot install an app bundle directly, so it is not useful for testers outside Play.

Check the signing config

The React Native template signs the release build type with the debug keystore so that assembleRelease works out of the box. That APK installs, but:

  • Google Play rejects debug signed uploads, so you will need a real key anyway.
  • If a tester has a copy signed with one key and you send an update signed with another, Android refuses the update until they uninstall.

Set up a release keystore once in android/app/build.gradle, keep the passwords out of the repository (for example in ~/.gradle/gradle.properties or CI secrets), and sign every test build with it. Android app signing for APK, AAB and sideloading covers the key types in more detail.

Version every build

Bump versionCode and versionName in android/app/build.gradle before each round. Android will not install a build with a lower versionCode over a higher one, and testers need a way to tell builds apart.

iOS: archive and export for testing

Register test devices first

An iPhone installs a build from a link only when it is signed Ad Hoc, Development or Enterprise. For outside testers that usually means Ad Hoc, which installs only on devices whose UDID is in the provisioning profile. Collect UDIDs with the free UDID finder, add them in the Apple Developer portal, and regenerate the profile. Registering a UDID explains the steps.

From Xcode

  1. Open ios/YourApp.xcworkspace (the workspace, not the project, because of CocoaPods).
  2. Pick "Any iOS Device" as the destination and choose Product, Archive. The scheme's archive action uses the Release configuration by default.
  3. In the Organizer, choose Distribute App and pick Release Testing. That is the current Xcode name for Ad Hoc distribution. "Debugging" is the old Development option.
  4. Export, and you get a folder with the .ipa.

From the command line

For CI or repeatable builds, use xcodebuild with an export options file:

xcodebuild -workspace ios/YourApp.xcworkspace -scheme YourApp \
  -configuration Release -destination "generic/platform=iOS" \
  -archivePath build/YourApp.xcarchive archive

xcodebuild -exportArchive -archivePath build/YourApp.xcarchive \
  -exportOptionsPlist ios/ExportOptions.plist -exportPath build/ipa

In ExportOptions.plist, set method to release-testing and teamID to your team id. Xcode 15.4 renamed the old ad-hoc value, and the old name now gives a deprecation warning. For manual signing, add signingStyle set to manual and a provisioningProfiles entry that maps your bundle id to the Ad Hoc profile name.

Inspect before you share

The IPA inspector reads the IPA in your browser, without uploading it, and shows the distribution type, profile expiry and the provisioned UDIDs. Thirty seconds there saves a round of "it says unable to install" messages.

The Expo EAS alternative

If your project uses Expo, EAS Build has its own way to make test builds. In eas.json, a build profile with "distribution": "internal" produces builds meant for direct install:

{
  "build": {
    "preview": {
      "distribution": "internal",
      "android": { "buildType": "apk" }
    }
  }
}

Run eas build --profile preview --platform all. On Android, buildType: "apk" makes an installable APK instead of an app bundle. On iOS, EAS signs with an Ad Hoc profile and registers devices through eas device:create, so the same UDID rule applies. EAS gives each build its own URL.

Many teams stop there, and that is fine. Teams that build some apps outside EAS, or want one place for every project's builds with passwords, release notes and the same install page on both platforms, download the EAS artifact and upload it to AppForShare. Both approaches use the same underlying signing, so pick based on where you already build.

Share the build

With an APK and an Ad Hoc IPA in hand:

  1. Upload each file to AppForShare from the home page or the dashboard.
  2. Send testers the install link or show the QR code.
  3. They open it in the phone's browser and install over the air.

Android testers allow installs from unknown sources for their browser once; the phone asks for it the first time. From a pipeline, the upload is one line after the build:

cd android && ./gradlew assembleRelease && cd ..
npx --yes appforshare@1 upload android/app/build/outputs/apk/release/app-release.apk --notes "New onboarding flow"

The CLI reads an API token from APPFORSHARE_TOKEN (create one under Dashboard, Settings on Pro and Team). Uploading builds from CI has examples for GitHub Actions, GitLab, Bitrise and others.

Common React Native install problems

What the tester seesLikely causeFix
Red screen about loading the scriptDebug build sharedBuild with assembleRelease or the Release configuration
Android: "App not installed"Existing copy signed with another key, or a lower versionCodeUninstall the old copy, sign every build with the same key, bump versionCode
iPhone: "Unable to install" or "integrity could not be verified"UDID not in the profile, expired profile, or App Store exportRegister the device, regenerate the profile, export with Release Testing
App opens but shows no dataRelease build still points at a local API addressUse a reachable test backend in the release config
iPhone asks for Developer ModeDevelopment signed buildTurn on Developer Mode in Settings, Privacy and Security, or switch to Ad Hoc

One more cause of the "no data" case: release builds on Android block plain http:// traffic by default, so a test backend without HTTPS can fail only in the release build. More iOS causes are in can't install an app on an iOS device.

A routine that works

  • Every merge to your main branch builds a release APK and an Ad Hoc IPA.
  • Both go to the same AppForShare project with the commit message as release notes.
  • New testers send their UDID first; you rebuild the IPA once their device is in the profile.
  • Store builds still go through TestFlight and Google Play testing tracks before release.

Send your next React Native build

AppForShare turns each .apk and .ipa into an install link and QR code, with the same page for Android and iPhone testers. Account uploads also get a store readiness check, which is guidance rather than a promise of approval, and a comparison with the previous build. Start on the Free plan with 7 day links, or move to Pro at $12 a month for CI uploads, release notes and links that stay live while you are subscribed. Sign up free and share your build.

Keep reading

App Distribution Platform · · 9 min read

AppForShare.io vs Competitors - A Comparative Analysis

For small teams and startups, selecting a mobile app testing platform is a critical crossroad. This choice is more than a mere selection: it’s a strategic decision that directly impacts the...