DEV Community

Cover image for How to Build an iOS App on Windows in 2026. Let Xcode Cloud Be Your Mac
Shovan
Shovan

Posted on Edited on

How to Build an iOS App on Windows in 2026. Let Xcode Cloud Be Your Mac

A practical Windows-first iOS workflow using GitHub, Vercel, Neon, Capacitor, App Store Connect CLI, Xcode Cloud and TestFlight. We tried and tested this and below is the approach. -Shovan, SCube

Can you build an iOS app on Windows? Yes, with an important qualification.

You can do most application development, testing, backend work, infrastructure management, App Store Connect configuration and release orchestration from Windows.

What you cannot move to Windows is Apple's native iOS compilation and signing toolchain.

The solution is to move that part somewhere else.

Instead of making every developer use a Mac, use Xcode Cloud as the Mac that builds the application.

The result looks like this:

Windows developer
       │
       │ Git / CLI
       ▼
     GitHub
       │
       ├──────────────► Vercel Preview
       │                       │
       │                       ▼
       │                      Neon
       │
       ▼
   Xcode Cloud
 Apple-hosted macOS
       │
       ▼
    TestFlight
       │
       ▼
      iPhone
Enter fullscreen mode Exit fullscreen mode

There is still one Mac-specific step: the first Xcode Cloud workflow must be initialised using Xcode.

After that, the normal developer workflow can be Windows-first.

This guide shows the complete setup.


Table of contents

  1. What this architecture actually solves
  2. The tools we will use
  3. Install the CLI stack on Windows
  4. Create an isolated Git development branch
  5. Build the application normally on Windows
  6. Create a preview environment with Vercel
  7. Give the feature its own Neon database
  8. Connect the preview deployment to the test database
  9. Create the Apple resources from the command line
  10. Prepare the iOS project for cloud builds
  11. The one-time Mac handoff
  12. Configure Xcode Cloud and TestFlight
  13. Manage iOS delivery from the CLI after bootstrap
  14. The daily Windows developer workflow
  15. Security and isolation rules
  16. Common problems
  17. FAQ

1. What this architecture actually solves

The usual assumption is:

iOS development requires every developer to own and work from a Mac.

That is no longer the most useful way to think about the problem.

There are really several different jobs hidden inside "building an iOS app":

Job Needs macOS?
Write React/TypeScript/web code No
Build APIs No
Run automated web tests No
Work with Git/GitHub No
Configure Vercel No
Create/manage Postgres environments No
Manage many App Store Connect resources No
Create Preview deployments No
Native iOS compilation Yes
iOS code signing Yes
Local iOS Simulator Yes
Native debugging in Xcode Yes
TestFlight installation on an iPhone No developer Mac required

The goal is therefore not Mac-free iOS development.

A better description is:

Windows-first iOS development with macOS compilation delegated to Xcode Cloud.

That distinction matters.

We are not trying to work around Apple's platform.

We are trying to stop Apple's platform from determining which computer every application developer must use.


2. The tools we will use

This guide uses:

Layer Tool
Source control Git
GitHub operations GitHub CLI (gh)
Web/API deployment Vercel CLI
Database Neon CLI
Web-to-native runtime Capacitor
Apple account automation App Store Connect CLI (asc)
Native build Xcode Cloud
Beta distribution TestFlight

Vercel and Neon are optional.

If your app uses another hosting platform or database, substitute those layers.

The core pattern is:

Git
  ↓
Cloud build
  ↓
Apple signing
  ↓
TestFlight
Enter fullscreen mode Exit fullscreen mode

The Vercel + Neon combination simply gives us a particularly clean way to create isolated Preview infrastructure for each feature branch.

A note about App Store Connect CLI

In this guide, asc refers to the open-source App Store Connect CLI project:

https://github.com/rorkai/App-Store-Connect-CLI

It is a community-maintained tool, not Apple's official CLI.

It provides a command-line interface for many App Store Connect, TestFlight, signing and Xcode Cloud operations.


3. Install the CLI stack on Windows

Start with PowerShell or Windows Terminal.

Git

If you do not already have Git:

winget install --id Git.Git -e
Enter fullscreen mode Exit fullscreen mode

Check:

git --version
Enter fullscreen mode Exit fullscreen mode

