Shotlist guide
Configuration
Every key in Shotlist/shotlist.json with its type, default and an example, the slot table with simulators, sizes and App Store Connect display types, and the locale mapping.
Configuration
A project is configured in Shotlist/shotlist.json. shotlist init writes one to start from. Shotlist reads it at the start of every command and refuses to run if it isn’t valid, naming the problem.
Shotlist ignores keys it doesn’t know, so a misspelt key is silently left out. If a setting seems to have no effect, check its spelling against this page.
A complete example
{
"version": 1,
"app": {
"project": "MyApp.xcodeproj",
"scheme": "MyApp",
"bundleID": "com.example.myapp",
"ascAppID": "1234567890"
},
"slots": ["iphone-6.9", "iphone-6.3", "ipad-13", "iphone-duo-outer", "iphone-duo-inner"],
"locales": ["en-GB", "en-US", "de-DE", "fr-FR"],
"appearances": ["light"],
"fixedDate": "2026-06-14T10:30:00Z",
"timeZone": "Europe/London",
"statusBar": { "time": "9:41", "battery": 100 },
"sequence": [
{ "scene": "library", "template": "caption-top" },
{ "scene": "book", "template": "caption-top" },
{ "scene": "stats", "template": "full-bleed", "appearance": "dark" },
{ "scene": "planner", "template": "split", "with": "library" }
],
"capture": { "concurrency": 2, "sceneTimeout": 20, "duoInnerOrientation": "landscape" },
"creative": {
"header": { "template": "header-brand", "localised": true },
"search": { "scene": "library", "template": "search-hero" },
"universal": true
}
}
Top-level keys
| Key | Type | Required | Default | Meaning |
|---|---|---|---|---|
version |
number | yes | The file format. Must be 1. |
|
app |
object | yes | The app to build and where it lives in App Store Connect. See app. | |
slots |
array of strings | yes | The screenshot sizes to capture. See Slots. | |
locales |
array of strings | yes | The locales to capture, such as "en-GB" and "de-DE". The app must declare each one. |
|
appearances |
array of strings | yes | "light", "dark" or both. |
|
fixedDate |
string | no | "2026-06-14T10:30:00Z" |
The run’s “now”, as an ISO 8601 date with a time zone. Reaches scenes as context.now and the app as Shotlist.now. |
timeZone |
string | no | your Mac’s time zone | The app’s time zone, as an IANA identifier such as "Europe/London". Set it, so dates render the same on any Mac. |
statusBar |
object | no | { "time": "9:41", "battery": 100 } |
The status bar to show. If you give it, give both keys. |
sequence |
array of objects | yes | The scenes to capture, in product-page order. 1 to 10 entries. See sequence. | |
capture |
object | no | Capture settings. See capture. | |
creative |
object | no | Product page header and search results art. Leave it out to make none. See creative. |
How the keys multiply
A run captures every combination of slot, locale and scene, in every appearance unless a sequence entry names its own:
cells = slots × locales × (scenes in each appearance)
Two slots, four locales and five scenes in one appearance make 40 cells. Adding "dark" to appearances doubles that, except for entries that name an appearance.
app
| Key | Type | Required | Default | Meaning |
|---|---|---|---|---|
project |
string | one of these two | The .xcodeproj, relative to the folder that holds Shotlist/. |
|
workspace |
string | one of these two | The .xcworkspace, relative to the same folder. Used instead of project when both are given. |
|
scheme |
string | yes | The scheme to build. It must build the app for the iOS Simulator. | |
bundleID |
string | yes | The app’s bundle identifier. Shotlist finds the built app by it, and finds the app in App Store Connect by it when there’s no ascAppID. |
|
ascAppID |
string | no | looked up by bundleID |
The app’s Apple ID in App Store Connect (App Information › Apple ID). "0000000000" counts as unset. |
configuration |
string | no | "Debug" |
The build configuration. It must define DEBUG, or ShotlistKit compiles to nothing. |
derivedData |
string | no | .shotlist/DerivedData |
A DerivedData folder to build into, relative to the folder that holds Shotlist/ or absolute. Point it at one that already holds a compiled SDK to save build time. |
ascLocales |
object | no | the built-in mapping | Shotlist locale to App Store Connect locale, where the default mapping doesn’t fit. |
Paths are relative to the folder that holds Shotlist/ (the --project-dir), and may climb out of it: "project": "../MyApp.xcodeproj" works.
sequence
Each entry is one screenshot position on the product page, in order.
| Key | Type | Required | Default | Meaning |
|---|---|---|---|---|
scene |
string | yes | A scene id from your Screenshots.swift. |
|
template |
string | no | "caption-top" |
The template that composes it. A built-in or one in Shotlist/Templates/. See Captions and composing. |
appearance |
string | no | every appearance in appearances |
Captures this entry in one appearance only, "light" or "dark", and composes it in that appearance. |
with |
string | no | For the split template: the second scene shown beside this one. It must also be in sequence, so it’s captured. |
App Store Connect takes 1 to 10 screenshots per size and locale, so sequence has 1 to 10 entries.
Each entry’s position sets the NN in file names: the third entry is 03-<scene>-<appearance>.png in raw captures.
Which appearance is composed. Compose makes one screenshot per entry. For an entry with its own appearance, that’s the appearance it uses. For the others, it uses the first in appearances. Capturing both appearances is useful for review; to publish a dark screenshot, give that entry "appearance": "dark".
capture
| Key | Type | Default | Meaning |
|---|---|---|---|
concurrency |
number | 2 |
How many simulators run at once. At least 1. Each simulator works through its locales one after another. --concurrency overrides it for one run. |
sceneTimeout |
number | 20 |
Seconds a scene has to become ready, in whole seconds. See Timeouts. |
duoInnerOrientation |
string | "landscape" |
The iPhone Duo’s inner display: "landscape" (2853 × 2007, how it opens) or "portrait" (2007 × 2853, rotated through Device Hub). |
Concurrency is per simulator, not per slot: both iPhone Duo slots share one simulator. With slots iphone-6.9, iphone-6.3 and both Duo displays, there are three simulators, and the default runs two at a time. More simulators at once needs more memory; on a Mac with plenty, 3 or 4 is fine.
creative
Product page header and search results art. Leave creative out to make none. See Captions and composing.
| Key | Type | Default | Meaning |
|---|---|---|---|
header |
object | none | Make header art. |
header.template |
string | "header-brand" |
The template. |
header.localised |
boolean | true |
One header per locale. With false, one header from the first locale’s captions, written to creative/shared/ and placed on every localisation. |
header.scene |
string | none | A scene whose capture appears in the header. Header art has no screenshot by default, because the screenshots sit right below it. |
search |
object | none | Make search results art. |
search.scene |
string | required with search |
The scene whose capture appears in the art. It must be in sequence. |
search.template |
string | "search-hero" |
The template. |
search.slot |
string | the first iphone-6.x slot, else the first slot |
The slot whose capture is used. |
universal |
boolean | true |
Also render the 5244 × 2950 (16:9) alternative of each. |
Slots
A slot is one App Store screenshot size and the simulator that produces it natively, so captures are never scaled.
| Slot | Simulator | Runtime (Xcode 27.1) | Captured size | Sizes Apple accepts | App Store Connect display type |
|---|---|---|---|---|---|
iphone-6.9 |
iPhone 17 Pro Max | iOS 27.0 | 1320 × 2868 | 1320 × 2868, 1290 × 2796, 1260 × 2736 | APP_IPHONE_67 |
iphone-6.3 |
iPhone 17 Pro | iOS 27.0 | 1206 × 2622 | 1206 × 2622, 1179 × 2556 | APP_IPHONE_61 |
ipad-13 |
iPad Pro 13-inch (M5) | iOS 27.0 | 2064 × 2752 | 2064 × 2752, 2048 × 2732 | APP_IPAD_PRO_3GEN_129 |
iphone-duo-outer |
iPhone Duo, outer display | iOS 27.1 | 1398 × 2034 | 1398 × 2034 | APP_IPHONE_DUO, undocumented: uploaded only with --experimental-duo |
iphone-duo-inner |
iPhone Duo, inner display | iOS 27.1 | 2853 × 2007 (landscape) or 2007 × 2853 (portrait) | 2007 × 2853 | none: exported for upload by hand |
Sizes are portrait; Apple accepts each in landscape too. Sizes were checked against Apple’s screenshot specifications on 31 October 2026.
iphone-6.3is the size Apple currently requires for iPhone (“iPhone with Dynamic Island, medium display”).iphone-6.9is accepted, not required. Capturing it costs little and gives the large-display class its own screenshots.ipad-13is required if the app runs on iPad.- The iPhone Duo’s two sizes are required from April 2027 for apps built with the iOS 27.1 SDK or later. See iPhone Duo.
When a simulator boots, Shotlist checks its framebuffer against this table. If a future simulator produces a size the slot doesn’t accept, the run fails rather than capturing the wrong size.
Each slot’s simulator runs on the newest installed iOS runtime that supports its device.
App Store Connect locales
Shotlist locales are BCP 47 codes such as en-GB. Most are App Store Connect’s codes too. These are mapped by default:
| Shotlist locale | App Store Connect locale |
|---|---|
nb-NO, nn-NO, or any nb-… or nn-… |
no |
Catalan, Czech, Danish, Greek, Finnish, Hebrew, Hindi, Croatian, Hungarian, Indonesian, Italian, Japanese, Korean, Malay, Polish, Romanian, Russian, Slovak, Swedish, Thai, Turkish, Ukrainian, Vietnamese (ca, cs, da, el, fi, he, hi, hr, hu, id, it, ja, ko, ms, pl, ro, ru, sk, sv, th, tr, uk, vi) with any region |
The language alone, such as sv-SE → sv |
| Anything else | Unchanged, such as en-GB, de-DE, fr-FR, pt-BR |
Where that doesn’t match your listing, map it in app.ascLocales:
"ascLocales": { "zh-CN": "zh-Hans", "zh-TW": "zh-Hant" }
The publish plan shows the App Store Connect locale for every row, so you can check the mapping before uploading.
Validation
Shotlist checks the file before every command. These are the errors it can report:
| Error | Fix |
|---|---|
no …/Shotlist/shotlist.json; run `shotlist init` first |
Run shotlist init, or pass --project-dir pointing at the folder that holds Shotlist/. |
…/shotlist.json isn't valid: … |
The JSON doesn’t parse, or a required key is missing or has the wrong type. The rest of the message names it. |
shotlist.json version 2 isn't supported; this Shotlist reads version 1 |
Set "version": 1, or update Shotlist. |
app needs a "project" or "workspace" |
Add one. |
unknown slot '…'; known slots: … |
Use a slot id from the table. |
unknown appearance '…'; use light or dark |
In appearances or a sequence entry. |
sequence is empty |
Add at least one entry. |
sequence has 11 scenes; App Store Connect takes at most 10 per size |
Remove entries. |
fixedDate '…' isn't an ISO 8601 date such as 2026-06-14T10:30:00Z |
Include the time and a time zone (Z or an offset). |