Skip to content

ios --remote compiles arm64 and x86_64 for a one-arch simulator; iOS sim cache key has no arch #1803

Closed

Description

@janicduplessis

Problem

stim ios --remote proxy|eas compiles the iOS simulator app for both arm64 and x86_64, although the remote simulator runs only one of them. On apps/mobile that roughly doubles xcodebuild time, .app size and DerivedData size compared with a single-arch build.

Local stim ios Debug builds are not affected: they compile only the host arch.

Separately, the iOS simulator cache key carries no architecture, so artifacts built for different archs share one key, locally and in remote cache providers.

Evidence

Machine: arm64 MacBook, Xcode 27.0 (27A266a), stim origin/main 45eff4b, apps/mobile, 2026-09-28.

Arguments Stim passes to xcodebuild. xcodebuildArgs (packages/stim-cli/src/engine/xcode.ts:385-424) passes -sdk iphonesimulator -destination <destination || id=<udid>>. It never passes ARCHS, ONLY_ACTIVE_ARCH or -arch. The only extra build settings are the compilation-cache settings. Local runs pass id=<owned simulator udid>. --remote passes GENERIC_SIM_DESTINATION = 'generic/platform=iOS Simulator' (packages/stim-cli/src/commands/ios/artifact.ts:51-52, :769, set from remoteDestination: Boolean(remoteDevice) in commands/ios.ts:806).

Existing artifacts in ~/.stim/build-cache/ios/ (lipo -archs on Stim.app/Stim and Stim.app/Frameworks/ExpoCamera.framework/ExpoCamera):

Entries Main executable Framework .app size
9 x <fp>-debug-sim arm64 arm64 171-180 MB
11 x <fp>-release-sim x86_64 arm64 x86_64 arm64 130-138 MB

Resolved build settings (xcodebuild -workspace Stim.xcworkspace -scheme Stim -sdk iphonesimulator -showBuildSettings -json, target Stim):

Configuration Destination Extra settings ARCHS ONLY_ACTIVE_ARCH
Debug id=<udid> none arm64 YES
Debug generic none arm64 x86_64 NO
Debug generic ARCHS=arm64 ONLY_ACTIVE_ARCH=YES arm64 NO
Release id=<udid> none arm64 x86_64 NO
Release id=<udid> ARCHS=arm64 ONLY_ACTIVE_ARCH=YES arm64 YES
Release generic none arm64 x86_64 NO

A generic destination forces ONLY_ACTIVE_ARCH=NO even when it is passed on the command line; only an explicit ARCHS narrows it.

A/B build. The A/B ran in a fresh worktree after expo prebuild and pod install. Each run used a fresh -derivedDataPath, Debug configuration, the generic destination, and no compilation cache. It was run once on a heavily loaded machine (load average 135-380 from other agents), so treat the times as indicative.

Run xcodebuild .app DerivedData lipo -archs (exe, framework)
generic (what --remote does today) 285 s 341 MB 9.7 GB x86_64 arm64, x86_64 arm64
generic + ARCHS=arm64 ONLY_ACTIVE_ARCH=YES 151 s 172 MB 4.4 GB arm64, arm64

