让 AI 看见 SwiftUI:Xcode Preview MCP 的实践、陷阱与期待

Xcode 26.3 是苹果第一次通过 MCP 把自身能力开放给外部 Agent。其中不少能力此前借助 CLI 也能获得,但 Preview 渲染是头一回出现。到了 Xcode 27,苹果又提供了 headless MCP server,调用起来更加顺手,于是我把 Preview 渲染大量嵌进了自己的 AI 工作流。一路用下来,踩了一些坑,也多了一些感悟和期待。

Preview 无法使用正确的渲染设备

在 Xcode 中,预览所用的设备不一定与当前选择的模拟器或实机一致,开发者可以单独为预览指定设备。

以我的 MacBook 为例:由于同时装了 Xcode 27.0 和 27.1 beta,即便打开的是 27.0,预览设备也会被优先设成 iPhone Duo。手动切换到其他设备或平台,预览会随之改变——但这只在 Xcode 的传统交互界面里有效。

一旦改用 Xcode MCP 提供的渲染工具,你会发现根本无法指定渲染设备,连 Xcode 内置的 AI 聊天框也不例外。

原因在于,MCP 的渲染工具目前没有设置目标设备的参数;返回结果里的 renderedDestination 也靠不住——它报告的是承载渲染的模拟器,而不是 Preview 实际模拟的机型。

#Preview 宏同样没有指定设备的参数,还会直接忽略 .previewDevice 的设置。

所以眼下,我只能退回到已被废弃的 PreviewProvider 写法,才能按指定设备渲染出正确的结果:

Swift
@available(*, deprecated, message: "采集用 Preview:只有 PreviewProvider 能指定设备")
struct CapturePreview_home: PreviewProvider {
    static var previews: some View {
        HomeScreens.view(for: "home")
            .previewDevice(PreviewDevice(rawValue: "iPhone 18 Pro"))
            .previewDisplayName("home")
    }
}

用 @available 把类型本身标为废弃,就不会再因为使用废弃 API 而产生编译警告。

代价是,放弃 #Preview 宏也就放弃了预览的许多新特性。如果你有 CI/CD 或 AI 工作流方面的需求,最好为这类场景单独准备一套预览代码。

更新: 文章发布后,erin sparling 分享了另一种 workaround:通过 XcodeSwitchRunDestination 在渲染前切换工程的 active run destination,可以配合普通的 #Preview 在 iPhone 与 iPad 等不同设备类别之间切换;previewVariantOverrides 还可以控制横竖屏。不过,这种方式无法可靠指定具体设备型号——例如指定 iPhone 17 Pro 时,实际承载渲染的可能是 iPhone 18 Pro——并且完整流程依赖 .xcodeproj 或 .xcworkspace。对于需要精确指定 Preview 设备或直接处理 Swift Package 的场景,仍可使用文中的 PreviewProvider 方案。

改了视图代码,截图却还是旧的

在自动化场景中,另一个更让人头疼的问题是:视图代码明明已经改了,Preview 渲染工具返回的却仍是旧画面,而且没有任何报错。

很多时候,预览代码和视图代码并不在同一个文件里。从现象看,如果只改了被预览文件所依赖的其他文件,Xcode 有时并不会重新构建,而是直接交回上一次的结果。只更新文件的修改时间(touch)无济于事,只有被预览文件的内容真正发生变化,才能可靠地触发重新构建。

这个问题在直接渲染 SPM 中的视图时尤为明显,我的测试里,出现的几率在 20%~40% 之间。换成 Xcode 工程(.xcodeproj)后,几率会低很多——我的测试中没有再出现,但这并不代表它一定不会发生。

当然,Agent 可以比较截图的 hash 值,甚至比较渲染耗时的长短,来判断拿到的是不是新图。可这些终究只是间接证据,无法保证返回的截图就是 Agent 想要的那一张。

好在我每次截图时,都会顺带收集视图里的其他信息(每个 .designAnchor 的 id、instance、label,以及窗口坐标 frame),于是干脆把指纹也放进这份信息里。整个过程是这样的:

  • 工具根据源码内容计算出指纹
  • 修改 Preview 代码,写入指纹(这一步改变了被预览文件的内容,本身就会迫使 Xcode 重新构建)
  • 获取截图,以及与之对应的其他信息(JSON)
  • 比较 JSON 中带回的指纹与本次计算的指纹;不一致就再改一次 Preview 文件并重新渲染,仍不一致则放弃这张截图
