← Blog

How to test an iOS app without a Mac

Run compile checks and the iOS Simulator on a cloud Mac, then test on a real iPhone with TestFlight, with a practical test matrix and faster feedback loop.

· 8 min read

Need a Mac for this? NoMac gives your agent full macOS with Xcode for $0.80 an hour, billed per minute. Start now →


The most reliable way to test a native iOS app without owning a Mac is to compile it on a cloud Mac and install the signed beta on a real iPhone through TestFlight. Use local tests on Windows or Linux for portable logic, and use the phone for the interface and Apple-specific behavior.

You do not get Apple's iOS Simulator on Windows or Linux. The cloud Mac runs it instead: your agent can boot a simulator there, install the build, run UI tests, and take screenshots. TestFlight on the phone covers what a simulator cannot.

A successful cloud compile proves that Xcode accepted the project. A successful TestFlight session proves that a human could install and use that build. You need both kinds of evidence.

Your no-Mac testing options

MethodWhat it provesWhat it cannot prove
Portable unit testsBusiness logic on Windows or LinuxSwiftUI/UIKit and iOS integration
Framework preview or Android buildShared product behaviorNative iOS binary and Apple conventions
Remote iOS SimulatoriOS UI across simulated devicesAll hardware and real-device performance
Cloud compile checkClean Xcode compilationUsability and runtime behavior
TestFlight on iPhoneSigned install and real-device behaviorDebugger-level internal state

Apple's testing documentation says simulators run on a Mac and do not reproduce every physical-device feature or performance characteristic. Apple recommends testing on physical devices as well.

Set up the TestFlight loop once

  • ·Join the Apple Developer Program.
  • ·Create the App Store Connect app record with the project's exact bundle ID.
  • ·Connect an App Store Connect team key to NoMac once, at nomac.app/apple.
  • ·Add your Apple account as an internal tester.
  • ·Install Apple's TestFlight app on the iPhone.
  • ·Authenticate NoMac where the project and coding agent run.
npm install -g @nomac/cli
nomac login

Internal testing is the fastest development route. External testers can reach a much larger audience, but the first external build may need Beta App Review and requires beta information. Apple's TestFlight overview explains the current internal and external paths.

Use three remote checks

Compile check: compiler feedback

Start a Mac, sync the current source to ~/drive/src, and build the iOS target for the Simulator without signing. This needs no Apple account. It should be the first remote step after local tests. The drive is kept between sessions, so later builds are incremental.

Simulator: interface feedback

xcodebuild test runs unit and UI tests on a simulator. xcrun simctl boots a device, installs the app, and saves screenshots the agent can look at. The first boot on a Mac takes about 90 seconds. See running the iOS Simulator in the cloud.

Signed build: device feedback

Once the compile check passes and the feature is coherent, the agent runs set_up_signing, archives and exports with the commands it returns, and sends the .ipa with send_to_testflight. Apple processes it, usually in 5 to 15 minutes, before TestFlight can install it, so build success, upload success, processing, and ready-to-test are separate states.

Run all local tests. Start a NoMac Mac, sync the current project, and build
it for the Simulator. Fix evidence-backed compiler or project errors. After it
passes, ask me before sending a signed build. Follow send_to_testflight until
the build is on TestFlight and report the build number.

A real-device test matrix

Installation and first launch

  • ·Install or update from TestFlight and confirm the expected build number.
  • ·Launch with no existing app data.
  • ·Check the launch screen, empty state, and first useful action.
  • ·Confirm the app does not depend on a developer's private network.

The main user journey

  • ·Complete the job the store description promises.
  • ·Try minimum, maximum, empty, and invalid values.
  • ·Cancel halfway and verify the app returns to a sensible state.
  • ·Save data, kill the app, reopen it, and confirm the state persists correctly.

iPhone behavior

  • ·Show and dismiss the keyboard on every form.
  • ·Deny each permission once; the app must explain recovery without trapping the user.
  • ·Send the app to the background and return during an active task.
  • ·Switch between Wi-Fi, mobile data, airplane mode, and restored connectivity.
  • ·Test camera, photos, location, notifications, microphone, or Bluetooth when used.
  • ·Rotate the device where orientation is supported.

Accessibility and appearance

  • ·Enable a large Dynamic Type size and confirm important controls remain reachable.
  • ·Check dark and light appearance.
  • ·Use VoiceOver to confirm controls have useful names and reading order.
  • ·Enable increased contrast or reduced motion when the design depends on them.
  • ·Check text truncation and safe areas on the smallest supported screen available.

Failure and recovery

  • ·Use an expired session or wrong password when the app has authentication.
  • ·Interrupt an upload or request.
  • ·Open a deep link before the app is running.
  • ·Trigger empty API results and server errors safely in a test environment.
  • ·Verify destructive actions require confirmation and can recover where promised.

Give the coding agent useful evidence

A report should identify the build, environment, action, actual result, and expected result. Attach the original screenshot and mark the relevant area when the issue is visual.

Build: 24
Device: iPhone, current public iOS release
Screen: Edit Recipe
Steps: Open an existing recipe, clear Water, tap Save
Actual: The screen closes and stores 0
Expected: Stay on the form and show "Water must be greater than zero"
Frequency: 3/3
Attachment: edit-recipe-zero-water.png

Ask the agent to reproduce the state from code, add a regression test for logic, make the smallest fix, run a compile check on the Mac, and send another TestFlight build only after it passes.

Manage build numbers and feedback

Never report “the latest build” when multiple releases are processing. Name the TestFlight build number. Keep one short test note per build: intended change, known issue, device tested, and result.

TestFlight can collect screenshots, comments, and crash details from testers. Use get_feedback to bring available feedback back into the agent loop, but keep reproduction steps in the issue or commit history so the fix remains understandable later.

When to work on the Mac directly

TestFlight is slower than a simulator for pixel-by-pixel design. Work on the cloud Mac itself when you need rapid SwiftUI changes, debugger breakpoints, Instruments, UI test authoring, simulator-only states, or a large matrix of device sizes. open_mac_desktop (CLI: nomac desktop) shows you its desktop, with Xcode and the Simulator, in your browser.

A productive hybrid is a simulator check on the cloud Mac for each change and a TestFlight build for each release candidate. The XcodeBuildMCP and NoMac comparison explains the local and distribution loops.

Before App Store review

Test the exact build you will submit, not a later local change. Then run review_lint on the App Store record, confirm screenshots and metadata match the build, provide working review credentials, and answer privacy and compliance questions. External TestFlight review is not a substitute for full App Review.

Continue with the easiest way to publish to the App Store in 2026. Without a Mac, the real iPhone is not a compromise: it is the most important test surface you have.

Give your agent a Mac in a minute.

Full macOS with Xcode and root, over MCP, CLI, API or SSH. $0.80 an hour, billed per minute. The Mac is deleted at stop; your 20 GB drive is kept for the next one. No Apple account needed to start.

Get started from $10

7-day money-back guarantee on your first purchase. Credit never expires.

Connect your agent →