CI 日志里已经出现“构建成功”,但测试任务仍要求启动 iOS Simulator;或者构建节点下载了完整运行时,却只负责生成制品。
最快的判断是:不要统一删除 Simulator。截至 2026 年 9 月 8 日,Xcode 27 Beta 6 已确认 UIKit Interface Builder 文档默认可用 toolchain 模式编译,部分纯构建任务可以不下载 Simulator;应用宿主测试、UI 测试和运行时兼容验证仍应保留 Simulator 或真机。建议先把远程 Mac 拆成轻量构建池与完整测试池,再用同一提交双轨试跑。
这篇文章适合三类团队:
- 只做编译、静态检查和制品生成,希望缩减环境依赖的构建工程师。
- 维护 XCTest、UI 自动化或多系统版本测试,需要确认 Simulator 边界的测试团队。
- 正在调整远程 Mac 节点池、缓存、恢复策略和 CI 路由的 DevOps、研发平台负责人。
Last updated:2026 年 9 月 8 日。本文技术判断核实自 Apple 的 Xcode 27 Beta 6 发布说明、Xcode 系统要求、Xcode 构建与测试文档。Xcode 27 和 iOS 27 目前仍按 Beta 资料处理。
[ SECTION_01 ] 先拆开四个容易混淆的依赖
“安装 Xcode”并不等于“安装 Simulator runtime”。CI 节点至少要区分以下对象:
| 对象 | 作用 | 是否等于 Simulator |
|---|---|---|
| iOS SDK | 编译、链接 iOS 代码所需的头文件、模块和库 | ❌ 不是 |
| Simulator runtime | 提供模拟 iOS 系统运行环境 | ✅ 是运行时 |
| 模拟设备 | 具体的 iPhone 或 iPad 模拟设备实例 | ✅ 依赖运行时 |
| Interface Builder 编译模式 | 编译 Storyboard、XIB 等 UIKit 文档 | ❌ 不等于设备运行 |
| 测试 destination | 告诉 xcodebuild 测试在哪个平台和设备上执行 |
可能要求 Simulator 或真机 |
Xcode 27 Beta 6 的变化集中在最后两个编译环节:Apple 引入了面向 UIKit 文档的 toolchain 编译模式,并说明该模式允许在不下载 Simulator 的情况下编译相关 Interface Builder 文档。官方发布说明同时给出了回退方式,包括将 IBC_COCOATOUCH_COMPILER_MODE 设为 simulator,或在手动调用 ibtool 时传入对应参数。
这并不代表应用已经“可以运行”。Apple 的运行文档仍将“在模拟设备或物理设备上构建并运行”作为测试应用的路径,并明确提醒 Simulator 不能完全复制真实设备的性能和特性。运行模拟或物理设备上的应用因此,编译成功只能证明当前构建路径完成,不能证明测试节点已经可以精简。
[ SECTION_02 ] 构建工程师的节点判断
纯构建可以先试无运行时节点
如果任务只包含源码编译、链接、资源处理、静态检查、归档前构建或制品上传,构建工程师可以先创建一个不主动下载 Simulator runtime 的候选节点。
但“纯构建”必须按实际命令定义,而不是按 Job 名称定义。建议在同一提交上分别执行:
xcodebuild build \
-workspace App.xcworkspace \
-scheme App \
-configuration Release
然后检查日志中是否出现:
platform=iOS Simulator- 指定的 Simulator
destination simctl、模拟设备启动或 runtime 路径- 隐式下载或初始化运行时的步骤
- Storyboard、XIB 或
ibtool的编译错误
Xcode 的命令行工具包括 xcodebuild、simctl 和 devicectl,但“工具存在”不代表每次构建都会启动模拟设备。Xcode 命令行工具参考真正要记录的是本次 Job 是否请求了 destination,以及是否产生了运行时调用。
Xcode 27 Beta 6 的边界
Apple 当前已确认两点:
- Xcode 27 只能安装并运行在 Apple Silicon Mac 上。
- UIKit Interface Builder 文档默认使用
toolchain模式编译,目标是让构建服务器脱离 Simulator 下载。
Xcode 27 Beta 6 的系统要求页面显示,该版本需要 macOS Tahoe 26.4 或更高版本,并对应 iOS 27 SDK。Xcode 系统要求表这些是工具链和宿主环境要求,不应被误读为“所有 CI 工作负载都不再需要 Simulator”。
构建设置的优先级也会影响判断。Xcode 文档说明,命令行传入的构建设置具有最高优先级;项目级、Target 级配置文件和系统默认值可能覆盖或改变最终行为。配置 Target 构建设置因此,项目中如果存在自定义 .xcconfig、Target 覆盖值或脚本参数,不能只看 Xcode 图形界面里的默认选项。
[ SECTION_03 ] Interface Builder 项目的迁移检查
Storyboard 和 XIB 项目最容易出现“第一次构建成功,后续任务失败”的假象。原因是旧项目可能包含自定义构建设置、特殊资源、插件或直接调用 ibtool 的脚本。
Apple 的 Build Settings Reference 将 IBC_COCOATOUCH_COMPILER_MODE 定义为控制 CocoaTouch 文档编译模式的设置,同时列出 Storyboard 编译相关的 IBSC_COCOATOUCH_COMPILER_MODE。构建设置参考工程维护者应同时检查项目中的 .storyboard、.xib、自定义 Run Script 和资源处理脚本,而不是只修改一个全局开关。
建议按以下顺序验收:
- 在候选轻量节点清理 Derived Data,避免缓存掩盖真实依赖。
- 使用现有项目执行一次普通
build,保存完整日志。 - 检查生成的
.storyboardc、.nib和资源目录是否齐全。 - 对比原节点和候选节点的产物清单、警告与错误。
- 对包含自定义模块、动态字体、复杂 Trait Variation 的界面做启动验证。
- 如果出现界面资源错误,记录触发的 Target、配置和资源文件。
- 仅对明确触发问题的目标回退到
simulator模式。
这里不能用“构建退出码为 0”作为唯一标准。界面资源可能在构建阶段生成成功,却在应用启动、Storyboard 加载或 UI 测试阶段才暴露问题。回退策略也应写入 CI 配置,保留一个明确的恢复入口,而不是临时登录节点手动修改。
[ SECTION_04 ] 测试团队的运行时分层
纯 Swift 逻辑测试
纯 Swift 逻辑测试通常只验证算法、数据转换、状态机或业务规则。如果测试目标不需要启动 iOS 应用进程,也不依赖 UIKit、Core Data 的特定运行时行为或系统权限,团队可以评估将其迁往轻量节点。
但不能把所有名称带有 “UnitTests” 的 Target 都归入这一类。测试目标可能包含 Test Host,也可能链接 UIKit、访问文件系统沙盒或依赖应用生命周期。最终要核对:
- 测试 Target 是否配置了
Test Host。 - 测试 Scheme 的 destination 是 macOS、iOS Simulator 还是物理设备。
- 测试产物是否生成
.xctestrun文件。 - 测试命令是否需要指定
-destination。 - 结果包是否包含真正执行的测试,而不是只有构建记录。
应用宿主测试
应用宿主测试需要启动被测 App,验证依赖应用进程、Bundle、沙盒、权限或平台框架的行为。这类测试即使不操作界面,也不应因为名字叫 Unit Tests 就直接放入无运行时节点。
Apple 文档说明,xcodebuild test 会输出 Xcode Test Results,也就是 .xcresults 结果包,里面可以包含测试会话、覆盖率和日志。运行测试并解读结果平台团队应将该结果包作为路由依据:如果结果显示测试实际依赖 iOS destination,就把它送往完整测试池。
UI 自动化测试
XCTest UI Tests 需要启动被测应用并操作用户界面。新 Interface Builder 编译模式只解决“界面文档如何编译”,不能替代“应用如何运行”。
UI 测试至少要保留:
- 已安装且版本匹配的 Simulator runtime。
- 明确的模拟设备 destination。
- 可恢复的设备初始化流程。
- 测试结果包与失败截图、视频或日志。
- 节点重启后重新启动测试的能力。
并行测试还需要额外观察模拟设备实例的创建、删除、克隆和资源竞争。Apple 的测试文档支持通过 Test Plan 管理测试集合和配置,也支持从命令行指定测试计划。使用 Test Plan 组织测试不要只看 Job 的“编译完成”状态,应确认测试计划中的测试确实执行。
[ SECTION_05 ] 构建池与测试池的方案对比
以下评分是面向 CI 架构的编辑判断,不是 Apple 官方性能评级。评分越高,表示越适合对应场景。
| 方案 | 适合任务 | Simulator 依赖 | 运维复杂度 | 节点精简评分 |
|---|---|---|---|---|
| 单池保留完整环境 | 构建、宿主测试、UI 测试混跑 | 高 | 低 | 3 / 5 |
| 双池拆分 | 轻量构建与完整测试分工 | 按任务分配 | 中 | 5 / 5 |
| 暂缓迁移 | Beta 兼容性未确认、旧项目较多 | 高 | 中 | 2 / 5 |
单池保留
单池最容易落地。所有任务使用相同的 Xcode、SDK、Simulator runtime 和缓存,问题定位也比较集中。
缺点是构建任务会长期承担完整测试环境的存储、初始化和恢复成本。若团队每天大量执行纯构建,这种架构会让不需要运行时的任务继续排队等待测试型节点。
双池拆分
双池方案将任务按依赖分流:
- 轻量构建池:静态检查、普通构建、资源编译、归档前检查、制品整理。
- 完整测试池:应用宿主测试、UI 测试、多 iOS 版本验证、真机前置检查。
build-for-testing 与 test-without-building 很适合验证这种边界。Apple 的命令行技术说明指出,前者用于构建测试相关目标并生成 .xctestrun 文件,后者可以在另一个节点使用这些测试产物执行测试。Build For Testing 与 Test Without Building
可按以下方式试跑:
xcodebuild build-for-testing \
-workspace App.xcworkspace \
-scheme App \
-destination 'generic/platform=iOS'
之后将测试产物交给完整测试节点:
xcodebuild test-without-building \
-xctestrun path/to/App_iphonesimulator.xctestrun \
-destination 'platform=iOS Simulator,name=iPhone 16'
具体 destination 需要根据项目目标和已安装 runtime 调整。关键不是照抄设备名称,而是验证构建节点生成的产物是否能被测试节点正确识别。
暂缓迁移
如果工程包含大量旧 Storyboard、直接调用 ibtool 的脚本、非标准插件,或当前 Xcode 27 Beta 仍有未解决的界面资源问题,暂缓删除 Simulator 更稳妥。
暂缓不等于放弃。平台团队可以先保留原节点,同时新增一个可回退的轻量节点。等候选节点连续通过真实项目验证,再修改默认路由。
[ SECTION_06 ] 三类节点配置的适用边界
| 节点类型 | 默认安装内容 | 应接收的任务 | 不应接收的任务 |
|---|---|---|---|
| 轻量构建节点 | Xcode、必要 SDK、依赖缓存 | 编译、静态检查、部分资源处理、制品生成 | UI 测试、应用宿主测试 |
| 完整测试节点 | Xcode、SDK、Simulator runtime、模拟设备 | XCTest、UI 自动化、Test Plan、多版本验证 | 无限制地承担所有普通构建 |
| 回退节点 | 与原生产节点一致的完整环境 | 迁移失败后的重跑、版本对照、发布阻断验证 | 长期替代所有新节点 |
这里的“轻量”不是简单删除某个目录。节点还需要考虑 Xcode 版本、SDK 版本、签名证书、依赖缓存、Derived Data 和制品传输。尤其是 build-for-testing 产生的测试产物必须与测试节点的工具链和目标运行时匹配。
[ SECTION_07 ] 发布负责人验收标准
发布负责人应使用包含真实复杂度的项目进行双轨验收,而不是创建一个最小 Demo。至少选择同时包含 Storyboard 或 XIB、应用宿主测试和 UI 测试的提交。
建议执行以下 6 步:
- 冻结同一提交:原节点和候选节点使用完全相同的 Git commit、Scheme、配置文件与依赖锁定文件。
- 记录环境边界:保存 Xcode、macOS、SDK、构建设置和测试 destination。
- 分别跑纯构建与完整测试:不要用一次全量 Job 代替两种任务。
- 传递测试产物:验证
build-for-testing生成的产物能否在完整测试节点执行。 - 比较结果证据:检查 App 产物、Storyboard 编译结果、
.xcresults、失败媒体和日志。 - 执行重启恢复:重启候选节点后重新运行关键任务,确认缓存、Simulator 和测试设备能恢复。
只有同时满足以下条件,才适合把对应任务正式迁移到轻量构建池:
- 纯构建连续通过;
- 日志没有隐式请求 Simulator destination;
- 没有自动下载或初始化 Simulator runtime;
- 生成产物与原节点一致;
- 失败后可以回退到完整节点;
- 运行时测试仍由测试池承担。
Apple 的 App Store Connect 文档也提醒,上传的构建需要经过系统处理后才会出现在后台,并且构建号会用于识别版本。上传构建到 App Store Connect因此,发布链路不能只验证本地编译结束,还应保留归档、签名、上传和处理状态的独立检查。
[ SECTION_08 ] FAQ:团队迁移前的四个关键问题
Xcode 27 构建 iOS 项目时可以不安装 Simulator 吗?
可以,但范围是“经过验证的构建任务”,不是整个 CI。Xcode 27 Beta 6 的 toolchain 模式让部分 UIKit Interface Builder 文档能够脱离 Simulator 编译;只要任务需要启动 App、执行宿主测试或 UI 自动化,就仍然要保留运行时环境。
哪些 CI 工作负载应保留 Simulator runtime?
需要指定 iOS Simulator destination、启动应用进程、访问 iOS 平台行为或执行 XCTest UI Tests 的任务,都应进入完整测试节点。纯 Swift 逻辑测试可以尝试迁移,但必须先确认没有 Test Host、系统框架运行时依赖和隐式 destination。
Interface Builder 的 toolchain 模式会影响现有 Storyboard 和 XIB 吗?
它改变的是编译路径,不是直接重写界面文件。旧项目、自定义构建设置和直接调用 ibtool 的脚本可能仍然触发兼容问题。迁移时应比较生成的 .storyboardc、.nib、启动结果和 UI 测试,而不是只依据构建退出码。
远程 Mac 构建节点和测试节点应该如何拆分?
把静态检查、普通构建、build-for-testing 和制品整理放到轻量节点;把 test-without-building、应用宿主测试、UI 测试和多版本验证放到完整节点。分拆前先用同一提交执行双轨流程,并保留原节点作为回退入口。
[ SECTION_09 ] 评分结论:何时保留,何时精简
| 判断条件 | 构建节点 | 测试节点 | 处理建议 |
|---|---|---|---|
| 只做源码编译和静态检查 | 5 / 5 | 1 / 5 | 优先试无 Simulator 构建节点 |
| 包含 Storyboard、XIB,但无运行时测试 | 4 / 5 | 2 / 5 | 先验证 toolchain,保留回退配置 |
| 包含应用宿主测试 | 2 / 5 | 5 / 5 | 测试任务进入完整节点 |
| 包含 XCTest UI Tests | 1 / 5 | 5 / 5 | 保留 Simulator runtime |
| 需要多版本 iOS 兼容验证 | 2 / 5 | 5 / 5 | 按版本建立测试节点或测试矩阵 |
| Xcode 27 Beta 兼容性尚未稳定 | 2 / 5 | 4 / 5 | 暂缓生产节点精简 |
对于远程 Mac 环境,推荐先复制一套真实项目到可回退节点,再分别运行普通构建、build-for-testing、test-without-building 和 UI 测试。若需要持续运行的 macOS 节点,可以先参考 NOVAKVM 的远程 Mac 服务入口,按构建池和测试池的实际周期选择环境,而不是先改动生产节点。
如果当前方案是把所有 CI 任务都塞进同一台本地 Mac、Windows 主机配合临时远程桌面,或只使用 Linux 云主机,常见问题是 macOS 工具链无法原生运行、Simulator 测试链路缺失、节点重启后环境难恢复。相比之下,按任务租赁可访问的远程 Mac,更适合先建立独立的构建池或测试池,再根据实际验证结果决定是否长期保留。需要固定地区节点时,也可以进一步查看 NOVAKVM 的 Mac 节点方案。
最终判断很明确:不要因为 Xcode 27 的新编译模式,就把所有节点上的 Simulator 一次性删除。 先让纯构建任务脱离运行时,再让应用宿主测试和 UI 测试继续留在完整节点;只有双轨结果、重启恢复和失败回退都通过后,节点精简才具备工程依据。