GitHub CLI

winget install --id GitHub.cli -e
Enter fullscreen mode Exit fullscreen mode

Authenticate:

gh auth login
Enter fullscreen mode Exit fullscreen mode

Verify:

gh auth status
Enter fullscreen mode Exit fullscreen mode

You can now create PRs, inspect CI, work with issues and call GitHub APIs without opening the browser.

For example:

gh pr status
gh pr list
gh repo view
Enter fullscreen mode Exit fullscreen mode

Vercel CLI

Assuming Node.js is installed:

npm install -g vercel
Enter fullscreen mode Exit fullscreen mode

Authenticate:

vercel login
Enter fullscreen mode Exit fullscreen mode

Verify:

vercel whoami
Enter fullscreen mode Exit fullscreen mode

Neon CLI

npm install -g neonctl
Enter fullscreen mode Exit fullscreen mode

Authenticate:

neonctl auth
Enter fullscreen mode Exit fullscreen mode

Check your projects:

neonctl projects list
Enter fullscreen mode Exit fullscreen mode

App Store Connect CLI

Check whether the WinGet package is currently available:

winget search asc
Enter fullscreen mode Exit fullscreen mode

If Rorkai.ASC is listed:

winget install --id Rorkai.ASC --exact
Enter fullscreen mode Exit fullscreen mode

Otherwise, use the signed Windows release binary from the project's GitHub Releases page.

Verify:

asc version
asc --help
Enter fullscreen mode Exit fullscreen mode

At this point the Windows machine has a command-line interface to almost every platform involved in delivery.


4. Create an isolated Git development branch

Do not start by modifying your release branch.

git switch main
git pull
git switch -c feature/new-ios-capability
Enter fullscreen mode Exit fullscreen mode

Push it:

git push -u origin feature/new-ios-capability
Enter fullscreen mode Exit fullscreen mode

If you or your coding agents regularly work on several features concurrently, use Git worktrees.

git worktree add ../new-ios-capability feature/new-ios-capability
Enter fullscreen mode Exit fullscreen mode

Inspect them:

git worktree list
Enter fullscreen mode Exit fullscreen mode

A worktree gives each feature or agent an independent checkout while keeping everything connected to the same Git repository.

That is especially useful for agent-assisted development:

Agent A → worktree A → feature A

Agent B → worktree B → feature B

Developer → main checkout
Enter fullscreen mode Exit fullscreen mode

There is much less chance of two processes rewriting the same working directory.


5. Build the application normally on Windows

This example assumes a web-first application wrapped for iOS using Capacitor.

A typical stack might be:

React / Vue / Svelte
        │
        ▼
     Web build
        │
        ▼
     Capacitor
        │
        ▼
      iOS app
Enter fullscreen mode Exit fullscreen mode

Install dependencies:

npm install
Enter fullscreen mode Exit fullscreen mode

Run locally:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Run tests:

npm test
Enter fullscreen mode Exit fullscreen mode

Build:

npm run build
Enter fullscreen mode Exit fullscreen mode

If Capacitor is not already installed:

npm install @capacitor/core @capacitor/cli
npm install @capacitor/ios
Enter fullscreen mode Exit fullscreen mode

Initialise it:

npx cap init
Enter fullscreen mode Exit fullscreen mode

Add iOS:

npx cap add ios
Enter fullscreen mode Exit fullscreen mode

After building your web app, copy the bundle into the native project:

npm run build
npx cap copy ios
Enter fullscreen mode Exit fullscreen mode

The native iOS project should live in Git.

Do not treat the Mac developer's laptop as the only place where the native project exists.

Your repository should contain everything Xcode Cloud requires to reproduce the build.

If you use React Native, Flutter or a native iOS project instead of Capacitor, the exact build commands change, but the GitHub → Xcode Cloud → TestFlight architecture still applies.


6. Create a preview environment with Vercel

Link the local directory to your Vercel project:

vercel link
Enter fullscreen mode Exit fullscreen mode

If your GitHub repository is connected to Vercel, pushing a feature branch can automatically create a Preview deployment.

You can also deploy manually:

vercel
Enter fullscreen mode Exit fullscreen mode

Do not use:

vercel --prod
Enter fullscreen mode Exit fullscreen mode

for experimental feature work.

The goal is:

