Guide

Build a React Native iOS app without a Mac

Most React Native work runs fine on Windows or Linux. The iOS build does not. CocoaPods and xcodebuild need macOS, so you borrow a Mac for that one step. Below is a GitHub Actions workflow that does it. We ran it on October 2, 2026, and it built React Native 0.87.1 with Xcode 26.6.

What works on Windows and Linux

Your JavaScript and TypeScript code, Metro, and Jest all run on any desktop. In our test, a freshly created app passed its Jest test on GitHub's ubuntu-24.04 runner and on its windows-2025 runner. Both used Node 22. You can also build and run the Android app on either system. The React Native docs walk through Android setup for Windows and for Linux.

For iOS, the same docs are blunt. On the Windows and Linux setup pages they say a Mac is required to build projects with native code for iOS. Their suggestion is Expo Go, which runs your JavaScript on a real iPhone during development. That covers screens and logic. It does not produce an app you can ship.

What needs macOS

  • pod install. The iOS project pulls React Native and its native dependencies through CocoaPods. Codegen runs during this step too.
  • xcodebuild. Xcode and the iOS SDK only exist for macOS. Apple lists macOS Tahoe 26.2 or later for Xcode 26.6 on its Xcode system requirements page.
  • The Simulator. It ships inside Xcode, so it is Mac only too.
  • Signing and upload. That part is covered in publishing an iOS app without a Mac.

The workflow we ran

It creates a new app with the command from reactnative.dev. It runs the tests on Linux and Windows. Then it builds iOS on a macos-26 runner. The init flags come from the React Native CLI docs. They pick npm, skip the pod prompt, and skip git setup. Put it at .github/workflows/rn-ios.yml and start it from the Actions tab.

