Shotlist guide
Shotlist user guide
What Shotlist does, what you need to run it, and the five steps from an Xcode project to screenshots in App Store Connect.
Shotlist user guide
Shotlist makes App Store screenshots a build step. You describe each screenshot as a scene in Swift. Shotlist builds your app, captures every scene on every device size, in every locale and appearance, in one run on simulators it manages itself. It then adds captions, draws the creative assets, checks everything against Apple’s rules and uploads the set to App Store Connect.
When you change the app, you run it again. Each run marks every screenshot as new, changed or unchanged against the last one, so you review only what moved.
This guide covers the command-line tool (shotlist), the Mac app and the in-app library (ShotlistKit). The CLI and the Mac app run the same engine and read the same project files, so you can switch between them freely.
What Shotlist does
| Stage | What happens | Command |
|---|---|---|
| Set up | Creates a Shotlist/ folder in your repo and a Screenshots.swift to start from. |
shotlist init |
| Check | Confirms Xcode, simulator runtimes, disk space and iPhone Duo automation. | shotlist doctor |
| Capture | Builds the app once, then captures every scene on its own simulators. Waits on signals from inside your app rather than fixed delays. | shotlist capture |
| Captions | Shows which captions are written, drafted or missing, drafts translations, and records sign-off. | shotlist captions |
| Compose | Places captures on captioned canvases and draws the product page header and search results art. | shotlist compose |
| Verify | Checks sizes, transparency, counts, locales, captions and Apple’s content rules. | shotlist verify |
| Publish | Prints a plan of what would change in App Store Connect, then carries it out with --apply. |
shotlist publish |
A few things set it apart from scripted screenshot tools:
- Scenes are typed Swift. A scene is your app’s real root view or view controller, taken to one screen and filled with fixture data. There is no UI test target to maintain.
- No sleeps. A scene is captured only when ShotlistKit reports that its preparation has finished, no request is in flight, no hold is open and two frames have passed with nothing new. The engine then captures until two consecutive frames match.
- Repeatable output. Dates, time zone, hashing order, status bar and animations are pinned, so two runs of the same build give byte-identical files.
- Every current size, including both displays of the iPhone Duo.
- Any architecture. SwiftUI with
@Observable, UIKit and the Composable Architecture (TCA) are all supported. TCA is never required. - Your simulators are left alone. Shotlist creates and uses its own, named
Shotlist · <slot> · <runtime>. - It ends in App Store Connect, and never deletes a screenshot it didn’t upload unless you ask it to.
Requirements
| You need | Why |
|---|---|
| A Mac running macOS 26 or later | The engine, the composer and the Mac app need it. The Mac app is for Apple silicon; the CLI is a universal binary, for Apple silicon and Intel. |
Xcode 27 or later, selected with xcode-select |
Shotlist builds with xcodebuild and drives simulators with simctl. doctor fails on anything older. |
| iOS 27 simulator runtimes (Xcode › Settings › Components) | Each slot needs a runtime that supports its device. With Xcode 27.1, the iPhone Duo runs on iOS 27.1 and every other device on iOS 27.0. |
| About 15 GB of free disk | Each Shotlist simulator takes 2 to 6 GB. Each project’s build takes about 1 GB more. |
| An app that deploys to iOS 17 or later | ShotlistKit, the library you add to your app, needs iOS 17. |
| An app that declares every locale you capture | iOS falls back to another language for a locale the app doesn’t declare. See Setting up a project. |
| Someone at the Mac, screen unlocked, for the iPhone Duo’s inner display | The Duo has to be unfolded through Device Hub, which can’t be reached while the screen is locked. Everything else runs unattended. See iPhone Duo. |
| A Shotlist subscription for capturing, composing and uploading | Setting up, checking, verifying, captions and the read-only publish plan are free. See Subscription. |
| An App Store Connect team API key, to publish | See App Store Connect. |
The five steps
1. Install Shotlist and check the Mac
Download the Mac app, the CLI, or both from the Shotlist page, and activate your licence key. Then check the Mac:
shotlist doctor
See Installing Shotlist.
2. Set up the project and add ShotlistKit
In your app’s repo:
shotlist init --scenes-file MyApp/Screenshots.swift
Add the ShotlistKit package to your app and host the scenes in your app’s entry point: @Shot does it in one line, or host them yourself inside #if DEBUG. Release builds contain none of it. See Setting up a project.
3. Write scenes
A scene is your app’s real root, taken to one screen, showing fixture content:
SwiftUIScene("library") { context in
RootView(router: Router(tab: .library), clock: .fixed(context.now))
.modelContainer(try container(context))
}
List the scene ids in sequence in Shotlist/shotlist.json, in the order they should appear on the product page. See Writing scenes, Readiness and fixtures and Making runs identical.
4. Capture
shotlist capture
Raw captures land in Shotlist/Output/raw/<locale>/<slot>/NN-<scene>-<appearance>.png, with a manifest and a run log beside them. See Capturing.
5. Compose, verify and publish
shotlist captions status
shotlist compose
shotlist verify
shotlist publish
shotlist publish --apply
publish on its own prints the plan and changes nothing. --apply carries it out. See Captions and composing, Verifying output and App Store Connect.
Where things live
Everything Shotlist needs sits in your app’s repo as plain files, so a release’s screenshots can be reviewed in the same pull request as the code that changed them.
MyApp/ the folder that holds your Xcode project
MyApp.xcodeproj
MyApp/Screenshots.swift your scenes, in the app target, inside #if DEBUG
Shotlist/
shotlist.json app, slots, locales, appearances, sequence
Captions.xcstrings a caption and subtitle per scene, per locale
Fixtures/ HTTP fixtures: shared/ and <locale>/
Templates/ your own templates and background images
published.json what Shotlist has put live in App Store Connect
.gitignore leaves out Output/
Output/ not committed
raw/<locale>/<slot>/NN-<scene>-<appearance>.png
previous/raw/… the last run's captures, for before and after
composed/<locale>/<slot>-NN.png
creative/<locale>/header.png, search.png, …
manifest.json
run.log
.shotlist/ builds and scratch files; ignores itself
The rest of this guide
| Page | Read it when |
|---|---|
| Installing Shotlist | You’re installing the app or the CLI, or activating a licence. |
| Setting up a project | You’re adding Shotlist to an app for the first time. |
| Writing scenes | You’re deciding how to reach each screen. |
| Readiness and fixtures | Your app loads data, images or anything asynchronous. |
| Making runs identical | Screenshots change between runs when the app hasn’t. |
| Configuration | You need the exact meaning of a shotlist.json key, or the slot sizes. |
| Capturing | You want to know what a run does, or recover from a failure. |
| iPhone Duo | You capture either Duo display. |
| Captions and composing | You’re writing captions, translating them, or changing templates. |
| Verifying output | You want to know what verify checks. |
| App Store Connect | You’re uploading. |
| The Mac app | You prefer a window to a terminal. |
| CI and automation | You run Shotlist from a script, CI or an agent. |
| Subscription | You want to know what’s free, what’s paid and how licences work. |
| Troubleshooting | Something failed and the message names it. |
| FAQ | You have a quick question. |