App Store 截图自动化:2026 fastlane snapshot 远程 Mac 教程

Apple 当前允许每个设备尺寸上传 1–10 张截图,格式支持 JPEG、JPG 和 PNG,且图片不能包含透明通道。(Apple 截图规格说明)

因此,判断是否采用 App Store 截图自动化,不应从“如何安装 fastlane”开始,而应先看截图矩阵:只有少量页面、单一语言、很少更新时,手动截图更省事;需要覆盖多语言、多设备,或者每次改版都要重新生成截图时,应采用 fastlane snapshot。远程 Mac 适合承载重复执行,但必须先固定测试数据、语言和 Simulator 环境,再逐步开启并行和上传。

这篇内容适合三类人:

  • 需要为多个 App 本地化版本重复制作截图的独立开发者;
  • 没有常驻 Mac、但需要运行 Xcode UI Tests 和 iOS Simulator 的 Windows/Linux 开发者;
  • 想把截图生成纳入版本发布流程的小型 App 团队。

截图任务通常包含四个不同环节:

  1. 在 Simulator 中捕获原始画面;
  2. 为原始画面添加设备边框、背景和营销文案;
  3. 检查语言、尺寸、顺序和内容;
  4. 上传到 App Store Connect。

fastlane snapshot 主要负责第 1 步,也可以生成汇总 HTML 页面;frameit 负责第 2 步;上传则属于 deliver 或 App Store Connect API 的职责。把这几步混在一起,最容易出现“截图生成成功,但上传资产不合格”的情况。(fastlane 截图文档)

可以先用下面的判断方式:

  • 单语言、单设备、少量固定页面:手动截图。
  • 多个页面,偶尔更新:先写一条可重复的 XCTest UI Test,再决定是否接入 snapshot。
  • 多语言、多设备、频繁改版:直接维护 Snapfile 和截图测试。
  • 截图任务需要在夜间或发布前无人值守运行:使用远程 Mac,并保存日志、结果目录和失败设备清单。

不要机械遍历所有 Simulator。Apple 对不同显示目标有明确的截图规格和替代缩放规则。例如,当前较新的 6.9 英寸 iPhone 截图可使用 1260 × 27361290 × 2796 等尺寸;如果没有提供相应尺寸,App Store Connect 可能使用可接受尺寸进行缩放。相关显示目标和缩放规则应以 Apple 的截图规格页面为准。

截图方案评分表

方案 多语言能力 多设备能力 重复执行 失败定位 适合场景
手动截图 单次、小规模发布
XCTest UI Tests + snapshot 持续更新的 App
snapshot + frameit + 上传流程 本地化商店素材流水线
远程 Mac 执行整套流程 取决于日志设计 没有常驻 Mac 或需要无人值守

评分只代表流程特性,不代表某个项目的执行速度。真正的瓶颈通常来自 Simulator Runtime、测试数据、网络状态和远程会话恢复,而不是命令本身。

不要直接复用日常回归测试。截图测试的目标是得到稳定、可比较的画面,因此建议建立专用 UI Test Target,例如:

AppUITestsForScreenshots

同时创建一个专用 Scheme,例如:

[PLACEHOLDER]_ScreenshotScheme

Scheme 必须设置为 Shared,并确保 UI Test Target 在 Build 和 Test 配置中可被命令行调用。fastlane 官方流程要求先创建 UI Test Target、执行 fastlane snapshot init、加入 SnapshotHelper.swift,再创建并共享 Scheme。

截图测试中的控件定位不要依赖坐标。应优先使用稳定的 accessibility identifier:

let app = XCUIApplication()
let continueButton = app.buttons["[PLACEHOLDER]_continue_button"]

XCTAssertTrue(continueButton.waitForExistence(timeout: 10))
continueButton.tap()

XCTest 的 XCUIElementQuery 用于查找和验证界面元素。若测试依赖屏幕坐标,换设备尺寸、换文字长度或换横竖屏后,测试很容易失效。(Apple XCUIElementQuery 文档)

然后在固定页面节点调用 snapshot:

import XCTest

final class ScreenshotUITests: XCTestCase {
    let app = XCUIApplication()

    override func setUpWithError() throws {
        continueAfterFailure = false
        setupSnapshot(app)
        app.launchArguments += [
            "-screenshot_mode",
            "-test_user",
            "[PLACEHOLDER]_user"
        ]
        app.launch()
    }

    func testScreenshots() {
        snapshot("01_Home")
        app.buttons["[PLACEHOLDER]_library"].tap()
        snapshot("02_Library")
        app.buttons["[PLACEHOLDER]_settings"].tap()
        snapshot("03_Settings")
    }
}

首次成功的证据不能只是“测试通过”。至少要同时确认:

  • 输出目录确实生成图片;
  • 文件名顺序符合商店展示顺序;
  • 每张图对应正确页面;
  • fastlane 生成的 HTML 汇总可以打开;
  • 测试日志中没有隐藏的超时或重试。

直接在 Xcode 中运行 UI Test,并不等同于通过命令行运行 snapshot。命令行流程还负责创建目录、重命名文件和生成汇总页面,因此首次验收应以输出资产为准,而不是只看 Xcode 的绿色状态。

多语言截图不能只切换系统语言。至少要分别控制:

  • 语言代码;
  • 区域格式;
  • 测试账号;
  • 登录状态;
  • 订阅状态;
  • 空数据或已有数据;
  • 功能开关;
  • 网络返回内容。

例如,英文界面可能显示较短的按钮文字,德语、法语或中文界面则可能改变按钮宽度和换行。若所有语言共用一份不可控的线上账号,截图结果会随着历史订单、推荐内容或接口状态变化。

Snapfile 可以集中定义语言、设备、Scheme、启动参数和输出目录:

scheme("[PLACEHOLDER]_ScreenshotScheme")

devices([
  "[PLACEHOLDER]_iPhone_Pro",
  "[PLACEHOLDER]_iPad_Pro"
])

languages([
  "en-US",
  "zh-Hans",
  "ja",
  "ko"
])

launch_arguments([
  "-screenshot_mode",
  "-test_user [PLACEHOLDER]_user",
  "-locale_data [PLACEHOLDER]_dataset"
])

output_directory("./fastlane/screenshots")
clear_previous_screenshots(true)
override_status_bar(true)

fastlane snapshot 支持配置多个语言和设备组合,并能使用启动参数把固定状态传入应用。官方示例也包含语言列表、设备列表、launch_arguments、输出目录和状态栏覆盖选项。(fastlane snapshot 参数文档)

固定登录状态时,建议采用“测试模式 + 本地种子数据”,而不是把真实账号密码直接写入测试代码。常见做法是:

  1. 测试启动时读取环境变量;
  2. 应用检测到 -screenshot_mode 后加载固定数据;
  3. 登录状态由测试环境注入;
  4. 网络请求使用稳定的 mock 或测试接口;
  5. 每次测试结束后清理本地缓存。

这样可以避免某个语言版本因为登录过期、订阅变化或接口异常而截出错误页面。登录、订阅、空数据和功能开关应当由启动参数或测试数据集明确指定,不要依赖上一轮 Simulator 的残留状态。

Apple 当前对 iPad 运行的 App 要求提供 iPad 截图;对 iPhone App,则会根据已上传的高分辨率截图进行适配和缩放。App Store Connect 允许在 Media Manager 中为其他设备尺寸补充专用截图,但不代表每种 Simulator 都必须执行。(App Store Connect 上传说明)

建议把设备组合拆成三类:

  • 主展示设备:用于生成商店默认展示素材;
  • 布局边界设备:用于检查最小或特殊尺寸下的文字、弹窗和横屏布局;
  • 平台专用设备:例如 iPad,用于确认是否需要单独设计页面。

横屏页面不要与竖屏页面混在同一个测试函数里。更容易维护的做法是:

screenshots/
├── en-US/
│   ├── iPhone/
│   ├── iPad/
│   └── landscape/
├── zh-Hans/
│   ├── iPhone/
│   └── iPad/

每个截图名称也应包含语义,而不是只使用 screen1.png

01_Home.png
02_Search.png
03_Subscription.png

这能让人工复核、失败重跑和上传前检查更容易。

没有本地 Mac 时,可以把 Xcode、Simulator 和 fastlane 放在远程 Mac 上执行。通过 NOVAKVM 的远程 Mac 环境 连接后,先不要同时启动多个 Simulator,也不要一开始就把截图、装饰和上传全部串成一条命令。

先完成下面的基线流程:

xcodebuild \
  -scheme "[PLACEHOLDER]_ScreenshotScheme" \
  -sdk iphonesimulator \
  -destination 'platform=iOS Simulator,name=[PLACEHOLDER]_iPhone' \
  test

fastlane snapshot \
  --scheme "[PLACEHOLDER]_ScreenshotScheme"

Xcode 可以通过 xcodebuild test 在终端运行测试,并输出 .xcresults 测试结果包,其中包含测试会话、日志和其他结果信息。(Apple Xcode 测试结果文档)

远程执行时要重点处理四类限制:

  • 用户会话:远程桌面断开后,图形会话是否仍保持可用;
  • Simulator Runtime:目标设备对应的运行时是否已安装;
  • 磁盘空间:多个 Runtime、DerivedData、归档和截图目录会持续占用空间;
  • 结果取回:截图、日志、HTML 汇总和 .xcresults 是否能下载到本地。

长任务不应依赖开发者一直盯着 VNC 窗口。应把命令放进可恢复的终端会话或脚本中,并将标准输出写入日志:

mkdir -p ./logs

fastlane snapshot \
  --scheme "[PLACEHOLDER]_ScreenshotScheme" \
  2>&1 | tee "./logs/snapshot-$(date +%Y%m%d-%H%M%S).log"

这里的日期命令只用于生成日志文件名,不代表任务执行耗时或固定运行时间。

远程 Mac 同时运行多个 Simulator 时的失败边界

并行执行看起来能提高吞吐,但截图流程同时依赖应用构建、UI Test、Simulator 图形界面、测试数据和文件写入。多个设备并行时,常见问题包括:

  • 测试数据被多个进程同时修改;
  • 应用启动和网络 mock 的返回顺序不稳定;
  • 某个 Simulator 卡在安装或启动阶段;
  • 多个任务写入相同输出目录;
  • 远程图形会话无法稳定承载多个窗口;
  • 一个设备失败后,整批任务被误判为全部失败。

因此建议采用渐进策略:

  1. 单设备串行运行;
  2. 增加第二个设备,确认目录和测试数据隔离;
  3. 再增加语言组合;
  4. 最后才尝试并行;
  5. 失败时只重跑失败的设备和语言组合。

不要把“并行启动成功”当成“截图流水线稳定”。稳定性的判断标准应是:失败任务可识别、结果目录不互相覆盖、单个组合能够独立重跑。

snapshot 生成的是原始应用画面,不等同于最终营销图。若需要设备边框、背景和标题,可以使用 frameit。不带标题的设备边框图片可能保持完整分辨率,不能直接上传到 App Store;要用于商店,必须按照要求配置背景和标题等参数。(fastlane frameit 文档)

推荐把目录分为三层:

fastlane/
├── screenshots_raw/
├── screenshots_marketing/
└── screenshots_upload/

对应流程为:

XCTest UI Tests
        ↓
fastlane snapshot
        ↓
原始截图验收
        ↓
frameit 或其他装饰流程
        ↓
尺寸与透明通道检查
        ↓
deliver 或 App Store Connect API

App Store Connect 每个设备尺寸最多可上传 10 张截图,截图不能含透明通道;如果应用支持 iPad,必须单独关注 iPad 截图要求。相关限制以 Apple 截图规格和上传说明为准。

上传前至少抽查以下内容:

  • 语言目录是否与 App Store Connect 本地化名称一致;
  • 文件是否为 JPEG、JPG 或 PNG;
  • 图片是否包含透明通道;
  • 图片尺寸是否对应目标显示类型;
  • 截图顺序是否从核心价值到次要功能;
  • 是否出现加载指示器、错误页或测试账号信息;
  • iPhone、iPad、横屏目录是否被错误混用;
  • 上传后的处理状态是否完成;
  • 本次生成使用的 Scheme、提交版本和测试数据是否已记录。

上传权限也不能默认所有账号都具备。App Store Connect 截图管理页面列出的相关角色包括 Account Holder、Admin、App Manager 和 Marketing。

如果要自动上传,建议先在本地或远程 Mac 上完成一次人工确认,再执行:

lane :store_screenshots do
  capture_ios_screenshots(
    scheme: "[PLACEHOLDER]_ScreenshotScheme"
  )

  # 经过人工或脚本验收后再上传
  upload_to_app_store(
    skip_binary_upload: true,
    skip_metadata: false
  )
end

不要让“生成截图”和“立即覆盖商店素材”无条件绑定。自动化的价值是减少重复劳动,不是把一次错误批量扩散到所有语言和设备。

如果只有一个语言、一个设备和几个页面,手动截图仍然是更低成本的选择。只要出现多语言、多设备、频繁改版或无人值守需求,fastlane snapshot 就更适合,因为它能把界面状态、截图命名、设备组合和结果汇总保存成可重复流程。

远程 Mac 的正确用法也不是“连接后直接并行跑满”。更稳妥的顺序是:

  • 先固定测试账号和本地数据;
  • 再固定语言、区域和启动参数;
  • 接着用单设备串行验证;
  • 然后扩展到 iPhone、iPad 和横屏组合;
  • 最后分离装饰、验收和上传。

与本地临时拼装相比,Windows/Linux 开发者常见的替代方案会受到 macOS 工具链不可用、远程 CI 图形环境不透明、失败后难以取回 Simulator 结果等限制;长期依赖不稳定的共享环境,也容易让截图任务变成发布流程中的隐藏故障点。若只是阶段性制作本地化素材,或需要在发布前重复运行 UI Tests,租赁 NOVAKVM 的远程 Mac 通常比为截图专门购买并长期维护一台 Mac 更灵活。可以先参考 远程 Mac 方案选择,再按语言数量、设备矩阵和更新频率决定是短期使用,还是保留一个常驻截图节点。

为截图自动化准备一台专属远程 Mac

NOVAKVM 提供 100% 独享的 Apple Silicon 裸金属 Mac,适合多语言、多设备截图矩阵的批量执行。

通过 SSH 或远程桌面接入完整系统权限,快速配置测试数据、模拟器与自动化脚本,减少本地环境差异。

查看定价 →