main
  ↓
Production

feature/new-ios-capability
  ↓
Preview
Enter fullscreen mode Exit fullscreen mode

Each feature branch should have a safe place to run without affecting your production application.

Verify:

vercel list
Enter fullscreen mode Exit fullscreen mode

7. Give the feature its own Neon database

Do not point experimental application code at your production database simply because it is convenient.

There are two useful isolation levels.

Option A: Neon database branch

For normal feature development, Neon branching works well.

Link the workspace once:

neonctl link
Enter fullscreen mode Exit fullscreen mode

Then create or select a database branch:

neonctl checkout feature-new-ios-capability
Enter fullscreen mode Exit fullscreen mode

Pull its environment:

neonctl env pull
Enter fullscreen mode Exit fullscreen mode

Now:

Git branch
feature/new-ios-capability

        ↕

Neon branch
feature-new-ios-capability
Enter fullscreen mode Exit fullscreen mode

This creates a useful branch-first development model.

Option B: completely separate Neon project

For high-risk experiments, protocol changes, migrations or release laboratories, stronger isolation may be appropriate.

Create a new project:

neonctl projects create
Enter fullscreen mode Exit fullscreen mode

This gives you a completely separate Postgres project and connection string.

A practical rule:

Normal feature
    → database branch

Risky migration / experimental app
    → separate database project
Enter fullscreen mode Exit fullscreen mode

Production data should never become the accidental test environment.


8. Connect the preview deployment to the test database

Now connect the feature-specific Vercel deployment to the feature-specific database.

For example:

vercel env add DATABASE_URL preview feature/new-ios-capability
Enter fullscreen mode Exit fullscreen mode

The CLI will prompt for the value.

For secrets, prefer the interactive prompt rather than putting the credential directly in the command because shell history is very good at remembering things you wish it had forgotten.

Verify the configuration:

vercel env ls preview feature/new-ios-capability
Enter fullscreen mode Exit fullscreen mode

Pull the exact Preview configuration locally if required:

vercel env pull .env.preview.local \
  --environment=preview \
  --git-branch=feature/new-ios-capability
Enter fullscreen mode Exit fullscreen mode

You now have:

feature/new-ios-capability
        │
        ├── Vercel Preview
        │
        └── isolated Neon database
Enter fullscreen mode Exit fullscreen mode

This principle is more important than the products being used:

Isolate code, deployment and data together.

A Git branch alone is not an isolated environment if it still writes to production infrastructure.


9. Create the Apple resources from the command line

This is where the workflow becomes considerably more interesting.

Many operations that traditionally require Developer Portal or App Store Connect browser sessions can now be orchestrated from the terminal.

Create an App Store Connect API key

In App Store Connect, create a Team API Key with the minimum role your workflow requires.

You will receive:

Key ID
Issuer ID
AuthKey_XXXXXXXXXX.p8
Enter fullscreen mode Exit fullscreen mode

The .p8 file is a private credential.

Never:

commit it
paste it into a ticket
paste it into an AI conversation
store it in your repository
Enter fullscreen mode Exit fullscreen mode

Authenticate asc:

asc auth login \
  --name "Company App Store Connect" \
  --key-id "<KEY_ID>" \
  --issuer-id "<ISSUER_ID>" \
  --private-key "<PATH_TO_AUTH_KEY.p8>" \
  --network
Enter fullscreen mode Exit fullscreen mode

Validate the setup:

asc auth status --validate
asc auth doctor
Enter fullscreen mode Exit fullscreen mode

You want the authentication check to succeed before creating anything.


Create the Bundle ID

Instead of opening Certificates, Identifiers & Profiles manually:

asc bundle-ids create \
  --identifier "com.example.product.lab" \
  --name "Product Lab" \
  --platform IOS
Enter fullscreen mode Exit fullscreen mode

Check:

asc bundle-ids list --output table
Enter fullscreen mode Exit fullscreen mode

Your new Bundle ID should appear.


Authenticate the Apple web session

Some operations are performed by asc through its Apple web-session workflow rather than the normal App Store Connect API.

Authenticate:

asc web auth login \
  --apple-id "developer@example.com"
Enter fullscreen mode Exit fullscreen mode

You may be prompted for password and two-factor authentication.

Check:

asc web auth status
Enter fullscreen mode Exit fullscreen mode

Create the App Store Connect app record

Now create the actual App Store Connect application:

asc web apps create \
  --name "Product Lab" \
  --bundle-id "com.example.product.lab" \
  --sku "product-lab-2026" \
  --primary-locale "en-GB" \
  --platform IOS \
  --apple-id "developer@example.com"
Enter fullscreen mode Exit fullscreen mode

Verify:

asc apps list \
  --bundle-id "com.example.product.lab" \
  --output table
Enter fullscreen mode Exit fullscreen mode

Record the numeric App Store Connect application ID returned by Apple.

For example:

App ID: <APP_STORE_CONNECT_APP_ID>
Enter fullscreen mode Exit fullscreen mode

We will use it later.

At this point we have created:

Git feature branch
        +
Preview deployment
        +
isolated database
        +
Apple Bundle ID
        +
App Store Connect application
Enter fullscreen mode Exit fullscreen mode

without making the developer work through several unrelated dashboards.


10. Prepare the iOS project for cloud builds

Xcode Cloud does not build whatever happens to be sitting on somebody's Mac.

It clones your repository into a clean build environment.

That means the Git repository must contain enough native configuration to reproduce the application.

At minimum, review:

ios project/workspace
shared Xcode scheme
Info.plist
entitlements
package/dependency locks
Capacitor configuration
CI scripts
Enter fullscreen mode Exit fullscreen mode

For Capacitor applications, a useful Xcode Cloud post-clone script might perform:

#!/bin/sh

set -e

npm ci
npm run build
npx cap copy ios
Enter fullscreen mode Exit fullscreen mode

The important idea is:

Xcode Cloud should rebuild the web assets from the same Git commit that it is compiling.

Do not rely on somebody remembering to manually copy the latest frontend build into Xcode.

If the script is created on Windows, remember that shell scripts in Git may also need their executable bit set:

git update-index --chmod=+x ci_scripts/ci_post_clone.sh
Enter fullscreen mode Exit fullscreen mode

Commit it:

git add .
git commit -m "Add Xcode Cloud build pipeline"
git push
Enter fullscreen mode Exit fullscreen mode

11. The one-time Mac handoff

Here is the remaining Apple boundary.

The first Xcode Cloud workflow must be configured using Xcode on a Mac.

This does not mean the Windows developer has to move their work to the Mac.

The Mac operator is simply bootstrapping Apple's cloud build system.

The Windows developer hands over:

Repository
Branch
Bundle ID
App Store Connect App ID
Scheme
required environment variables
Enter fullscreen mode Exit fullscreen mode

The Mac operator then:

  1. Pulls the same repository and branch.
  2. Opens the .xcodeproj or .xcworkspace.
  3. Signs into the correct Apple Developer team.
  4. Confirms the Bundle ID.
  5. Confirms the correct shared Xcode scheme.
  6. Opens Xcode Cloud setup.
  7. Selects the product/scheme.
  8. Grants Xcode Cloud access to the GitHub repository.
  9. Selects the correct branch.
  10. Starts the first build.

That is the one-time bootstrap.

The Windows developer does not need to hand over their application implementation.

Everything is already in Git.


12. Configure Xcode Cloud and TestFlight

For an internal development build, keep the workflow deliberately narrow.

For example:

Workflow
Internal Lab

Scheme
ProductLab

Branch
feature/new-ios-capability

Action
Archive iOS

Distribution
Internal TestFlight
Enter fullscreen mode Exit fullscreen mode

Avoid accidentally pointing an experimental workflow at your production branch.

If your application needs environment variables during its build, configure them on this workflow.

For example:

APP_ENV=lab

API_ORIGIN=https://<preview-deployment>

FEATURE_LAB=1
Enter fullscreen mode Exit fullscreen mode

The exact values depend on your application.

A good test-build configuration should fail closed.

For example:

Missing test API configuration
        ↓
disable sync / API functionality
Enter fullscreen mode Exit fullscreen mode

is safer than:

Missing test API configuration
        ↓
silently use production
Enter fullscreen mode Exit fullscreen mode

Once Xcode Cloud completes the archive, it can distribute the build directly through TestFlight.

You should not need to manually:

