Profile
Back to NewsBack
GitHub Trending 34 min
Reader Mode
keremerkan/ascelerate: A command-line tool for building, archiving, and publishing apps to the App Store — from Xcode archive to App Review submission. Built with Swift on the App Store Connect API.

keremerkan/ascelerate: A command-line tool for building, archiving, and publishing apps to the App Store — from Xcode archive to App Review submission. Built with Swift on the App Store Connect API.

ascelerate icon

ascelerate — A Swift CLI for App Store Connect

A command-line tool for building, archiving, and publishing apps to the App Store — from Xcode archive to App Review submission. Built with Swift on the App Store Connect API.

Note: Covers the core app release workflow: archiving, uploading builds, managing versions and localizations, screenshots, review submission, provisioning (devices, certificates, bundle IDs, profiles), and full management of in-app purchases and subscriptions. Also handles customer reviews and developer responses, in-app events, custom product pages, and Sales/Finance/Analytics report downloads. Most provisioning commands support interactive mode — run without arguments to get guided prompts.
Full documentation: ascelerate.dev

Requirements

  • macOS 13+
  • Swift 6.0+ (only for building from source)

Installation

Homebrew

brew tap keremerkan/tap
brew trust keremerkan/tap
brew install ascelerate

Since Homebrew 6.0, third-party taps must be explicitly trusted before their code runs. The brew trust step above approves the tap; alternatively, a fully-qualified install (brew install keremerkan/tap/ascelerate) prompts you to trust it interactively.

The tap provides a pre-built binary for Apple Silicon Macs, so installation is instant.

Install script

curl -sSL https://raw.githubusercontent.com/keremerkan/ascelerate/main/install.sh | bash

Downloads the latest release, installs to /usr/local/bin, and removes the quarantine attribute automatically. Apple Silicon only.

Download manually

Download the latest release from GitHub Releases:

curl -L https://github.com/keremerkan/ascelerate/releases/latest/download/ascelerate-macos-arm64.tar.gz -o ascelerate.tar.gz
tar xzf ascelerate.tar.gz
mv ascelerate /usr/local/bin/

Since the binary is not signed or notarized, macOS will quarantine it on first download. Remove the quarantine attribute:

xattr -d com.apple.quarantine /usr/local/bin/ascelerate
Note: Pre-built binaries are provided for Apple Silicon (arm64) only. Intel Mac users should build from source.

Build from source

git clone https://github.com/keremerkan/ascelerate.git
cd ascelerate
swift build -c release
strip .build/release/ascelerate
cp .build/release/ascelerate /usr/local/bin/
Note: The release build takes several minutes because it compiles the App Store Connect client that ascelerate generates from Apple's OpenAPI specification. strip removes debug symbols, reducing the binary from ~156 MB to ~41 MB.

Shell completions

Set up tab completion for subcommands, options, and flags (supports zsh and bash):

ascelerate install-completions

This detects your shell and configures everything automatically. Restart your shell or open a new tab to activate.

AI coding skill

ascelerate ships with a skill file that gives AI coding agents (Claude Code, Grok Build, Cursor, Windsurf, GitHub Copilot) full knowledge of all commands, JSON formats, and workflows.

Via the binary (detects your installed agents):

ascelerate install-skill          # install/update for every detected agent
ascelerate install-skill --all    # include all supported agents (e.g. Copilot)

It auto-detects Claude Code, Grok Build (which reads the Claude Code skill path natively), Cursor, and Windsurf (and GitHub Copilot with --all), installs/updates the skill for each, and checks for outdated skills on each run to prompt you after upgrades.

Via npx (any AI coding agent):

npx ascelerate-skill

This presents an interactive menu to select your agent and installs the skill to the appropriate directory. The skill file is fetched from GitHub, so it's always up to date. Use npx ascelerate-skill --uninstall to remove it.

Setup

1. Create an API Key

Go to App Store Connect > Users and Access > Integrations > App Store Connect API and generate a new key. Download the .p8 private key file.

2. Configure

ascelerate configure

This will prompt for your Key ID, Issuer ID, and the path to your .p8 file, plus an optional vendor number (only needed for reports sales/reports finance). The private key is copied into ~/.ascelerate/ with strict file permissions (owner-only access).

Usage

Aliases

Instead of typing full bundle IDs every time, you can create short aliases:

# Add an alias (interactive app picker)
ascelerate alias add myapp

Now use the alias anywhere you'd use a bundle ID

ascelerate apps info myapp ascelerate apps versions myapp ascelerate apps localizations view myapp

List all aliases

ascelerate alias list

Remove an alias

ascelerate alias remove myapp

Aliases are stored in ~/.ascelerate/aliases.json. Any argument that doesn't contain a dot is looked up as an alias — real bundle IDs (which always contain dots) work unchanged.

JSON output

Read commands support --json for machine-readable output, ready for jq, scripts, and AI agents:

ascelerate apps list --json
ascelerate apps info <bundle-id> --json
ascelerate apps versions <bundle-id> --json
ascelerate apps review preflight <bundle-id> --json
ascelerate apps review status <bundle-id> --json
ascelerate builds list --bundle-id <bundle-id> --json
ascelerate testflight builds <bundle-id> --json
ascelerate testflight status <bundle-id> --json
ascelerate reviews list <bundle-id> --json
ascelerate reviews info <review-id> --json
ascelerate iap list <bundle-id> --json
ascelerate iap info <bundle-id> <product-id> --json
ascelerate iap pricing show <bundle-id> <product-id> --json
ascelerate sub groups <bundle-id> --json
ascelerate sub list <bundle-id> --json
ascelerate sub info <bundle-id> <product-id> --json
ascelerate sub pricing show <bundle-id> <product-id> --json
ascelerate rate-limit --json

