The Screenshot Problem

Every time CCFish ships a new feature or redesigns a screen, we need updated App Store screenshots. For a single iOS release, that means:

- **6.5-inch iPhone screenshots** (6 screens)

- **5.5-inch iPhone screenshots** (6 screens, re-cropped)

- **12.9-inch iPad screenshots** (6 screens, re-cropped)

- **Localized text** for English, Vietnamese, Japanese, Korean

That's 72 individual screenshot files per release if we do it manually. At one release every two weeks, the manual workflow was consuming an entire design sprint.

Enter automation.

The Pipeline Architecture

We built a CI/CD pipeline that generates all 72 screenshots from a single source of truth:

```

CCFish Game Client (Cocos Creator build)

→ iPad Simulator Screenshot Capture (Xcode CLI)

→ Image Processor (Sharp on Node.js)

→ Localization Layer (L10n JSON)

→ Export to App Store Connect (Fastlane deliver)

```

Step 1: Automated Screenshot Capture

Instead of manually swiping through the game, we script the screenshot capture using Xcode's `simctl` and a headless interaction layer:

```bash

#!/bin/bash

Capture all screenshots for a given locale

DEVICE="iPad Pro (12.9-inch) (6th generation)"

LOCALE=$1

xcrun simctl boot "$DEVICE"

xcrun simctl bootstatus "$DEVICE" -b

Launch CCFish with locale override

xcrun simctl launch booted com.ccfish.game --AppleLocale "$LOCALE"

sleep 3 # Wait for initial load

Trigger screenshot scenes via URL scheme

for scene in "main-menu" "gameplay" "shop" "settings" "leaderboard" "achievements"; do

xcrun simctl openurl booted "ccfish://screenshot/$scene"

sleep 1

xcrun simctl io booted screenshot "screenshots/$LOCALE/$scene.png"

done

echo "Captured $LOCALE screenshots"

```

Step 2: Image Processing Pipeline

Raw simulator screenshots need cropping, resizing for different device targets, and overlay application:

```javascript

const sharp = require('sharp');

async function processScreenshot(source, devices) {

const base = await sharp(source);

const results = [];

for (const device of devices) {

const resized = base

.clone()

.resize(device.width, device.height, {

fit: 'contain',

background: { r: 0, g: 0, b: 0, alpha: 1 }

});

// Overlay device frame + localized text

const framed = await resized

.composite([

{ input: `frames/${device.name}.png`, top: 0, left: 0 },

{ input: `overlays/${device.locale}.png`, top: 20, left: 20 }

])

.toFile(`output/${device.name}_${device.locale}.png`);

results.push(framed);

}

return results;

}

```

Step 3: Localization Layer

All text overlays come from a single JSON file per locale:

```json

{

"en": {

"main-menu": { "title": "Dive In", "subtitle": "Match and Collect!" },

"gameplay": { "title": "Swish to Catch", "subtitle": "Easy to Learn, Fun to Master" }

},

"vi": {

"main-menu": { "title": "Khám Phá", "subtitle": "Chơi và Sưu Tập!" },

"gameplay": { "title": "Vuốt để Bắt", "subtitle": "Dễ Học, Khó Bỏ" }

}

}

```

Step 4: Fastlane Upload

The final step uploads everything to App Store Connect:

```ruby

lane :upload_screenshots do

Dir.glob('output/*.png').each do |screenshot|

deliver(

screenshots: [screenshot],

skip_metadata: true,

skip_app_version_update: true

)

end

end

```

CI/CD Integration

We run this pipeline in GitHub Actions whenever a release branch is created:

```yaml

name: Generate Screenshots

on:

create:

branches: [release/**]

jobs:

screenshots:

runs-on: macos-14

steps:

- uses: actions/checkout@v4

- run: brew install imagemagick

- run: ./scripts/capture-screenshots.sh

- run: node scripts/process-images.js

- run: bundle exec fastlane upload_screenshots

```

Results

| Metric | Before | After |

|--------|--------|-------|

| Time per release | 4-6 hours | 12 minutes |

| Screenshot errors | ~5% (human cropping mistakes) | <1% |

| Languages supported | 2 | 4 |

| Cost per release | $250 (designer time) | $0.08 (GitHub Actions) |

Key Takeaways

1. **Simulator screenshots with URL scheme triggers** are surprisingly reliable. We added a `ccfish://screenshot/<scene>` handler in Cocos Creator that navigates to the right in-game state and hides UI overlays.

2. **Sharp is a beast.** Processing 72 screenshots takes under 30 seconds on a GitHub Actions runner.

3. **Separate capture from processing.** Raw captures go to one directory, processed outputs to another. If the overlay design changes, we reprocess without re-capturing.

4. **Locale-driven builds are worth the effort.** The Cocos Creator build accepts an `--AppleLocale` argument that swaps all string assets. This was a one-time engineering investment that every subsequent release benefits from.

If you're maintaining a mobile game and dreading screenshot updates, this pipeline pays for itself in the first release. The code is straightforward, the tools are free, and your designer will thank you.