cd /news/ai-agents/a-method-for-handling-orientation-an… · home › topics › ai-agents › article
[ARTICLE · art-143212] src=gist.github.com ↗ pub= topic=ai-agents verified=true sentiment=· neutral

A method for handling orientation and device selection using agent-driven Xcode MCP connections

A developer documented a workaround for rendering SwiftUI previews on chosen devices and orientations by driving Xcode's MCP bridge (xcrun mcpbridge) from an external agent on Xcode 27.0 RC. Orientation is handled by passing a value from each RenderPreview result's supportedPreviewVariantOverrides field back as previewVariantOverrides, while device selection requires calling XcodeSwitchRunDestination to change the workspace's active run destination and waiting roughly 45 seconds before the first render. The developer also reported that XcodeListRunDestinations hung for 240 seconds twice on the release candidate, so the switch tool should be called with a display title directly.

by read4 min views1 publishedSep 30, 2026

Workarounds for "Letting AI See SwiftUI": device and orientation in RenderPreview

Notes on the open problems in fatbobman's post, from driving Xcode's MCP bridge (xcrun mcpbridge) from an external agent. Observed on Xcode 27.0 RC, with plain #Preview (no PreviewProvider).

Orientation: solved with previewVariantOverrides

Every RenderPreview result includes a supportedPreviewVariantOverrides field listing the variants that preview accepts. Pass one back as previewVariantOverrides and the render uses it:

{
  "workspaceIdentifier": "<id>",
  "sourceFilePath": "App/Views/SomeView.swift",
  "previewDefinitionIndexInFile": 0,
  "previewVariantOverrides": { "Orientation": "Landscape Left" }
}

Read the keys and values from supportedPreviewVariantOverrides rather than hard-coding them. We use this for every landscape iPad render.

Device: switch the run destination before rendering

RenderPreview has no device parameter, and .previewDevice is ignored under #Preview. But the preview renders on the workspace's active run destination, and the bridge can change that:

// XcodeSwitchRunDestination
{ "workspaceIdentifier": "<id>", "displayTitle": "iPhone 17 Pro (27.0)" }

Check the switch against its own response. It returns activeDestinationDisplayTitle, so compare that to what you asked for, and stop if they differ.

Wait before the first render. We give it about 45 s after a switch; rendering immediately can still use the old destination.

XcodeListRunDestinations hung for 240 s, twice, on this Xcode RC, so don't depend on listing. XcodeSwitchRunDestination takes the display title directly.

This reliably selects iPhone vs iPad with #Preview, so you don't need the deprecated PreviewProvider for that.

The loop, in short

XcodeOpenWorkspace, then XcodeSwitchRunDestination. Check activeDestinationDisplayTitle and wait.

For each preview, call RenderPreview with previewDefinitionIndexInFile, plus previewVariantOverrides if you need an orientation. If the response says "Waiting for packages", retry after 60 s.

Copy previewSnapshotPath out, and check that renderedDestination is the right device family.

AGENTS.md: rendering SwiftUI previews through Xcode's MCP bridge

Instructions for an agent that needs to see a SwiftUI view: render a #Preview to a PNG on a chosen device family and orientation, and confirm it's the right render before trusting it. Written against Xcode 27.0 RC. Works with plain #Preview; no PreviewProvider needed.

Connect

Start the bridge and speak MCP (JSON-RPC 2.0, one JSON object per line) over stdio:

xcrun mcpbridge

Send initialize (e.g. protocolVersion: "2025-06-18"), wait for its response, then send notifications/initialized.

Call tools with tools/call. The result text is in result.content[0].text, and it's JSON: parse it.

Xcode must allow external agents (Settings: "Allow external coding agents"). Depending on the Xcode build, the first connection may show an approval dialog.

Procedure

  1. Open the workspace

XcodeOpenWorkspace with { "path": "<abs path to .xcodeproj or .xcworkspace>" }. Keep the returned workspaceIdentifier; every later call needs it.

  1. Choose the device: switch the run destination

RenderPreview has no device parameter, and #Preview ignores .previewDevice. Previews render on the workspace's active run destination, so set that first:

XcodeSwitchRunDestination with { "workspaceIdentifier": "<id>", "displayTitle": "iPhone 17 Pro (27.0)" }

Check the switch. Parse activeDestinationDisplayTitle from the response. If it isn't exactly what you asked for, stop. Never render on an unconfirmed destination.

Wait about 45 s before the first render. Rendering immediately after a switch can still use the previous destination.

Don't depend on XcodeListRunDestinations. It hung for 240 s, twice, on Xcode 27.0 RC. XcodeSwitchRunDestination accepts the display title directly, so you don't need to list first.

  1. Render

RenderPreview with:

{
  "workspaceIdentifier": "<id>",
  "sourceFilePath": "<path to the .swift file>",
  "previewDefinitionIndexInFile": 0,
  "timeout": 900,
  "previewVariantOverrides": { "Orientation": "Landscape Left" }
}

previewDefinitionIndexInFile is the 0-based index of the #Preview within that file.

Orientation (and other variants): omit previewVariantOverrides on the first render. The response lists supportedPreviewVariantOverrides; take the keys and values from there and pass them back. Don't hard-code values you haven't seen in that list.

If the response text contains "Waiting for packages", wait 60 s and retry, up to about 4 times.

  1. Verify the render before using it

From the parsed result:

previewSnapshotPath: the PNG. Copy it somewhere stable right away.

errors: must be empty.

renderedDestination.deviceModelName / .systemVersion: use these as a guard, not as a record of what you asked for. Check the family ("iPhone" vs "iPad"). If it's wrong, stop and discard the image.

The exact model is not controllable. Asking for "iPhone 17 Pro (27.0)" has rendered as "iPhone 18 Pro": same size class, different model. Don't claim a specific model in your output.

Look at the PNG (open it with your image-reading tool) before describing it. A render can succeed and still show the wrong state.

Stale renders: a render can come from a cached build. If freshness matters, embed a fingerprint in the preview (e.g. a short hash or a counter drawn in the view) and check that it shows up in the image. That's the more robust approach described in the original post.

  1. Close

XcodeCloseWorkspace with { "workspaceIdentifier": "<id>" }, then end the bridge process.

Rules

One destination per run. To render iPhone and iPad, run the procedure twice, switching destinations between runs.

Name output files with the preview label, the device family and the source revision (e.g. footer-idle-iphone-<sha7>.png) so renders from different builds can't be mixed up.

Report what was verified: the destination the switch confirmed, the family the render reported, and whether the fingerprint matched. If any check failed, say so rather than presenting the image.

Preview fixtures that exist only in DEBUG must be wrapped, previews included, in #if DEBUG, or Release builds will fail to compile.

── more in #ai-agents 4 stories · sorted by recency
── more on @xcode 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/a-method-for-handlin…] indexed:0 read:4min 2026-09-30 · —