name: rn-ios
on: workflow_dispatch
jobs:
  test-linux:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v7
        with:
          node-version: 22
      - run: npx @react-native-community/cli@latest init RNDemo --pm npm --install-pods false --skip-git-init true
      - run: npm test
        working-directory: RNDemo
  test-windows:
    runs-on: windows-latest
    steps:
      - uses: actions/setup-node@v7
        with:
          node-version: 22
      - run: npx @react-native-community/cli@latest init RNDemo --pm npm --install-pods false --skip-git-init true
      - run: npm test
        working-directory: RNDemo
  build-ios:
    runs-on: macos-26
    steps:
      - uses: actions/setup-node@v7
        with:
          node-version: 22
      - run: sudo xcode-select -s /Applications/Xcode_26.6.app
      - run: xcodebuild -version && node -v && ruby -v
      - run: npx @react-native-community/cli@latest init RNDemo --pm npm --install-pods false --skip-git-init true
      - run: node -p "require('react-native/package.json').version"
        working-directory: RNDemo
      - run: bundle install
        working-directory: RNDemo/ios
      - run: bundle exec pod install
        working-directory: RNDemo/ios
      - name: Build for the iOS Simulator (Debug)
        run: |
          xcodebuild -workspace RNDemo.xcworkspace -scheme RNDemo \
            -configuration Debug -sdk iphonesimulator \
            -destination 'generic/platform=iOS Simulator' \
            -derivedDataPath build build
        working-directory: RNDemo/ios
      - name: Build for the iOS Simulator (Release, JS bundled)
        run: |
          xcodebuild -workspace RNDemo.xcworkspace -scheme RNDemo \
            -configuration Release -sdk iphonesimulator \
            -destination 'generic/platform=iOS Simulator' \
            -derivedDataPath build build
        working-directory: RNDemo/ios
      - run: ls -la build/Build/Products/Release-iphonesimulator/RNDemo.app/main.jsbundle && du -sh build/Build/Products/*-iphonesimulator/RNDemo.app
        working-directory: RNDemo/ios

In your own repo, replace the init step with a checkout of your code and an install of its packages. The rest stays the same. Change RNDemo to your workspace and scheme name.

What the run printed

These lines are copied from the build-ios job log. The whole macOS job took 2 minutes 42 seconds.

Xcode 26.6
Build version 17F113
v22.23.2
0.87.1
Pod install took 43 [s] to run
Pod installation complete! There are 87 dependencies from the Podfile and 86 total pods installed.
** BUILD SUCCEEDED **
Welcome to Metro v0.87.1
** BUILD SUCCEEDED **
-rw-r--r--  1 runner  staff  1268567 Oct  2 04:02 build/Build/Products/Release-iphonesimulator/RNDemo.app/main.jsbundle
154M	build/Build/Products/Debug-iphonesimulator/RNDemo.app
 38M	build/Build/Products/Release-iphonesimulator/RNDemo.app

The Debug build took about 39 seconds and the Release build about 35. That is fast because React Native 0.87 downloads a prebuilt core. The pod install log said so plainly. Older versions compile far more native code, so expect longer builds there.

Why build twice? A Debug app loads its JavaScript from Metro while it runs. The Release build packs the JavaScript into the app as main.jsbundle, here 1.27 MB. If the Release build passes, your bundle and your native code both compile.

Run it on every pull request

The workflow above starts by hand. To catch iOS breakage before it merges, change the trigger line so it runs on pull requests too:

on:
  pull_request:
  workflow_dispatch:

When a build fails, the log is long. Ours ran past 18,000 lines for two small builds. Search the log for "error:" and read the first match. Later errors are often side effects of the first one. If pod install fails, look at the Podfile and your native dependencies first.

What it costs

GitHub bills each job rounded up to the minute. Our macOS job came to 3 minutes. At $0.062 a minute, the rate in GitHub's runner pricing reference in October 2026, that is about 19 cents. Public repositories pay nothing for standard runners. For a few builds a week, this is the cheapest way to get an iOS build.

On Expo, there is another route. EAS Build compiles iOS in Expo's cloud. The Free plan includes 15 iOS builds a month in a low-priority queue. After that a medium iOS build costs $2, per expo.dev/pricing in October 2026.

Where CI stops being enough

A CI job proves the app compiles. It cannot show you the app. A simulator build only runs in the iOS Simulator, which needs a Mac screen. When a native module breaks on iOS only, you want Xcode open. You want breakpoints and a Simulator you can click. For that, see using Xcode from Windows with a remote Mac.

The hosted runner also starts empty every time. Each run downloads Node packages, gems and pods again. In our run the macOS job also carried a GitHub notice. It said macOS arm64 jobs may queue longer due to capacity constraints.

Who should not rent a Mac for this

If you build a few times a week, stay on hosted runners or EAS. A dedicated Mac pays off at around 2,242 macOS minutes a month at GitHub's base rate. It also pays off when you need the screen. If neither is true, a rented Mac is money you do not need to spend.

If you do cross that line, the same workflow runs on a self-hosted macOS runner. Change runs-on and point xcode-select at your Xcode. Pods and derived data stay warm between runs. See pricing for the M6 plans.

Frequently asked questions

Can I build a React Native iOS app on Windows?

+

You can write and test it on Windows, but the iOS build needs macOS. The React Native docs say a Mac is required to build projects with native code for iOS. A cloud Mac or a CI runner covers that step.

Can I run the iOS Simulator on Windows?

+

No. The Simulator ships with Xcode, which only runs on macOS. You can view a remote Mac's Simulator through a remote desktop, or use Expo Go on a real iPhone during development.

How long does a React Native iOS build take on GitHub Actions?

+

In our October 2026 run, a new React Native 0.87.1 app took 2 minutes 42 seconds for the whole macOS job. That included pod install and both a Debug and a Release simulator build. Larger apps take longer.

Do I need an Apple Developer account to build for the Simulator?

+

No. Our workflow built for the Simulator without any signing. You need the paid Apple Developer Program to sign the app for a device, TestFlight or the App Store.

Related guides