List commands emit a top-level JSON array, detail commands a single object. Enum values are raw API constants (WAITING_FOR_REVIEW, IOS), dates are ISO 8601, every resource carries its id, null fields are omitted, and empty results emit [] — never prose. Warnings become booleans (iap info/sub info report "hasPricing": false). --json implies non-interactive mode, and errors go to stderr so stdout is always valid JSON.

Apps

# List all apps
ascelerate apps list

Show app details

ascelerate apps info <bundle-id>

List App Store versions

ascelerate apps versions <bundle-id>

Create a new version

ascelerate apps create-version <bundle-id> <version-string> ascelerate apps create-version <bundle-id> 2.1.0 --platform ios --release-type manual

View or update the copyright notice

ascelerate apps copyright <bundle-id> ascelerate apps copyright <bundle-id> --set "2026 Your Name" --version 2.1.0 --platform macos

Check review submission status

ascelerate apps review status <bundle-id> ascelerate apps review status <bundle-id> --version 2.1.0

Submit for review

ascelerate apps review submit <bundle-id> ascelerate apps review submit <bundle-id> --version 2.1.0 ascelerate apps review submit <bundle-id> --platform macos

Resolve rejected review items (after fixing issues and replying in Resolution Center)

ascelerate apps review resolve-issues <bundle-id>

Cancel an active review submission

ascelerate apps review cancel-submission <bundle-id>

View or update App Review Information (contact, demo account, notes)

ascelerate apps review info <bundle-id> ascelerate apps review info <bundle-id> --contact-email [email protected] --demo-account-name reviewer --demo-account-password "hunter2" --demo-account-required true --notes "Steps to test…"

App Review attachment files (demo videos, docs, etc.)

ascelerate apps review attachment list <bundle-id> ascelerate apps review attachment upload <bundle-id> demo.mp4 ascelerate apps review attachment delete <attachment-id>

For universal-purchase apps (one App Store record spanning iOS, macOS, tvOS, and/or visionOS), the same version string can exist once per platform. create-version and review submit default to iOS — pass --platform macos (or tvos, visionos) to target another platform. All other version-scoped commands (localizations, media, build attach, review preflight/info/attachments/resolve-issues/cancel-submission, phased release, release, routing coverage) accept an optional --platform as well; without it they prompt whenever a version (or active review submission) matches more than one platform — and refuse with a hint instead of prompting under --yes.

Pre-submission preflight checks

Before submitting for review, run preflight to verify that all required fields are filled in across every locale:

# Check the latest editable version
ascelerate apps review preflight <bundle-id>

Check a specific version

ascelerate apps review preflight <bundle-id> --version 2.1.0