archive
export IPA
upload IPA
wait for processing
assign build
Enter fullscreen mode Exit fullscreen mode

for every change.


13. Manage iOS delivery from the CLI after bootstrap

Once the first Xcode Cloud product exists, return to the command line.

Inspect it:

asc apps ci-product view \
  --id "<APP_STORE_CONNECT_APP_ID>" \
  --output json \
  --pretty
Enter fullscreen mode Exit fullscreen mode

List workflows:

asc xcode-cloud workflows list \
  --app "<APP_STORE_CONNECT_APP_ID>" \
  --output table
Enter fullscreen mode Exit fullscreen mode

Trigger a build:

asc xcode-cloud run \
  --app "<APP_STORE_CONNECT_APP_ID>" \
  --workflow "Internal Lab" \
  --branch "feature/new-ios-capability" \
  --wait
Enter fullscreen mode Exit fullscreen mode

Inspect the result:

asc xcode-cloud build-runs list \
  --workflow-id "<WORKFLOW_ID>"
Enter fullscreen mode Exit fullscreen mode

This is where the Mac stops being the team's build machine.

Apple's cloud infrastructure becomes the build machine.


14. The daily Windows developer workflow

After the platform has been bootstrapped, an ordinary Windows developer's day becomes surprisingly conventional.

git pull

git switch feature/new-ios-capability

npm install

npm run dev

# write code

npm test

git add .
git commit -m "Implement capability"
git push
Enter fullscreen mode Exit fullscreen mode

The rest can happen automatically:

git push
   │
   ▼
 GitHub
   │
   ├─────────────► Vercel Preview
   │                    │
   │                    ▼
   │               isolated DB
   │
   ▼
Xcode Cloud
   │
   ├── install dependencies
   ├── build web application
   ├── copy native assets
   ├── compile iOS application
   ├── sign
   └── archive
          │
          ▼
      TestFlight
          │
          ▼
        iPhone
Enter fullscreen mode Exit fullscreen mode

The developer tests the actual iOS application through TestFlight.

A local Mac is no longer part of every feature cycle.


15. Security and isolation rules

The tooling is useful, but the architecture matters more than the commands.

These are the rules I would keep.

1. Never make production the default fallback

Bad:

TEST_API_URL missing
        ↓
use PROD_API_URL
Enter fullscreen mode Exit fullscreen mode

Better:

TEST_API_URL missing
        ↓
fail build or disable network functionality
Enter fullscreen mode Exit fullscreen mode

2. Isolate more than Git

A real test environment may need:

separate Git branch
separate deployment
separate database
separate Bundle ID
separate iOS sandbox
separate TestFlight application
Enter fullscreen mode Exit fullscreen mode

A Git branch connected to the production database is not isolated.


3. Keep credentials outside the repository

Do not commit:

.p8 keys
database passwords
Vercel tokens
Apple passwords
API keys
Enter fullscreen mode Exit fullscreen mode

Use the relevant platform's secret/environment system.


4. Give CI the minimum required permissions

An automated build normally does not need unrestricted access to every production resource.

Use scoped credentials and separate test infrastructure where practical.


5. Keep native configuration in Git

Your Mac operator should not possess invisible project knowledge.

If a scheme, entitlement or CI script is required to build the application, it should normally be represented in source control.


6. Treat TestFlight as part of CI/CD

TestFlight should not be the final manual chore performed by one person.

It can be the output of the pipeline.


16. Common problems

no Xcode Cloud product found

If you run something similar to:

asc xcode-cloud workflows list \
  --app "<APP_ID>"
Enter fullscreen mode Exit fullscreen mode

and receive:

no Xcode Cloud product found
Enter fullscreen mode Exit fullscreen mode

before the first Xcode Cloud setup, that is expected.

Complete the one-time workflow setup using Xcode on a Mac and start the first build.

Then retry the CLI.


App works locally but fails in Xcode Cloud

Remember that Xcode Cloud starts with a clean environment.

Check that:

dependencies are installed
runtime versions are compatible
required configuration is committed
build scripts are committed
environment variables exist
dependency lockfiles exist
the Xcode scheme is shared
Enter fullscreen mode Exit fullscreen mode

If your build only works because of something installed manually on one developer's Mac, the CI build has exposed useful information.


Preview app connects to production

Stop.

