Skip to content
Development

Captioning App Store screenshots without breaking Apple's exact-dimension rule

By Victor Da Luz
iosapp-storepythondev-logdeep-cut-atlas

I’ve had four raw screenshots sitting in the repo for Deep Cut Atlas since the last recapture: Discover, Playlist, History, Settings. No captions, just the app. That’s fine, Apple’s own docs even say to prioritize clean UI captures. But every bit of ASO research I could find says the same thing back: bold, short, benefit-focused captions on your screenshots can lift install conversion meaningfully, because most people decide whether to install from the thumbnail in search results, before they ever open your product page.

So I picked up an old low-priority backlog item: build a captioned screenshot set. Should be simple. It was not simple, and the two ways it went sideways taught me more than the actual image compositing did.

The first wrong idea: overlay text on the screen

My first instinct was to draw a headline directly onto the existing screenshot, maybe with a dark scrim behind it so the text stays legible over whatever UI happens to be there. Before writing any code I ran the plan past a second opinion, and it caught the problem immediately: that’s the amateur look. Covering part of a real, meaningful screen (the status bar, a nav title, actual content near the top) is exactly what polished App Store listings avoid. The correct technique is to reframe, not overlay: scale the screenshot down, put it inside a dedicated caption band, let the UI stay fully intact.

That’s a real constraint fight, though, because Apple’s screenshot requirements are unforgiving. App Store Connect wants an exact pixel size per device class (1320x2868 for the 6.9-inch iPhone lineup right now) with zero tolerance, and no alpha channel. A raw device screenshot at that exact resolution has no spare canvas anywhere. So “add a caption band” really means: build a new canvas at the exact required size, put a solid band across the top holding the headline, then scale the original screenshot down to fit underneath it, rounded corners and a soft shadow for a bit of polish. Flatten to opaque RGB before saving, assert the output dimensions match before calling it done.

I built that as a small Python/Pillow script, generated one draft, and got a real look at it before batching the rest. Good so far.

The second, much worse mistake: I overwrote the wrong files

I had two active issues that both cared about the same directory. This one wanted to build a captioned set. The other - urgent, resubmission-blocking - needed those exact same raw screenshot files uploaded to App Store Connect as-is. I generated the captioned batch and copied it straight over the raw files in place.

The reaction that came back, verbatim: “why would you overwrite the existing clean ones? we need them for the submission!!!!!!!”

Fair and correct. I’d taken files that another open, urgent task explicitly depended on, and replaced them without stopping to check. The fix itself was easy, nothing had been committed yet, so git checkout -- on the affected paths restored everything instantly. But the mistake shouldn’t have happened at all. This repo already had a working pattern for exactly this situation sitting right next to the files I overwrote: a resized/ subdirectory holding a derived variant of the base screenshots, never touching the originals. I should have followed that precedent from the start instead of reaching for “just copy the new version over the old one.”

The corrected version writes captioned output to a new captioned/ sibling directory. Raw files never move.

Smaller lesson: don’t assume “looks off” without asking what

After regenerating everything the second time, I got pushback again: “why did you change the format? The sample was better.” I went and diffed the regenerated file against the originally-approved draft, pixel for pixel. Identical. Turned out the actual complaint was about a second, smaller screenshot set (406x890 marketing images, a different size entirely) that I’d also captioned using the same script with proportionally-scaled constants. Rather than guess at what “the format” meant a second time, I asked a narrower follow-up question. The answer: skip that set entirely, keep only the App Store Connect one. Much faster than another round of guessing and being wrong again.

What shipped

Four captioned App Store Connect screenshots, exact 1320x2868, RGB, no alpha, sitting in a new captioned/ subdirectory next to the untouched originals in the repo. They are not yet uploaded anywhere, and the live App Store Connect listing still shows the older, stale screenshot set until a future submission pass deals with that (a separate, already-tracked issue). Whether these captioned images end up replacing the raw set in that upload is a decision for that future pass, not something this issue resolved.

The actual takeaway

The technically interesting part of this issue (reframe-composite instead of overlay, exact-dimension constraints, proportional scaling across canvas sizes) took maybe twenty minutes to get right, most of that with a second pair of eyes on the plan before a line of code got written. The part that actually cost time and trust was a file-path decision made on autopilot: “just put the new thing where the old thing was.” Generated assets that another task depends on deserve the same care as a destructive infra command, not less just because they’re PNGs instead of a database.

Related reading