The command checks version state, build attachment, and then goes through each locale to verify localization fields (description, what's new, keywords, support URL), app info fields (name, subtitle, privacy policy URL), and screenshots. Results are grouped by locale with colored pass/fail indicators:

Preflight checks for MyApp v2.1.0 (Prepare for Submission)

Check Status ────────────────────────────────────────────────────────────────── Version state ✓ Prepare for Submission Build attached ✓ Build 42

en-US (English (United States)) App info ✓ All fields filled Localizations ✓ All fields filled Screenshots ✓ 2 sets, 10 screenshots

de-DE (German (Germany)) App info ✗ Missing: Privacy Policy URL Localizations ✗ Missing: What's New Screenshots ✗ No screenshots ────────────────────────────────────────────────────────────────── Result: 5 passed, 3 failed

The What's New check is skipped when the app has no previously released version — that field only exists for updates, not for a first release.

Exits with a non-zero status when any check fails, making it suitable for CI pipelines and workflow files. With --json, it emits a structured report — a passed boolean plus one entry per check — while keeping the same exit-code behavior.

Build Management

# Interactively select and attach a build to a version
ascelerate apps build attach <bundle-id>
ascelerate apps build attach <bundle-id> --version 2.1.0

Attach the most recent build automatically

ascelerate apps build attach-latest <bundle-id> ascelerate apps build attach-latest <bundle-id> --platform macos

Remove the attached build from a version

ascelerate apps build detach <bundle-id>

Build lookups are platform-aware: on universal-purchase apps, iOS and macOS builds can share build numbers, so the attach commands only consider builds matching the target version's platform.

Phased Release

# View phased release status
ascelerate apps phased-release <bundle-id>

Enable phased release (starts inactive, activates when version goes live)

ascelerate apps phased-release <bundle-id> --enable

Pause, resume, or complete a phased release

ascelerate apps phased-release <bundle-id> --pause ascelerate apps phased-release <bundle-id> --resume ascelerate apps phased-release <bundle-id> --complete

Remove phased release entirely

ascelerate apps phased-release <bundle-id> --disable

Manual Release

When a version's release option is set to manual, the approved version sits in Pending Developer Release until you release it:

# Release the version that is pending developer release
ascelerate apps release <bundle-id>

Target a specific version or platform

ascelerate apps release <bundle-id> --version 2.1.0 --platform macos

Age Rating

# View age rating declaration
ascelerate apps app-info age-rating <bundle-id>

Export age rating to JSON

ascelerate apps app-info age-rating export <bundle-id>

Update age ratings from a JSON file

ascelerate apps app-info age-rating import <bundle-id> --file age-rating.json

The JSON file uses the same field names as the API. Only fields present in the file are updated:

{
  "isAdvertising": false,
  "isUserGeneratedContent": true,
  "violenceCartoonOrFantasy": "INFREQUENT_OR_MILD",
  "alcoholTobaccoOrDrugUseOrReferences": "NONE"
}

Intensity fields accept: NONE, INFREQUENT_OR_MILD, FREQUENT_OR_INTENSE. Boolean fields accept true/false.

Routing App Coverage

# View current routing coverage status
ascelerate apps routing-coverage <bundle-id>

Upload a .geojson file

ascelerate apps routing-coverage <bundle-id> --file coverage.geojson

Localizations

# View localizations (latest version by default)
ascelerate apps localizations view <bundle-id>
ascelerate apps localizations view <bundle-id> --version 2.1.0 --locale en-US

Export localizations to JSON

ascelerate apps localizations export <bundle-id> ascelerate apps localizations export <bundle-id> --version 2.1.0 --output my-localizations.json

Update a single locale

ascelerate apps localizations update <bundle-id> --whats-new "Bug fixes" --locale en-US

Bulk update from JSON file

ascelerate apps localizations import <bundle-id> --file localizations.json

The JSON format for export and bulk update:

{
  "en-US": {
    "description": "My app description.\n\nSecond paragraph.",
    "whatsNew": "- Bug fixes\n- New dark mode",
    "keywords": "productivity,tools,utility",
    "promotionalText": "Try our new features!",
    "marketingURL": "https://example.com",
    "supportURL": "https://example.com/support"
  },
  "de-DE": {
    "whatsNew": "- Fehlerbehebungen\n- Neuer Dunkelmodus"
  }
}

Only fields present in the JSON are updated -- omitted fields are left unchanged.

Screenshots & App Previews

# Download all screenshots and preview videos
ascelerate apps media download <bundle-id>
ascelerate apps media download <bundle-id> --folder my-media/ --version 2.1.0
ascelerate apps media download <bundle-id> --locale en-US,tr

Upload screenshots and preview videos from a folder

ascelerate apps media upload <bundle-id> media/

Upload from an archive (zip, tar, tar.gz supported)

ascelerate apps media upload <bundle-id> screenshots.zip

Upload to a specific version

ascelerate apps media upload <bundle-id> media/ --version 2.1.0

Replace existing media in matching sets before uploading

ascelerate apps media upload <bundle-id> media/ --replace

Interactive mode: pick a folder or archive from the current directory

ascelerate apps media upload <bundle-id>

When the folder argument is omitted, the command lists all subdirectories and archive files in the current directory as a numbered picker. Archives (zip, tar, tar.gz) are extracted automatically before upload.

Organize your media folder with locale and display type subfolders:

media/
├── en-US/
│   ├── APP_IPHONE_67/
│   │   ├── 01_home.png
│   │   ├── 02_settings.png
│   │   └── preview.mp4
│   └── APP_IPAD_PRO_3GEN_129/
│       └── 01_home.png
└── de-DE/
    └── APP_IPHONE_67/
        ├── 01_home.png
        └── 02_settings.png
  • Level 1: Locale (e.g. en-US, de-DE, ja)
  • Level 2: Display type folder name (see table below)
  • Level 3: Media files -- images (.png, .jpg, .jpeg) become screenshots, videos (.mp4, .mov) become app previews
  • Files are uploaded in alphabetical order by filename
  • Unsupported files are skipped with a warning

Display types

App Store Connect requires APP_IPHONE_67 screenshots for iPhone apps and APP_IPAD_PRO_3GEN_129 screenshots for iPad apps. All other display types are optional.

| Folder name | Device | Screenshots | Previews | |---|---|---|---| | APP_IPHONE_67 | iPhone 6.7" (iPhone 17 Pro Max, 16 Pro Max, 15 Pro Max) | Required | Yes | | APP_IPAD_PRO_3GEN_129 | iPad Pro 12.9" (3rd gen+) | Required | Yes |

All optional display types

| Folder name | Device | Screenshots | Previews | |---|---|---|---| | APP_IPHONE_61 | iPhone 6.1" (iPhone 17 Pro, 16 Pro, 15 Pro) | Yes | Yes | | APP_IPHONE_65 | iPhone 6.5" (iPhone 11 Pro Max, XS Max) | Yes | Yes | | APP_IPHONE_58 | iPhone 5.8" (iPhone 11 Pro, X, XS) | Yes | Yes | | APP_IPHONE_55 | iPhone 5.5" (iPhone 8 Plus, 7 Plus, 6s Plus) | Yes | Yes | | APP_IPHONE_47 | iPhone 4.7" (iPhone SE 3rd gen, 8, 7, 6s) | Yes | Yes | | APP_IPHONE_40 | iPhone 4" (iPhone SE 1st gen, 5s, 5c) | Yes | Yes | | APP_IPHONE_35 | iPhone 3.5" (iPhone 4s and earlier) | Yes | Yes | | APP_IPHONE_DUO | iPhone Duo (uploaded through the asset library, see below) | Yes | No | | PRODUCT_PAGE_HEADER | Product page header image (asset library, see below) | Yes | No | | APP_STORE_SEARCH_RESULTS | App Store search results image (asset library, see below) | Yes | No | | APP_IPAD_PRO_3GEN_11 | iPad Pro 11" | Yes | Yes | | APP_IPAD_PRO_129 | iPad Pro 12.9" (1st/2nd gen) | Yes | Yes | | APP_IPAD_105 | iPad 10.5" (iPad Air 3rd gen, iPad Pro 10.5") | Yes | Yes | | APP_IPAD_97 | iPad 9.7" (iPad 6th gen and earlier) | Yes | Yes | | APP_DESKTOP | Mac | Yes | Yes | | APP_APPLE_TV | Apple TV | Yes | Yes | | APP_APPLE_VISION_PRO | Apple Vision Pro | Yes | Yes | | APP_WATCH_ULTRA | Apple Watch Ultra | Yes | No | | APP_WATCH_SERIES_10 | Apple Watch Series 10 | Yes | No | | APP_WATCH_SERIES_7 | Apple Watch Series 7 | Yes | No | | APP_WATCH_SERIES_4 | Apple Watch Series 4 | Yes | No | | APP_WATCH_SERIES_3 | Apple Watch Series 3 | Yes | No | | IMESSAGE_APP_IPHONE_67 | iMessage iPhone 6.7" | Yes | No | | IMESSAGE_APP_IPHONE_61 | iMessage iPhone 6.1" | Yes | No | | IMESSAGE_APP_IPHONE_65 | iMessage iPhone 6.5" | Yes | No | | IMESSAGE_APP_IPHONE_58 | iMessage iPhone 5.8" | Yes | No | | IMESSAGE_APP_IPHONE_55 | iMessage iPhone 5.5" | Yes | No | | IMESSAGE_APP_IPHONE_47 | iMessage iPhone 4.7" | Yes | No | | IMESSAGE_APP_IPHONE_40 | iMessage iPhone 4" | Yes | No | | IMESSAGE_APP_IPAD_PRO_3GEN_129 | iMessage iPad Pro 12.9" (3rd gen+) | Yes | No | | IMESSAGE_APP_IPAD_PRO_3GEN_11 | iMessage iPad Pro 11" | Yes | No | | IMESSAGE_APP_IPAD_PRO_129 | iMessage iPad Pro 12.9" (1st/2nd gen) | Yes | No | | IMESSAGE_APP_IPAD_105 | iMessage iPad 10.5" | Yes | No | | IMESSAGE_APP_IPAD_97 | iMessage iPad 9.7" | Yes | No |

Note: Watch and iMessage display types support screenshots only -- video files in those folders are skipped with a warning. The --replace flag deletes all existing assets in each matching set before uploading new ones.
> App Store Connect has no screenshot set for iPhone Duo: files in an APP_IPHONE_DUO folder go to the app's asset library and are placed on the version localization in file order. Accepted sizes are 2853×2007 / 2007×2853 (inner display, unfolded) and 2034×1398 / 1398×2034 (cover display); other sizes are rejected before upload. A file that still fails after retries makes media upload place that locale's iPhone Duo screenshots again at the end of the run, in file order. media verify lists them with file names and states and, given the folder, flags locales whose iPhone Duo files or order differ; media download doesn't include them yet, and media prune never deletes them.
> PRODUCT_PAGE_HEADER and APP_STORE_SEARCH_RESULTS folders also go through the asset library, one image per locale each that isn't tied to a device class (the version shows it on iPhone, iPad and iPhone Duo alike), on every platform's versions: the product page header takes a PNG at 3840×1646 or 5244×2950, and the search results image a 3:2 JPG/PNG from 1920×1280 to 3840×2560 or a 5244×2950 PNG. Each slot also takes a video instead (header: 3840×1646; search results: 3:2 in the same size range; 5–30 s at 30 or 60 fps). Uploading replaces the locale's current image or video (the old one is removed after the new one is uploaded). An APP_IPHONE_DUO folder can hold app previews too (1920×886 or 886×1920, 15–30 s at 23–30 fps, with audio). Custom product pages take all three through product-pages media upload --display-type PRODUCT_PAGE_HEADER (or APP_STORE_SEARCH_RESULTS, APP_IPHONE_DUO). media download saves the library images; App Store Connect gives no download link for library videos.
> media download saves files in this same folder structure (defaults to -media/), so you can download, edit, and re-upload.

Using with app-store-screenshots

app-store-screenshots is a companion skill for AI coding agents that generates production-ready App Store screenshots. It creates a Next.js page that renders ad-style marketing layouts using framed device screenshots from ascelerate screenshot frame and exports them as a zip file ready for upload via ascelerate apps media upload:

en-US/APP_IPHONE_67/01_hero.png
en-US/APP_IPAD_PRO_3GEN_129/01_hero.png
de-DE/APP_IPHONE_67/01_hero.png

Install the skill for your AI coding agent:

npx skills add keremerkan/ascelerate

Upload the exported zip directly:

ascelerate apps media upload <bundle-id> screenshots.zip --replace

Verify and retry stuck media

Sometimes screenshots or previews get stuck in "processing" after upload. Use media verify to check the status of all media at once and optionally retry stuck items:

# Check status of all screenshots and previews
ascelerate apps media verify <bundle-id>

Check a specific version

ascelerate apps media verify <bundle-id> --version 2.1.0

Retry stuck items using local files from the media folder

ascelerate apps media verify <bundle-id> media/

Without --folder, the command shows a read-only status report. Sets where all items are complete show a compact one-liner; sets with stuck items expand to show each file and its state. With --folder, it prompts to retry stuck items by deleting them and re-uploading from the matching local files, preserving the original position order.

A screenshot also counts as stuck when it reads complete but its asset library placement is still processing; App Review refuses a version in that state ("Asset is being processed"), and retrying with the folder uploads it again.

Prune stale sets

--replace on upload only clears sets that match a local folder — server sets for screen sizes you no longer ship (say, an old 5.8-inch set) keep their outdated screenshots. media prune deletes the sets that have no matching local locale/display-type folder, after listing them with asset counts and confirming:

ascelerate apps media prune <bundle-id> media/
ascelerate apps media prune <bundle-id> media/ --version 2.1.0 --platform ios

Locales that have no local folder at all are skipped entirely — the command only prunes within locales the folder actually manages.

Remove asset library images

media remove takes one kind of asset library item off a version (the PRODUCT_PAGE_HEADER or APP_STORE_SEARCH_RESULTS image or video, or APP_IPHONE_DUO screenshots; --previews for Duo app previews), for the locales given with --locale or all of them, after listing what it found and asking:

ascelerate apps media remove <bundle-id> PRODUCT_PAGE_HEADER --locale en-US
ascelerate apps media remove <bundle-id> APP_IPHONE_DUO --locale en-US,tr --version 2.1.0

Clean up the asset library

Versions share the app's asset library images (a new version's screenshots are the previous version's images, and old versions keep theirs), so an image stays in use as long as any version, custom product page or event places it. When ascelerate removes a placement (media remove, media upload --replace, or replacing a header or search results image), it also deletes the image if nothing uses it any more and it never went through App Review. Earlier uploads can still have left unused images; media library counts the images and lists the unused ones, and --delete-unused deletes them after asking:

ascelerate apps media library <bundle-id>
ascelerate apps media library <bundle-id> --delete-unused
ascelerate apps media library <bundle-id> --only APP_IPHONE_DUO --delete-unused

--only narrows it to images that fit the given kinds, by asset category and pixel size: PRODUCT_PAGE_HEADER, APP_STORE_SEARCH_RESULTS, APP_IPHONE_DUO, a screenshot display type such as APP_IPHONE_67, or UNFINISHED_UPLOADS for uploads whose file never arrived, so the library can be cleaned one device type at a time. Images less than an hour old are always kept (an upload running at the same time may be about to place them), and a run that hits App Store Connect's hourly API limit stops and says how many are left.

Images that went through App Review are never deleted, and each one is checked again right before it is deleted.

Capturing Screenshots

Capture App Store screenshots directly from iOS/iPadOS simulators using UI tests. Replaces fastlane snapshot.

# Generate config and helper files
ascelerate screenshot init                        # Creates ascelerate/screenshot.yml and ascelerate/ScreenshotHelper.swift

Capture screenshots

ascelerate screenshot run ascelerate screenshot run -l en-US,tr-TR # Only capture a subset of configured languages ascelerate screenshot frame # Frame screenshots with device bezels ascelerate screenshot doctor # Check config and environment for problems

Add ScreenshotHelper.swift to your UITest target, then call setupScreenshots(app) in setUp() and screenshot("name") to capture:

override func setUp() {
    setupScreenshots(app)
    app.launch()
}

func testScreenshots() { screenshot("01-home") app.buttons["Settings"].tap() screenshot("02-settings") }

On the iPhone Duo simulator (Xcode 27.1+), setHinge(.open) / setHinge(.closed) unfold and fold the device mid-test (it always boots folded). deviceBezel also takes a list of bezels; each screenshot is framed with the one whose screen fits its size, so folded and unfolded shots each get the right frame.

Configure via ascelerate/screenshot.yml:

# workspace: MyApp.xcworkspace
project: MyApp.xcodeproj
scheme: AppUITests
devices:
  - simulator: iPhone 17 Pro Max
    # frameDevice: true
    # deviceBezel: ./bezels/iPhone 17 Pro Max.png
  - simulator: iPad Pro 13-inch (M5)
    # frameDevice: true
    # deviceBezel: ./bezels/iPad Pro 13-inch (M5).png
languages:
  - en-US
  - de-DE
outputDirectory: ./screenshots

framedOutputDirectory: ./screenshots/framed

clearPreviousScreenshots: true localizeSimulator: true overrideStatusBar: true

darkMode: false

disableAnimations: false

waitAfterBoot: 0

configuration: Debug

testplan: MyTestPlan

numberOfRetries: 0 # Retry failed languages (erase + reboot simulator)

stopAfterFirstError: false

reinstallApp: false

disableAssetDownloads: false # Disable mobileassetd (no Siri/keyboard/ML asset downloads; also blocks on-device ML assets)

xcargs: -maximum-parallel-testing-workers 2

Features:

  • Builds once, then runs tests across all languages
  • iPhone and iPad run concurrently per language
  • Status bar override (9:41, full bars, no carrier)
  • Simulator localization per language
  • Dark mode support
  • Animation disabling for reliable captures
  • Automatic retries for failed languages (erases simulator, re-localizes, reboots, and reruns)
  • Errors skip and continue, with summary table and error logs saved to output
  • Helper version tracking with update warnings
  • Device bezel framing with Apple Product Bezels (download required)
  • Config validation via doctor subcommand
  • create-helper available separately but also run automatically by init
Output structure:
screenshots/
├── en-US/
│   ├── iPhone-01-home.png
│   └── iPad-01-home.png
└── de-DE/
    └── ...

With frameDevice enabled, framed screenshots are saved to {outputDirectory}/framed/ (or framedOutputDirectory if set).

App Info & Categories

# View app info, categories, and per-locale metadata
ascelerate apps app-info view <bundle-id>

List all available category IDs (no bundle ID needed)

ascelerate apps app-info view --list-categories

Update localization fields for a single locale

ascelerate apps app-info update <bundle-id> --name "My App" --subtitle "Best app ever" ascelerate apps app-info update <bundle-id> --locale de-DE --name "Meine App"

Update categories (can combine with localization flags)

ascelerate apps app-info update <bundle-id> --primary-category UTILITIES ascelerate apps app-info update <bundle-id> --primary-category GAMES_ACTION --secondary-category ENTERTAINMENT

Export all app info localizations to JSON

ascelerate apps app-info export <bundle-id> ascelerate apps app-info export <bundle-id> --output app-infos.json

Bulk update localizations from a JSON file

ascelerate apps app-info import <bundle-id> --file app-infos.json

Territory Availability

# View which territories the app is available in
ascelerate apps availability <bundle-id>

Show full country names

ascelerate apps availability <bundle-id> --verbose

Make territories available or unavailable

ascelerate apps availability <bundle-id> --add CHN,RUS ascelerate apps availability <bundle-id> --remove CHN

Encryption Declarations

# View existing encryption declarations
ascelerate apps encryption <bundle-id>

Create a new encryption declaration

ascelerate apps encryption <bundle-id> --create --description "Uses HTTPS for API communication" ascelerate apps encryption <bundle-id> --create --description "Uses AES encryption" --proprietary-crypto --third-party-crypto

EULA

# View the current EULA (or see that the standard Apple EULA applies)
ascelerate apps eula <bundle-id>

Set a custom EULA from a text file

ascelerate apps eula <bundle-id> --file eula.txt

Remove the custom EULA (reverts to standard Apple EULA)

ascelerate apps eula <bundle-id> --delete

Subscription Grace Period

The grace period lets subscribers keep access for a short window after a failed renewal payment while Apple retries billing. Settings apply to the whole app.

# View current grace period configuration
ascelerate apps subscription-grace-period <bundle-id>

Enable for production with a 16-day window, applies to all renewals

ascelerate apps subscription-grace-period <bundle-id> --opt-in true --duration SIXTEEN_DAYS --renewal-type ALL_RENEWALS

Enable for sandbox testing too

ascelerate apps subscription-grace-period <bundle-id> --sandbox-opt-in true

Valid --duration values: THREE_DAYS, SIXTEEN_DAYS, TWENTY_EIGHT_DAYS. Valid --renewal-type values: ALL_RENEWALS, PAID_TO_PAID_ONLY.

Devices

# List registered devices
ascelerate devices list
ascelerate devices list --platform IOS --status ENABLED

Show device details (interactive picker if name/UDID omitted)

ascelerate devices info ascelerate devices info "My iPhone"

Register a new device (interactive prompts if options omitted)

ascelerate devices register ascelerate devices register --name "My iPhone" --udid 00008101-XXXXXXXXXXXX --platform IOS

Update a device (interactive picker and update prompts if omitted)

ascelerate devices update ascelerate devices update "My iPhone" --name "Work iPhone" ascelerate devices update "My iPhone" --status DISABLED

Certificates

# List signing certificates
ascelerate certs list
ascelerate certs list --type DISTRIBUTION

Show certificate details (interactive picker if omitted)

ascelerate certs info ascelerate certs info "Apple Distribution: Example Inc"

Create a certificate (interactive type picker if --type omitted)

Auto-generates RSA key pair and CSR, imports into login keychain

ascelerate certs create ascelerate certs create --type DISTRIBUTION ascelerate certs create --type DEVELOPMENT --csr my-request.pem

Revoke a certificate (interactive picker if omitted)

ascelerate certs revoke ascelerate certs revoke ABC123DEF456

Bundle Identifiers

# List bundle identifiers
ascelerate bundle-ids list
ascelerate bundle-ids list --platform IOS

Show details and capabilities (interactive picker if omitted)

ascelerate bundle-ids info ascelerate bundle-ids info com.example.MyApp

Register a new bundle ID (interactive prompts if options omitted)

ascelerate bundle-ids register ascelerate bundle-ids register --name "My App" --identifier com.example.MyApp --platform IOS

Rename a bundle ID (identifier itself is immutable)

ascelerate bundle-ids update ascelerate bundle-ids update com.example.MyApp --name "My Renamed App"

Delete a bundle ID (interactive picker if omitted)

ascelerate bundle-ids delete ascelerate bundle-ids delete com.example.MyApp

Enable a capability (interactive pickers if omitted)

Shows only capabilities not already enabled

ascelerate bundle-ids enable-capability ascelerate bundle-ids enable-capability com.example.MyApp --type PUSH_NOTIFICATIONS

Disable a capability (picks from currently enabled capabilities)

ascelerate bundle-ids disable-capability ascelerate bundle-ids disable-capability com.example.MyApp

After enabling or disabling a capability, if provisioning profiles exist for that bundle ID, the command offers to regenerate them (required for changes to take effect).

Note: Some capabilities (e.g. App Groups, iCloud, Associated Domains) require additional configuration in the Apple Developer portal after enabling.

Provisioning Profiles

# List provisioning profiles
ascelerate profiles list
ascelerate profiles list --type IOS_APP_STORE --state ACTIVE

Show profile details (interactive picker if omitted)

ascelerate profiles info ascelerate profiles info "My App Store Profile"

Download a profile (interactive picker if omitted)

ascelerate profiles download ascelerate profiles download "My App Store Profile" --output ./profiles/

Create a profile (fully interactive if options omitted)

Prompts for name, type, bundle ID, certificates, and devices

ascelerate profiles create ascelerate profiles create --name "My Profile" --type IOS_APP_STORE --bundle-id com.example.MyApp --certificates all

--certificates all uses all certs of the matching family (distribution, development, or Developer ID)

You can also specify serial numbers: --certificates ABC123,DEF456

Delete a profile (interactive picker if omitted)

ascelerate profiles delete ascelerate profiles delete "My App Store Profile"

Reissue profiles (delete + recreate with latest certs of matching family)

ascelerate profiles reissue # Interactive: pick from all profiles (shows status) ascelerate profiles reissue "My Profile" # Reissue a specific profile by name ascelerate profiles reissue --all-invalid # Reissue all invalid profiles ascelerate profiles reissue --all # Reissue all profiles regardless of state ascelerate profiles reissue --all --all-devices # Reissue all, using all enabled devices for dev/adhoc ascelerate profiles reissue --all --to-certs ABC123,DEF456 # Use specific certificates instead of auto-detect

Builds

# List all builds (shows app version, platform, and build number)
ascelerate builds list
ascelerate builds list --bundle-id <bundle-id>
ascelerate builds list --bundle-id <bundle-id> --version 2.1.0
ascelerate builds list --bundle-id <bundle-id> --platform macos

Archive an Xcode project

ascelerate builds archive ascelerate builds archive --scheme MyApp --output ./archives

Validate a build before uploading

ascelerate builds validate MyApp.ipa

Upload a build to App Store Connect

ascelerate builds upload MyApp.ipa

Wait for a build to finish processing

ascelerate builds await-processing <bundle-id> ascelerate builds await-processing <bundle-id> --build-version 903 ascelerate builds await-processing <bundle-id> --build-version 903 --platform macos

The archive command auto-detects the .xcworkspace or .xcodeproj in the current directory and resolves the scheme if only one exists. It accepts .ipa, .pkg, or .xcarchive files for upload and validate. When given an .xcarchive, it detects the archive's platform and automatically exports to .ipa (iOS-family) or .pkg (macOS) before uploading, passing the matching platform to altool.

TestFlight

# Beta groups
ascelerate testflight groups list <bundle-id>
ascelerate testflight groups info <bundle-id> "External Testers"
ascelerate testflight groups create <bundle-id> --name "Friends" --public-link --public-link-limit 100
ascelerate testflight groups update <bundle-id> "Friends" --public-link false
ascelerate testflight groups delete <bundle-id> "Friends"

Give a group access to a build (defaults to the latest)

ascelerate testflight groups add-build <bundle-id> "Friends" ascelerate testflight groups add-build <bundle-id> "Friends" --build 123 ascelerate testflight groups remove-build <bundle-id> "Friends" --build 123

Public-link recruitment criteria (device/OS filters)

ascelerate testflight groups criteria view <bundle-id> "Friends" --options ascelerate testflight groups criteria set <bundle-id> "Friends" --filter IPHONE:18.0 --filter IPAD:17.0:26 ascelerate testflight groups criteria clear <bundle-id> "Friends"

Testers

ascelerate testflight testers list <bundle-id> --group "Friends" ascelerate testflight testers add <bundle-id> --email [email protected] --group "Friends" ascelerate testflight testers remove <bundle-id> [email protected] --group "Friends" ascelerate testflight testers remove <bundle-id> [email protected] # remove from the whole app ascelerate testflight testers invite <bundle-id> [email protected] # re-send the invitation email ascelerate testflight testers import <bundle-id> --file testers.csv --group "Friends"

Builds & distribution

ascelerate testflight builds <bundle-id> # TestFlight states per build ascelerate testflight versions <bundle-id> # pre-release version trains ascelerate testflight status <bundle-id> --build 123 # processing, testing, and beta review states ascelerate testflight expire <bundle-id> --build 123 ascelerate testflight notify <bundle-id> # notify testers about the latest build ascelerate testflight auto-notify <bundle-id> --enabled true

What to Test (per build, per locale)

ascelerate testflight whats-new view <bundle-id> ascelerate testflight whats-new set <bundle-id> --text "Bug fixes" --locale en-US ascelerate testflight whats-new set <bundle-id> --text "Bug fixes" # all existing locales ascelerate testflight whats-new export <bundle-id> --output notes.json ascelerate testflight whats-new import <bundle-id> --file notes.json

Beta review (required for external testing)

ascelerate testflight submit <bundle-id> ascelerate testflight app-info view <bundle-id> ascelerate testflight app-info update <bundle-id> --locale en-US --feedback-email [email protected] ascelerate testflight review-info <bundle-id> # contact + demo account; pass flags to update ascelerate testflight eula <bundle-id> --file eula.txt # custom beta license agreement

Tester feedback

ascelerate testflight feedback crashes list <bundle-id> ascelerate testflight feedback crashes log <submission-id> --output crash.log ascelerate testflight feedback screenshots list <bundle-id> ascelerate testflight feedback screenshots download <bundle-id> # picker; zips screenshots + comment

Build-scoped commands default to the latest non-expired build; pass --build (and --platform for universal-purchase apps) to target a specific one. testers import reads one tester per line (email[,first name[,last name]]), skipping blank lines, # comments, and a leading header row.

In-App Purchases

# List and inspect
ascelerate iap list <bundle-id>
ascelerate iap list <bundle-id> --type consumable --state approved
ascelerate iap info <bundle-id> <product-id>

Promoted purchases (shown on the App Store product page)

ascelerate iap promoted list <bundle-id> ascelerate iap promoted add <bundle-id> <product-id> ascelerate iap promoted reorder <bundle-id> com.example.a,com.example.b ascelerate iap promoted toggle <bundle-id> <product-id> --enabled false ascelerate iap promoted remove <bundle-id> <product-id>

Create, update, and delete

ascelerate iap create <bundle-id> --name "100 Coins" --product-id <product-id> --type CONSUMABLE ascelerate iap update <bundle-id> <product-id> --name "100 Gold Coins" ascelerate iap delete <bundle-id> <product-id>

Submit for review

ascelerate iap submit <bundle-id> <product-id>

Manage localizations

ascelerate iap localizations view <bundle-id> <product-id> ascelerate iap localizations export <bundle-id> <product-id> ascelerate iap localizations import <bundle-id> <product-id> --file iap-de.json

Pricing — set the base region price (auto-equalizes to all other territories)

ascelerate iap pricing show <bundle-id> <product-id> ascelerate iap pricing tiers <bundle-id> <product-id> --territory USA ascelerate iap pricing set <bundle-id> <product-id> --price 4.99 ascelerate iap pricing set <bundle-id> <product-id> --price 4.99 --base-territory GBR

Pricing — manage per-territory manual overrides

ascelerate iap pricing override <bundle-id> <product-id> --price 5.99 --territory FRA ascelerate iap pricing remove <bundle-id> <product-id> --territory FRA

Pricing — copy the schedule between products (same app or another one)

ascelerate iap pricing export <bundle-id> <product-id> --output prices.json ascelerate iap pricing import <other-bundle-id> <other-product-id> --file prices.json

Per-IAP territory availability (independent of the app's territories)

ascelerate iap availability <bundle-id> <product-id> ascelerate iap availability <bundle-id> <product-id> --add CHN,RUS --remove ITA --available-in-new-territories true

Offer codes (campaigns + redeem codes)

ascelerate iap offer-code list <bundle-id> <product-id> ascelerate iap offer-code create <bundle-id> <product-id> --name "Launch Promo" --eligibility NON_SPENDER,ACTIVE_SPENDER --price 0.99 --territory USA --equalize-all-territories ascelerate iap offer-code toggle <bundle-id> <product-id> <offer-code-id> --active true ascelerate iap offer-code gen-codes <bundle-id> <product-id> <offer-code-id> --count 100 --expires 2026-12-31 ascelerate iap offer-code add-custom-codes <bundle-id> <product-id> <offer-code-id> --code PROMO2026 --count 1000 --expires 2026-12-31 ascelerate iap offer-code view-codes <one-time-use-batch-id> --output codes.txt

Promotional images + App Review screenshot

ascelerate iap images list <bundle-id> <product-id> ascelerate iap images upload <bundle-id> <product-id> ./hero.png ascelerate iap images delete <bundle-id> <product-id> <image-id> ascelerate iap review-screenshot view <bundle-id> <product-id> ascelerate iap review-screenshot upload <bundle-id> <product-id> ./review.png ascelerate iap review-screenshot delete <bundle-id> <product-id>

Filter values are case-insensitive. Types: CONSUMABLE, NON_CONSUMABLE, NON_RENEWING_SUBSCRIPTION. States: APPROVED, MISSING_METADATA, READY_TO_SUBMIT, WAITING_FOR_REVIEW, IN_REVIEW, etc.

iap info and iap pricing show warn when an IAP has no price schedule — the same condition surfaced in apps review preflight (which skips products you removed from sale instead of failing them). When set changes the base territory price, existing per-territory manual overrides are preserved by default. If overrides exist, an interactive menu offers to revert any of them; pass --remove-all-overrides for a non-interactive wipe.

iap pricing export writes the base territory and every manual price to a JSON file keyed by territory code; iap pricing import applies such a file to any IAP — prices are matched to the target product's own tiers by customer price, so the file works across products and apps. Import replaces the schedule wholesale: territories not listed in the file revert to auto-equalize. If the current schedule already matches the file, import is a no-op.

Offer code one-time-use codes are generated asynchronously. After gen-codes, run view-codes to fetch the actual code values. If the response is empty, retry in a few seconds. Custom codes (add-custom-codes) are developer-supplied strings that don't need separate generation.

Images and review screenshots use Apple's 3-step file upload flow (reserve → PUT chunks → commit with MD5). The CLI handles all three steps in upload.

Subscriptions

```bash

List and inspect

ascelerate sub groups ascelerate sub list ascelerate sub info

Create, update, and delete subscriptions

ascelerate sub create --name "Monthly" --product-id --period ONE_MONTH --group-id ascelerate sub update --name "Monthly Plan" ascelerate sub delete

Manage subscription groups

ascelerate sub create-group --name "Premium" ascelerate sub update-group --name "Premium Plus" ascelerate sub delete-group

Submit for review

ascelerate sub submit

Subscription localizations

ascelerate sub localizations view ascelerate sub localizations export ascelerate sub localizations import --file sub-de.json

Subscription group localizations

ascelerate sub group-localizations view ascelerate sub group-localizations export ascelerate sub group-localizations import --file group-de.json

Pricing — single territory or fan-out across all territories

ascelerate sub pricing show ascelerate sub pricing tiers --territory USA ascelerate sub pricing set --price 4.99 --territory USA ascelerate sub pricing set --price 4.99 --equalize-all-territories

Standard global price raise: grandfather existing subscribers at the old price

ascelerate sub pricing set --price 9.99 --equalize-all-territories --preserve-current

Pricing — copy per-territory prices between subscriptions (same app or another one)

ascelerate sub pricing export --output prices.json ascelerate sub pricing import --file prices.json --preserve-current

Per-subscription territory availability (independent of the app's territories)

ascelerate sub availability ascelerate sub availability --add CHN,RUS --remove ITA --available-in-new-territories true ascelerate sub availability --plan monthly # the "Monthly with 12-Month Commitment" plan of an annual subscription

Introductory offers (free trials and intro discounts for new subscribers)

ascelerate sub intro-offer list ascelerate sub intro-offer create --mode FREE_TRIAL --duration ONE_WEEK --periods 1 ascelerate sub intro-offer create --mode PAY_AS_YOU_GO --duration ONE_MONTH --periods 3 --territory USA --price 0.99 ascelerate sub intro-offer update --end-date 2026-12-31 ascelerate sub intro-offer delete

Promotional offers (server-signed offers for existing subscribers)

ascelerate sub promo-offer list ascelerate sub promo-offer create --name "Loyalty 50%" --code LOYALTY50 --mode PAY_AS_YOU_GO --duration ONE_MONTH --periods 3 --price 4.99 --territory USA --equalize-all-territories ascelerate sub promo-offer update --price 5.99 --equalize-all-territories ascelerate sub promo-offer delete

Offer codes (redeemable codes — one-time-use batches and custom codes)

ascelerate sub offer-code list ascelerate sub offer-code create --name "Launch Free Month" --eligibility NEW --offer-eligibility STACK_WITH_INTRO_OFFERS --mode FREE_TRIAL --duration ONE_MONTH --periods 1 --price 0 --territory USA --equalize-all-territories ascelerate sub offer-code toggle --active true ascelerate sub offer-code gen-codes --count

... (README truncated for length)

Chat with me