Swift
fileprivate enum SourceFingerprint_8848880ac81b6df2 {}

struct CapturePreview_home: PreviewProvider {
    static var previews: some View {
        HomeScreens.view(for: "home")
            /// 嵌入指纹
            .designProbe("home", build: String(describing: SourceFingerprint_8848880ac81b6df2.self))
            .previewDevice(PreviewDevice(rawValue: "iPhone 18 Pro"))
    }
}

注意,指纹用的是类型名,而不是字符串字面量。Preview 会对字面量做热替换,只改字面量时可能根本不重新构建,于是出现“指纹是新的、其余代码却是旧的”这种假象;而类型名只有重新编译才会改变。

.designProbe 的代码可以 在此查看。

这样一来,拿到的每张截图都能确认来自当前代码,旧截图再也不会被误用。与此同时,借助这些锚点信息,我还能在 AI 工作流中给出更有针对性的反馈。

其他踩过的坑

使用 Preview 渲染工具时,下面几个问题也经常碰到:

  • 改代码后 Preview 承载进程崩溃:崩溃栈停在 SwiftUICore 的 DebugReplaceableViewChild.updateValue(),类型转换失败,发生在 Preview 热替换视图实现的时候。它的几率与是否改写了 Preview 文件强相关,高的时候接近一半;好在紧接着重试一次,通常就能恢复。
  • 单行书写的 #Preview 会让索引错位:一个文件里的三个 #Preview 各写在一行时,previewDefinitionIndexInFile: 2 渲染出来的是第 0 个,返回的 sourceLineNumber 却是对的;改成多行书写则完全正常。结论很简单:#Preview 一律多行书写。
  • 增删文件后可能找不到文件:写入新文件后立刻渲染,会报 FileNotFoundError;删掉一个文件后立刻渲染另一个没改过的文件,会报 ProviderError: noPreviewInfos。有时等上一两秒就能恢复,但 headless 模式会缓存工程结构,新增的文件往往要关闭并重新打开工作区才能被识别。

尽管坑不少,Xcode 提供的 Preview 渲染工具仍然给开发者和创作者带来了极大的便利,也打开了不少想象空间。

AI + Preview + SwiftUI == 苹果开发者的创意工具

在开发的不同阶段,我们对 UI 的要求并不相同。比如在创意阶段,我习惯用文档来沉淀和整理想法,这件事如今 AI 已经能帮上大忙。但无论把文档转换成流程图还是状态分析,对很多开发者来说都不够直观。

于是我把 Preview 渲染接入了这个阶段。这里的 UI 不必精确,也不用关心运行逻辑,但把对应的截图嵌进流程分析之后,梳理的速度快了很多,那些之前没发现的问题、没考虑周全的地方,也更容易浮出水面。

下面的视频展示的是我自制的工具 FlowCanvas。它帮我把文档变成可视、可交互的画布,并在调整之后自动与文档保持一致。

SwiftUI Preview 同样是一个出色的创意工具。当你设计 UI 毫无头绪时,可以让 Agent 帮你发散,再通过反馈和交流不断迭代,直到找到想要的呈现方式。

下面的视频是我另一个借助 Preview 渲染能力打造的工具 FormStudio:

在开发和使用这些工具的过程中,我一直在想:如果 Xcode 能直接提供类似的能力,那该多好。哪怕只是开放更多插件接口(不依赖 CLI 或 MCP),让开发者能够在其中自行扩展,也会是一个不错的选择。

在 AI 时代,IDE 的存在感正在下降,但与交互、设计相关的操作,仍为人类开发者保留着一片空间。

Xcode 在 UIKit 时代曾提供过 Storyboard 这样试图连接设计与逻辑的优秀工具,但在 SwiftUI 与 AI 的时代,我们依然只能对着碎片化的 Preview 管中窥豹。Xcode 应该作出改变了——它不应只服务于写代码的人,程序员、设计师与产品经理之间的界限,正在被 AI 迅速抹平。

当 Xcode 不再被 code 所拘束,它依然能赢得大家的青睐。

订阅 Fatbobman 周报

每周精选 Swift 与 SwiftUI 开发技巧,加入众多开发者的行列。

立即订阅