{"slug": "a-method-for-handling-orientation-and-device-selection-using-agent-driven-xcode", "title": "A method for handling orientation and device selection using agent-driven Xcode MCP connections", "summary": "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.", "body_md": "Workarounds for \"Letting AI See SwiftUI\": device and orientation in RenderPreview\n\nNotes 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).\n\nOrientation: solved with previewVariantOverrides\n\nEvery RenderPreview result includes a supportedPreviewVariantOverrides field listing the variants that preview accepts. Pass one back as previewVariantOverrides and the render uses it:\n\n```\n{\n  \"workspaceIdentifier\": \"<id>\",\n  \"sourceFilePath\": \"App/Views/SomeView.swift\",\n  \"previewDefinitionIndexInFile\": 0,\n  \"previewVariantOverrides\": { \"Orientation\": \"Landscape Left\" }\n}\n```\n\nRead the keys and values from supportedPreviewVariantOverrides rather than hard-coding them. We use this for every landscape iPad render.\n\nDevice: switch the run destination before rendering\n\nRenderPreview 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:\n\n```\n// XcodeSwitchRunDestination\n{ \"workspaceIdentifier\": \"<id>\", \"displayTitle\": \"iPhone 17 Pro (27.0)\" }\n```\n\nCheck the switch against its own response. It returns activeDestinationDisplayTitle, so compare that to what you asked for, and stop if they differ.\n\nWait before the first render. We give it about 45 s after a switch; rendering immediately can still use the old destination.\n\nXcodeListRunDestinations hung for 240 s, twice, on this Xcode RC, so don't depend on listing. XcodeSwitchRunDestination takes the display title directly.\n\nThis reliably selects iPhone vs iPad with #Preview, so you don't need the deprecated PreviewProvider for that.\n\nThe loop, in short\n\nXcodeOpenWorkspace, then XcodeSwitchRunDestination. Check activeDestinationDisplayTitle and wait.\n\nFor each preview, call RenderPreview with previewDefinitionIndexInFile, plus previewVariantOverrides if you need an orientation. If the response says \"Waiting for packages\", retry after 60 s.\n\nCopy previewSnapshotPath out, and check that renderedDestination is the right device family.\n\nAGENTS.md: rendering SwiftUI previews through Xcode's MCP bridge\n\nInstructions 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.\n\nConnect\n\nStart the bridge and speak MCP (JSON-RPC 2.0, one JSON object per line) over stdio:\n\n```\nxcrun mcpbridge\n```\n\nSend initialize (e.g. protocolVersion: \"2025-06-18\"), wait for its response, then send notifications/initialized.\n\nCall tools with tools/call. The result text is in result.content[0].text, and it's JSON: parse it.\n\nXcode must allow external agents (Settings: \"Allow external coding agents\"). Depending on the Xcode build, the first connection may show an approval dialog.\n\nProcedure\n\n1. Open the workspace\n\nXcodeOpenWorkspace with { \"path\": \"<abs path to .xcodeproj or .xcworkspace>\" }. Keep the returned workspaceIdentifier; every later call needs it.\n\n2. Choose the device: switch the run destination\n\nRenderPreview has no device parameter, and #Preview ignores .previewDevice. Previews render on the workspace's active run destination, so set that first:\n\nXcodeSwitchRunDestination with { \"workspaceIdentifier\": \"<id>\", \"displayTitle\": \"iPhone 17 Pro (27.0)\" }\n\nCheck the switch. Parse activeDestinationDisplayTitle from the response. If it isn't exactly what you asked for, stop. Never render on an unconfirmed destination.\n\nWait about 45 s before the first render. Rendering immediately after a switch can still use the previous destination.\n\nDon'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.\n\n3. Render\n\nRenderPreview with:\n\n```\n{\n  \"workspaceIdentifier\": \"<id>\",\n  \"sourceFilePath\": \"<path to the .swift file>\",\n  \"previewDefinitionIndexInFile\": 0,\n  \"timeout\": 900,\n  \"previewVariantOverrides\": { \"Orientation\": \"Landscape Left\" }\n}\n```\n\npreviewDefinitionIndexInFile is the 0-based index of the #Preview within that file.\n\nOrientation (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.\n\nIf the response text contains \"Waiting for packages\", wait 60 s and retry, up to about 4 times.\n\n4. Verify the render before using it\n\nFrom the parsed result:\n\npreviewSnapshotPath: the PNG. Copy it somewhere stable right away.\n\nerrors: must be empty.\n\nrenderedDestination.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.\n\nThe 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.\n\nLook at the PNG (open it with your image-reading tool) before describing it. A render can succeed and still show the wrong state.\n\nStale 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.\n\n5. Close\n\nXcodeCloseWorkspace with { \"workspaceIdentifier\": \"<id>\" }, then end the bridge process.\n\nRules\n\nOne destination per run. To render iPhone and iPad, run the procedure twice, switching destinations between runs.\n\nName 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.\n\nReport 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.\n\nPreview fixtures that exist only in DEBUG must be wrapped, previews included, in #if DEBUG, or Release builds will fail to compile.", "url": "https://wpnews.pro/news/a-method-for-handling-orientation-and-device-selection-using-agent-driven-xcode", "canonical_source": "https://gist.github.com/everyplace/67df76ddf3d88f8f35ff202a85080ee2", "published_at": "2026-09-30 01:27:01+00:00", "updated_at": "2026-10-01 13:18:47.801565+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "developer-tools", "ai-tools"], "entities": ["Xcode", "xcrun mcpbridge", "RenderPreview", "XcodeSwitchRunDestination", "XcodeListRunDestinations", "SwiftUI", "XcodeOpenWorkspace", "PreviewProvider"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/a-method-for-handling-orientation-and-device-selection-using-agent-driven-xcode", "markdown": "https://wpnews.pro/news/a-method-for-handling-orientation-and-device-selection-using-agent-driven-xcode.md", "text": "https://wpnews.pro/news/a-method-for-handling-orientation-and-device-selection-using-agent-driven-xcode.txt", "jsonld": "https://wpnews.pro/news/a-method-for-handling-orientation-and-device-selection-using-agent-driven-xcode.jsonld"}}