This agrees with the offload prototype (#1801) on the Mac mini: 5m07s, 341 MB, 9.9 GB fat vs 2m38s, 171 MB, 4.5 GB arm64 when targeting a concrete simulator.

Cause

--remote. It uses the generic destination because xcodebuild cannot target a remote simulator's UDID. Since #1212 the remote device is also connected only after the build (commands/ios.ts:791-795), so there is no UDID at build time. With a generic destination, Xcode sets ONLY_ACTIVE_ARCH=NO and builds every ARCHS value, which is arm64 x86_64 for the iphonesimulator SDK.

Local Debug. It is single-arch because id=<udid> gives Xcode an active arch and the project's Debug ONLY_ACTIVE_ARCH=YES applies.

Local Release. A local Release simulator build is fat because Release keeps the Xcode default ONLY_ACTIVE_ARCH=NO. #608 treats that as the project's intentional universal policy. It is noted here only for completeness. Narrowing it would override a project setting, which needs its own decision.

Remote simulator arch. Stim cannot learn it today:

  • proxy: nothing in engine/device-remote.ts or engine/agent-device.ts reads host information. agent-device devices --json (0.21.12) reports platform, id, name, kind, target and booted, but no CPU arch.
  • eas: Stim knows only the requested deviceType. I did not verify what CPU EAS Simulator hosts run, and did not start a billable session to check.

On a modern Mac the answer is almost always the host arch, since Apple silicon runs arm64 simulators and Rosetta simulators are opt-in. It is still not something Stim observes.

Cache key. buildCacheKey (packages/core/index.ts:168-179) adds an ABI component only for Android. Android uses -debug-sim-arm64-v8a (#394, #961). An iOS simulator key is <fp>-<configuration>-sim[-compiler][-profile][-scheme-...] with no arch, so these artifacts all share one key today:

  • a local arm64 Debug build;
  • an Intel Mac's x86_64-only local Debug build;
  • a fat --remote build.

The remote upload passes only { configuration } as run options (commands/ios/artifact.ts:824-833), so remote providers key the same way. Nothing validates the arch of a cached .app before install: there is no lipo or arch check in packages/stim-cli/src. Today an arm64-only Debug artifact uploaded by one machine can be a remote hit for an x86_64 Mac. That gap already exists independently of --remote, but making --remote single-arch would widen it: a --remote build could then store a single-arch artifact for an arch that differs from the local one.

Fix idea

  1. --remote: build only the target arch. Keep the generic destination, because no UDID exists at build time. Append ARCHS=<arch> ONLY_ACTIVE_ARCH=YES as build settings; verified above to narrow a generic build to one arch. Default <arch> to the local host arch (process.arch, mapping x64 to x86_64). That is correct whenever the local Mac and the remote host share an arch, which is the common case.

  2. Detect the remote arch where Stim can.

    • proxy: this needs a host-info answer from the agent-device daemon, or a uname -m it can run. Neither exists in the protocol Stim uses today, so check agent-device before designing around it.
    • eas: confirm the host CPU from EAS documentation or a session's metadata. If it is fixed (for example always Apple silicon), a constant per backend is enough.

    Until then, (1) with the local arch is the practical default.

  3. Put the arch in the iOS simulator cache key, mirroring Android's -arm64-v8a. For example, add -arm64 or -x86_64 when the build is single-arch, and keep the current key for universal builds. This key is shared with @stim-cli/expo-build-cache and cache-packages.test.ts, so both follow. Changing the key invalidates existing iOS simulator entries once. A universal artifact could still satisfy either arch as a fallback, but only as a deliberate decision.

  4. Manual override (proposal only). An ios.simulatorArch setting (auto | arm64 | x86_64) in packages/core/state/settings-registry.ts would cover mismatched local and remote hosts until detection exists. Adding a setting or flag needs a product decision, so it is not part of the fix unless that decision is made.

Verification for a fix:

  • lipo -archs on the --remote artifact shows one arch.
  • A real stim ios --remote proxy install and launch still succeeds.
  • The cache key carries the arch.
  • An x86_64 key misses an arm64 entry.

Activity

  1. janicduplessis commented on Sep 28, 2026

    @janicduplessis
    CollaboratorAuthor

    Checked on a real EAS Simulator session (iOS, eas-cli 24.8.0, 2026-09-28, stopped after about 2 minutes):

    • eas simulator:get --json has no architecture or host field. It only has id, name, status, platform, timestamps, remote config and artifacts.
    • The session's agent-device daemon (0.21.16): /health returns only the service name and version. agent-device devices --json lists simulators with platform, appleOs, id, name, kind and booted, again with no architecture.
    • Indirect evidence the host is arm64: the host offers an Apple Vision Pro simulator, and visionOS simulators only run on Apple silicon Macs.

    So neither backend reports the remote architecture today. Defaulting --remote to arm64, or to this Mac's architecture, is right for EAS in practice. Reliable detection would need agent-device to report the host architecture, which is an upstream request.

  2. janicduplessis commented on Sep 28, 2026

    @janicduplessis
    CollaboratorAuthor

    Claimed: fix/1803-remote-ios-single-arch

  3. added a commit that references this issue on Sep 28, 2026
    810bd76
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions