Automate iOS Screenshots with fastlane snapshot (Setup Guide)
Set up fastlane snapshot to capture iOS screenshots in every language and device: UI test target, SnapshotHelper, Snapfile options, status bar and output.
ScreenShipper team · · 7 min read
Quick answer: fastlane snapshot runs your Xcode UI tests on several simulators and languages and saves a screenshot wherever your test calls snapshot("01Home"). Run fastlane snapshot init, add SnapshotHelper.swift to a UI test target, call setupSnapshot(app) before app.launch(), list devices and languages in the Snapfile, then run fastlane snapshot from the terminal.
What fastlane snapshot Does and Doesn't Do
snapshot automates the boring part of store screenshots: launching the app on every device in every language and capturing the same screens each time. When your UI changes, you re-run one command instead of tapping through 100 screens by hand.
It captures raw screens only. It doesn't add headlines, backgrounds or device frames, and it doesn't decide which screens sell your app. The pieces fit together like this:
| Piece | Role |
|---|---|
| UI test target | Navigates the app; you write or record these tests in Xcode |
| SnapshotHelper.swift | Provides setupSnapshot() and snapshot() to your tests |
| Snapfile | Lists devices, languages, scheme and output options |
fastlane snapshot | Runs the tests across the matrix and collects the images |
| frameit (optional) | Adds device frames and titles to the captures |
| deliver (optional) | Uploads the screenshots to App Store Connect |
snapshot is an alias of the capture_ios_screenshots action, and fastlane's own examples call it as capture_screenshots inside a lane.
Set Up the UI Test Target and SnapshotHelper
Install fastlane first, either with Homebrew (brew install fastlane) or, better for teams, through Bundler with gem "fastlane" in your Gemfile so everyone runs the same version.
Then follow the steps from the fastlane documentation:
- In Xcode, add a UI Testing Bundle target with File, then New, then Target, if you don't have one.
- In your project folder, run
fastlane snapshot init. It creates a Snapfile and a SnapshotHelper.swift file. - Add SnapshotHelper.swift to your UI test target.
- Create a scheme for the UI test target and tick Shared in Product, Scheme, Manage Schemes. fastlane runs from the command line and needs the shared scheme.
- In your test class's
setUp, createlet app = XCUIApplication(), callsetupSnapshot(app), thenapp.launch(). - Navigate through the app in your tests and call
snapshot("01Home"),snapshot("02Search")and so on at each screen you want.
Xcode's Record button can generate the navigation code while you click through the app in the Simulator. Clean it up afterward: replace fragile queries with accessibility identifiers, such as app.buttons["addTask"].tap(), so the tests survive translation and layout changes.
Warning: Running the UI tests from Xcode doesn't produce the screenshots. Only fastlane snapshot collects them, so always run it from the terminal.
Configure the Snapfile
The Snapfile is a small Ruby file in your fastlane folder. The template fastlane snapshot init creates has commented-out examples of the main options. These are the ones most projects use:
| Option | Example | What it does |
|---|---|---|
devices | devices(["iPhone 16 Pro Max", "iPhone 16 Pro", "iPad Pro 13-inch (M4)"]) | Simulators to capture on |
languages | languages(["en-US", "de-DE", "fr-FR"]) | Languages to run; default is en-US |
scheme | scheme("MyAppUITests") | The shared scheme containing your UI tests |
output_directory | output_directory("./fastlane/screenshots") | Where screenshots are saved |
clear_previous_screenshots | clear_previous_screenshots(true) | Deletes old captures before a run |
override_status_bar | override_status_bar(true) | Sets 9:41, full battery and full reception |
launch_arguments | launch_arguments(["-DemoContent YES"]) | Arguments passed to your app on launch |
localize_simulator | localize_simulator(true) | Also switches the simulator's system language |
dark_mode | dark_mode(true) | Captures in dark appearance |
number_of_retries | number_of_retries(1) | Retries failed tests before giving up |
Device names must match your installed simulators exactly, so copy them from xcrun simctl list devices. Choose simulators whose screens produce the sizes you need: App Store Connect requires a 6.3-inch iPhone set (1206 × 2622 or 1179 × 2556) and a 6.9-inch or 6.5-inch set for the largest phones, plus iPad 13-inch if your app runs on iPad. In the example above, the iPhone 16 Pro produces 1206 × 2622 and the iPhone 16 Pro Max produces 1320 × 2868. The iPhone size guide lists every accepted resolution.
Run fastlane action snapshot to see every option your installed version supports.
Demo Data and a Clean Status Bar
Automated screenshots are only as good as the content on screen. snapshot passes the -FASTLANE_SNAPSHOT YES launch argument, so your app can detect a screenshot run with UserDefaults.standard.bool(forKey: "FASTLANE_SNAPSHOT"). Use it to:
- Load realistic sample content instead of an empty state
- Skip onboarding, login walls and permission prompts
- Disable analytics, ads and rating prompts during the run
- Fix dates, names and numbers so every run looks identical
Localize the demo content as well. A German screenshot with an English to-do list looks unfinished.
For the status bar, override_status_bar(true) handles the classic 9:41 look. To do it by hand on a running simulator, use xcrun simctl status_bar booted override --time 9:41 --batteryState charged --batteryLevel 100 --cellularBars 4, as covered in our clean status bar guide.
Run snapshot and Find the Output
From the project folder, run fastlane snapshot, or bundle exec fastlane snapshot if you use Bundler. snapshot boots the simulators, runs your tests in each language and copies the images into the output directory, separated by language and device. File names come from the strings you passed to snapshot(), which is why a numeric prefix like 01, 02, 03 keeps them in gallery order.
When the run finishes, an HTML summary opens showing every screenshot on every device. Scan it for truncated text and empty states before you go further.
To combine steps, define a lane in your Fastfile named screenshots that calls capture_screenshots, then optionally frame_screenshots(white: true), then upload_to_app_store. Run it with fastlane screenshots. If you only want to send images, upload_to_app_store accepts skip_binary_upload: true and skip_metadata: true.
Common problems
| Problem | Fix |
|---|---|
| No screenshots after running tests in Xcode | Run fastlane snapshot from the terminal instead |
| "Device not found" errors | Copy exact names from xcrun simctl list devices |
| Flaky taps or missing screens | Use accessibility identifiers and wait for elements to exist |
| Screens show the wrong language | Localize your demo data; try localize_simulator(true) |
| Old and new images mixed together | Set clear_previous_screenshots(true) |
From Raw Captures to Store-Ready Screenshots
Raw UI screenshots show your app but don't explain it. Designed store screenshots usually add a benefit headline, a background and often a device frame, and that's what people judge first in search results. You have three paths:
- Upload as-is with
upload_to_app_store. It's the fastest route, and fine for internal tools or utilities with very clear UIs. - Frame and title them with frameit, which adds device frames and text from a Framefile.json. See our fastlane frameit guide.
- Design them in a screenshot tool, using the captures as source images, then export the store sizes.
Whichever you choose, keep snapshot in the loop. When the UI changes next quarter, you re-run one lane and replace the source screens instead of starting over. Android projects can do the same with fastlane screengrab, and Flutter teams can find their options in Flutter App Store screenshots.
Frequently Asked Questions
Does fastlane snapshot work with SwiftUI apps?
Yes. snapshot relies on XCUITest, which drives SwiftUI views the same way as UIKit. Add accessibility identifiers to the views your tests tap so queries stay stable.
Can fastlane snapshot capture dark mode screenshots?
Yes. The dark_mode option sets the appearance for the run, so you can produce a separate dark set with a second Snapfile or lane.
Is fastlane free?
Yes. fastlane is open source, and snapshot, frameit and screengrab are included at no cost.
Why does snapshot take so long?
Every device and language combination is a full UI test run. Trim the device list to the sizes you actually upload, keep tests short, and let concurrent simulators run in parallel.
Do I need a Mac to run fastlane snapshot?
Yes. snapshot drives Xcode's UI tests and the iOS Simulator, so it runs on macOS, either on your own machine or on a macOS CI runner.
Sources
From raw captures to store-ready screenshots
Drop your simulator or emulator captures into a template and export every store size, or frame a single screen with the free mockup generator.