Do not solve this by continuing to test carefully.

Fix the architecture.

Check:

branch-specific environment variables
API origin
database connection string
runtime environment detection
fallback behaviour
Enter fullscreen mode Exit fullscreen mode

Your experimental build should be structurally unable to use production by mistake.


TestFlight contains old frontend code

If you wrap a web application with Capacitor, make the cloud build rebuild the web application before Xcode compiles it.

For example:

npm ci
npm run build
npx cap copy ios
Enter fullscreen mode Exit fullscreen mode

Do not assume previously generated native assets are current.


17. FAQ

Can I build an iOS app on Windows?

Yes, most application development can be performed on Windows. Apple's iOS compiler, signing tools and Xcode still require macOS, so use a Mac or a macOS cloud build environment such as Xcode Cloud for the native build.

Do I need a Mac for iOS development?

You do not necessarily need a Mac for every developer or every development task. You still need access to macOS for the initial Xcode Cloud setup, native debugging, the iOS Simulator and some Xcode-specific configuration.

Can Xcode Cloud build an iOS app from GitHub?

Yes. Xcode Cloud connects to your Git repository, checks out the source code, resolves dependencies, executes configured scripts and runs Xcode build/archive actions in Apple's cloud environment.

What is Xcode Cloud?

Xcode Cloud is Apple's CI/CD platform for Apple-platform applications. It runs builds and tests in cloud-hosted macOS environments and integrates with Xcode, App Store Connect and TestFlight.

Can a Windows developer publish to TestFlight?

A Windows developer can drive much of the workflow from Git and CLI tools. Xcode Cloud performs the macOS build and signing, after which the build can be delivered to TestFlight without the Windows developer manually archiving it on a Mac.

Can Xcode Cloud run without my Mac being online?

Yes. Once the repository and workflow are configured, Apple's cloud infrastructure performs the build. Your local Mac is not acting as the build server.

Can I use Capacitor for iOS development on Windows?

Capacitor allows most of the shared web application to be developed with normal web tooling on Windows. Native iOS compilation and native debugging still require Apple's macOS/Xcode environment.

Does this work with React Native or Flutter?

The same delivery architecture can. The application-specific build commands are different, but GitHub, isolated cloud environments, Xcode Cloud and TestFlight can serve the same roles.

Is App Store Connect CLI an official Apple tool?

No. The asc tool used in this guide is an open-source third-party CLI for App Store Connect. Evaluate it like any other dependency before adopting it in a production release process.

Should every feature get its own database?

Not necessarily. Database branches are often sufficient for normal feature work. For high-risk experiments, migrations or security-sensitive testing, a completely separate database project can provide stronger isolation.


The bigger idea

The interesting part of this workflow is not any individual CLI.

GitHub CLI has existed for years.

Cloud databases have existed for years.

CI/CD has existed for years.

The change is that these systems can now be treated as one programmable development control plane.

Developer or coding agent
          │
          ├── Git
          ├── GitHub
          ├── preview infrastructure
          ├── database
          ├── Apple configuration
          ├── cloud build
          └── beta distribution
Enter fullscreen mode Exit fullscreen mode

A coding agent can operate the same commands a developer can.

That changes the role of the human.

Instead of:

open dashboard
click settings
copy value
open another dashboard
create environment
open another dashboard
upload build
wait
repeat
Enter fullscreen mode Exit fullscreen mode

the human can concentrate on:

architecture
constraints
security
review
device validation
release approval
Enter fullscreen mode Exit fullscreen mode

The workflow still works without AI.

Agents simply make the programmable interface much more valuable.


Final architecture

                  Windows developer

                        │
                   code + tests
                        │
                        ▼
                      GitHub
                        │
             ┌──────────┴───────────┐
             │                      │
             ▼                      ▼
        Vercel Preview          Xcode Cloud
             │                      │
             ▼                      │
      isolated database             │
                                    ▼
                              signed iOS build
                                    │
                                    ▼
                                TestFlight
                                    │
                                    ▼
                                  iPhone
Enter fullscreen mode Exit fullscreen mode

The Mac has not disappeared.

It has stopped being the centre of the development workflow.

And for teams where most developers use Windows, that is a much more important improvement.


[build ios app on windows] [xcode cloud]

Top comments (0)