React Native iOS Apps

Overview

This guide covers building React Native iOS applications for Pie testing. The build must be self-contained with all JavaScript code bundled, so it can run without connecting to Metro bundler.

⚠️
Important: These instructions apply to React Native CLI projects. Expo projects require a different build process.

What You’ll Need

  • React Native CLI: Version 0.60 or higher
  • macOS Computer: iOS builds cannot be created on Windows or Linux
  • Xcode: Latest version installed with command line tools
  • Node.js: With npm or yarn installed
  • CocoaPods: If your project uses native dependencies

Choose Your Build Method

React Native projects can be built using two methods:

  1. Manual Bundling: Full control over the bundling process (recommended for first-time builds)
  2. Automated Bundling: Faster process if your project is already configured
ℹ️
Which method should I use? Check your project’s Build Phases in Xcode. If you see “Bundle React Native code and images”, use the automated method. Otherwise, use manual bundling.

Manual Bundling Method

Step 1: Install Dependencies

Open terminal in your project root:

# Install JavaScript dependencies
npm install
# or
yarn install

# Install CocoaPods dependencies (if applicable)
cd ios && pod install && cd ..

Step 2: Stop Metro Bundler

Ensure no Metro bundler is running to avoid conflicts:

# Kill any running Metro instances
killall -9 node

Alternatively, close any terminal windows running Metro bundler.

Step 3: Bundle JavaScript Code

Run the bundle command from your project root:

npx react-native bundle \
  --entry-file index.js \
  --platform ios \
  --dev false \
  --bundle-output ios/main.jsbundle \
  --assets-dest ios
ℹ️

Entry file variations: Your project might use:

  • index.js — Standard JavaScript entry
  • index.tsx — TypeScript entry
  • index.ios.js — Platform-specific entry
  • Custom entry defined in package.json or app.json

This command creates:

  • Production JavaScript bundle at ios/main.jsbundle
  • Asset files (images, fonts) in the ios folder
  • Hermes bytecode files (.hbc) if using Hermes engine
  • An assets folder (only if your project has images or fonts)

Step 4: Add Bundle to Xcode

  1. Open your iOS project:

    open ios/YourProjectName.xcworkspace

    (or .xcodeproj if not using CocoaPods)

  2. In Project Navigator (left panel), right-click your project name

  3. Select “Add Files to ‘[YourProjectName]’”

  4. Navigate to the ios directory

  5. Select main.jsbundle and the assets folder (if it exists)

  6. Important checkboxes:

    • “Copy items if needed” must be checked
    • ✅ Your app target must be selected under “Add to targets”
  7. Click “Add”

Verify: Click main.jsbundle in Project Navigator and check the File Inspector (right panel) to confirm your target is checked under “Target Membership”.

Step 5: Configure JavaScript Loading

Update your AppDelegate to load the bundled JavaScript instead of Metro.

For Objective-C (AppDelegate.m)

Find the jsCodeLocation configuration in the application:didFinishLaunchingWithOptions: method.

Replace the existing code with:

jsCodeLocation = [[NSBundle mainBundle] 
                  URLForResource:@"main" 
                  withExtension:@"jsbundle"];

If you see conditional DEBUG code like this, the Release build already uses the bundle:

#if DEBUG
  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] 
                    jsBundleURLForBundleRoot:@"index"];
#else
  jsCodeLocation = [[NSBundle mainBundle] 
                    URLForResource:@"main" 
                    withExtension:@"jsbundle"];
#endif

For Swift (AppDelegate.swift)

Replace the existing code with:

let jsCodeLocation = Bundle.main.url(forResource: "main", 
                                     withExtension: "jsbundle")

If conditional DEBUG code is present, the Release build already uses the bundle:

#if DEBUG
  let jsCodeLocation = RCTBundleURLProvider.sharedSettings()
    .jsBundleURL(forBundleRoot: "index")
#else
  let jsCodeLocation = Bundle.main.url(forResource: "main", 
                                       withExtension: "jsbundle")
#endif

Step 6: Build the App

  1. Select your target simulator in Xcode (latest iOS version recommended)

  2. Choose your build scheme:

    • Debug — Larger file size, includes diagnostics
    • Release — Optimized build (recommended for testing)
  3. Navigate to ProductClean Build Folder (⇧⌘K)

  4. Navigate to ProductBuild (⌘B)

  5. Wait for the build to complete successfully

