You've built your app. Maybe it's Flutter or React Native and the Android version is already on Google Play. Maybe it's a small Swift project you wrote in VS Code. Then you hit the wall every Windows developer hits sooner or later:
Xcode only runs on macOS.
You need Xcode (or at least Apple's build tools) to compile an iOS app, sign it and upload it to App Store Connect. There's no official Windows version, and there isn't going to be one.
So what are your options if you don't own a Mac? Here's an honest overview, followed by a full walkthrough of the approach I use.
Disclosure: I build JustMac, one of the services mentioned below. I've tried to describe every option fairly, including the ones that are free.
Option 1: Let a CI service build it for you
If your project is Flutter or React Native, you may not need a Mac desktop at all:
- Expo EAS Build builds iOS apps in the cloud for Expo / React Native projects, and EAS Submit can upload the result to App Store Connect.
- Codemagic does the same for Flutter (and other frameworks).
- GitHub Actions has macOS runners. They're free for public repositories and billed per minute for private ones.
Good for: repeatable builds once everything works.
Not great for: the first setup. When signing fails, a CocoaPods dependency breaks or you need to see your app in the iOS Simulator, debugging through CI logs is slow and painful.
Option 2: Buy a (used) Mac mini
A second-hand Apple silicon Mac mini is the most capable long-term option. If you'll ship iOS apps every week, it's probably worth it.
Good for: daily iOS development.
Not great for: "I need to ship one build this week." It's an upfront cost, and you need a desk, a screen and time to set it up.
Option 3: Rent a remote Mac
You rent a real Mac in a data centre and use its desktop remotely. There are several providers: MacinCloud, MacStadium, AWS EC2 Mac instances, and smaller services like JustMac (mine). They differ mainly in:
- How you connect: a VNC / remote desktop client, or simply a browser tab.
- Minimum rental period: Apple's macOS licence requires a minimum lease of 24 hours, so you'll usually see daily, weekly or monthly plans.
- Dedicated or shared: a whole physical Mac versus a user account on a shared machine.
Good for: occasional builds, first setup, debugging signing problems, testing in the Simulator.
Not great for: heavy daily use, where buying becomes cheaper.
⚠️ What about a "Hackintosh" or a macOS virtual machine on your PC? Apple's licence only allows macOS on Apple hardware, and these setups break regularly with Xcode updates. I wouldn't build anything you plan to publish on them.
Walkthrough: from Windows to TestFlight with a rented Mac
Here's the full process. I'll use JustMac in the screenshots, but the steps on the Mac are the same on any remote Mac, or on a Mac you own.
What you need first
- An Apple Account (free).
- An Apple Developer Program membership (US$99/year). You don't need it to run your app in the Simulator, but you do need it to upload to TestFlight or the App Store.
- Your project in a Git repository, or as an archive you can upload.
Step 1: Get a Mac and open it
Pick a machine, pay for a day, and open the desktop. With JustMac it opens in a browser tab, so there's nothing to install on Windows.
Step 2: Install Xcode
Open the App Store on the Mac, sign in with your own Apple Account and install Xcode. It's a big download, so start this first and grab a coffee.
Then open Xcode once and let it install its additional components (including the iOS Simulator runtime).
Step 3: Bring your project over
The easiest way is Git. Open Terminal and run:
git clone https://github.com/you/your-app.git
cd your-app
No Git repository? Upload an archive instead. In JustMac you can drag a file onto the Mac window.
For Flutter, install the toolchain with Homebrew first:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install --cask flutter
brew install cocoapods
flutter pub get
For React Native:
brew install node cocoapods
npm install
cd ios && pod install && cd ..
Step 4: Run it in the Simulator
Before worrying about signing, make sure the app actually builds and runs:
-
Native Swift: open the
.xcodeproj/.xcworkspace. In the toolbar at the top of Xcode, click the device name next to your app's name and pick an iPhone from the iOS Simulators section. Then press Run (⌘R). -
Flutter:
open -a Simulator, thenflutter run. -
React Native:
npx react-native run-ios(ornpx expo run:ioswith Expo).
This is where most "it works on Android" projects find their iOS-only problems: missing permissions in Info.plist, CocoaPods version conflicts, or a minimum iOS version that's too low for a dependency. Fix them now, while you can see what's happening.
Step 5: Set up signing
- In Xcode, go to Settings → Accounts and add your Apple Account.
- Select your project, then the app target, then the Signing & Capabilities tab.
- Tick Automatically manage signing and choose your Team.
- Make sure the Bundle Identifier is unique (for example
com.yourname.yourapp).
Xcode creates the certificate and provisioning profile for you.
Step 6: Create the app in App Store Connect
In a browser, open App Store Connect, go to Apps → + → New App, and choose the same Bundle ID you used in Xcode. You only do this once per app.
Step 7: Archive and upload
- Look at the toolbar at the top of the Xcode window. In the middle you'll see your app's name followed by a device, for example MyApp › iPhone 16. Click the device name (iPhone 16) and choose Any iOS Device (arm64) from the list. This tells Xcode to build for real iPhones instead of the Simulator.
- Choose Product → Archive. (If Archive is greyed out, a simulator is still selected: go back to step 1.)
- When the Organizer window opens, select the archive and click Distribute App → App Store Connect → Upload.
For Flutter you can build the archive from the terminal with flutter build ipa, then upload it from the Organizer or with Apple's Transporter app.
Step 8: TestFlight and review
After a few minutes of processing, the build appears in App Store Connect under TestFlight. Add yourself as a tester, install the TestFlight app on an iPhone and try the real thing. When you're happy, attach the build to a version and submit it for App Store review.
Before your rental ends
- Push your code (including any changes Xcode made to project files) back to Git.
- Sign out of your Apple Account in Xcode and the App Store if you want to be extra careful. On JustMac the Mac is wiped to factory settings an hour after the rental ends anyway.
Which option should you choose?
| Situation | Best option |
|---|---|
| Expo / React Native project that already builds | Expo EAS Build |
| Flutter project, builds already work | Codemagic or GitHub Actions |
| First iOS build, signing problems, need the Simulator | Rent a Mac for a day or a week |
| You'll do iOS development every week | Buy a Mac mini |
My personal rule: rent first, automate later. Getting the first build through signing and App Store Connect is much easier when you can see a real Xcode window. Once it works, moving the build to CI is straightforward.
If you want to try the browser-based approach, JustMac rents dedicated Mac minis and MacBook Airs by the day, week or month, and the desktop opens in a browser tab. Questions about any of the steps above? Ask in the comments and I'll help.





Top comments (0)