Step 7: Test Your Build Offline

This is a critical verification step:

  1. Disable network connectivity (turn off WiFi)
  2. Close all terminal windows (ensure Metro isn’t running)
  3. Open iOS Simulator
  4. Navigate to ProductShow Build Folder in Finder
  5. Go to:
    • Debug: Build/Products/Debug-iphonesimulator/
    • Release: Build/Products/Release-iphonesimulator/
  6. Drag the .app file into the simulator to install it
  7. Launch the app and verify it starts without errors
  8. Test core functionality to confirm everything works
  9. Re-enable network
If the app works offline, your bundle is correctly configured and ready for upload!

Step 8: Package for Upload

  1. Right-click the .app file in Finder
  2. Select “Compress” to create a .zip archive
  3. Upload to Pie’s upload portal

Step 9: Restore Development Settings (Optional)

If you modified AppDelegate.m or AppDelegate.swift, restore the original code to continue local development with Metro bundler.

Automated Bundling Method

If your project has the “Bundle React Native code and images” build phase configured:

Quick Steps

  1. Open your project:

    open ios/YourProjectName.xcworkspace
  2. Select your app target → Build Settings

  3. Search for “Build Configuration” and set it to “Release”

  4. Navigate to ProductClean Build Folder (⇧⌘K)

  5. Navigate to ProductBuild (⌘B)

  6. The JavaScript bundle is created automatically during build

  7. Locate your .app file in ProductShow Build Folder in Finder

  8. Test offline, compress to .zip, and upload

Advantage: No manual bundling or code modifications needed. The Release scheme handles everything automatically.

Troubleshooting

Blank or White Screen

Possible Causes:

  • Bundle not found in app bundle
  • Incorrect entry file specified
  • Hermes compatibility issues

Solutions:

  • Verify main.jsbundle has Target Membership checked in Xcode
  • Confirm entry file name (check package.json for custom entry)
  • If using Hermes, ensure all dependencies are compatible

App Tries to Connect to Metro (localhost:8081)

Possible Causes:

  • AppDelegate still in development mode
  • Debug scheme selected instead of Release
  • Cached build artifacts

Solutions:

  • Verify AppDelegate changes were saved
  • Switch to Release scheme or update AppDelegate code
  • Clean Build Folder and rebuild: ProductClean Build Folder
  • Search codebase for hardcoded localhost:8081 references

Assets Not Loading (Missing Images/Fonts)

Possible Causes:

  • Assets folder not added to Xcode
  • Assets not included in target
  • Incorrect asset paths in code

Solutions:

  • Verify assets folder was added with “Copy items if needed”
  • Check Target Membership in File Inspector
  • Confirm code uses correct relative paths for assets

Native Modules Failing

Possible Causes:

  • CocoaPods not installed
  • Static linking issues
  • New architecture compatibility (RN 0.68+)

Solutions:

  • Run cd ios && pod install && cd ..
  • Check module documentation for static build requirements
  • Verify module compatibility with React Native architecture

Build Errors in Xcode

Possible Causes:

  • Hermes configuration issues
  • Missing bundle file
  • Duplicate symbols

Solutions:

  • Verify hermes_enabled in ios/Podfile
  • Confirm ios/main.jsbundle exists
  • Delete DerivedData: rm -rf ~/Library/Developer/Xcode/DerivedData

Hermes Engine Considerations

If your project uses Hermes (default in React Native 0.70+):

  • ✅ Bundling process remains the same
  • ✅ Generates more efficient bundles with better performance
  • ✅ Smaller file sizes
  • ✅ Check ios/Podfile for hermes_enabled = true
  • ⚠️ Ensure all dependencies are Hermes-compatible

Build Scheme Comparison

FeatureReleaseDebug
Bundle SizeSmallerLarger
PerformanceOptimizedUnoptimized
Debug SymbolsNoYes
Recommended ForPie TestingIssue Diagnosis

Recommendation: Use Release builds for Pie testing as they represent production-like behavior.

Additional Resources

Next Steps

  1. Upload your build to Pie’s upload portal
  2. Review Post-Upload Requirements
  3. Pie’s AI agents will begin testing within 30 minutes

Need Help?

Contact Pie Labs support with:

  • React Native version
  • Build errors or console logs
  • Screenshots of relevant issues
  • Description of what you’ve tried