asc-client: A Swift CLI for the App Store Connect API

·

If you’ve ever shipped an iOS app, you know the ritual. Open Xcode, archive, wait, open App Store Connect in the browser, click through a dozen screens to set up localizations, upload screenshots one display size at a time, attach the build, fill in What’s New, and finally hit Submit. For one app, it’s tedious. For multiple apps across multiple locales, it’s a time sink.

I wanted a single tool that could handle the entire release pipeline from the terminal: Create a version, build, upload, set metadata, attach the build, submit for review, without leaving the command line. That’s why I built asc-client.

What It Is

asc-client is a command-line tool for the App Store Connect API, built in Swift. It’s a single native binary. Installation is straightforward:

brew tap keremerkan/tap
brew install asc-client
asc-client configure

The configure command walks you through setting up your App Store Connect API key. It copies your .p8 private key to a secure location with owner-only permissions.

The Full Release Pipeline in One Workflow

The most useful feature is workflow files. Instead of running commands one by one, you write a plain text file with one command per line:

# release.workflow — Ship MyApp v2.1.0

apps create-version com.example.MyApp 2.1.0
builds archive --scheme MyApp
builds upload --latest --bundle-id com.example.MyApp
builds await-processing com.example.MyApp
apps update-localizations com.example.MyApp --file localizations.json
apps attach-latest-build com.example.MyApp
apps submit-for-review com.example.MyApp

Then run it:

asc-client run-workflow release.workflow

The workflow displays all steps, asks for confirmation, and runs them sequentially. If any step fails, it stops and tells you where. Add --yes for CI/CD environments where there’s no one to confirm.

Steps are aware of each other. When builds upload finishes, the uploaded build version is automatically passed to subsequent commands. There is no need to hardcode build numbers. The create-version command is idempotent: if you re-run a workflow after a partial failure, it picks up where it left off.

Screenshot and Preview Management

Managing App Store screenshots across locales and device sizes is one of the most painful parts of App Store Connect. asc-client handles the entire flow:

# Download everything to a local folder
asc-client apps download-media com.example.MyApp

# The folder structure mirrors the API
# media/en-US/APP_IPHONE_67/01_home.png
# media/en-US/APP_IPAD_PRO_3GEN_129/01_home.png
# media/de-DE/APP_IPHONE_67/01_home.png

# Edit locally, then re-upload
asc-client apps upload-media com.example.MyApp --folder media/ --replace

Files are sorted alphabetically for ordering. Prefix them 01_, 02_, 03_ and they’ll appear in the right order on the App Store. App preview videos (.mp4, .mov) go in the same folders alongside screenshots.

If uploads get stuck (which happens more often than expected with Apple’s processing pipeline), verify-media shows the status of every item and can retry stuck ones:

asc-client apps verify-media com.example.MyApp --folder media/

Bulk Localization Updates

Instead of clicking through each locale in App Store Connect, export your localizations to JSON, edit them, and push them back:

asc-client apps export-localizations com.example.MyApp

This gives you a clean JSON file:

{
  "en-US": {
    "description": "App description here",
    "whatsNew": "- Bug fixes\n- New feature",
    "keywords": "keyword1,keyword2",
    "supportURL": "https://example.com/support"
  },
  "de-DE": {
    "description": "App-Beschreibung hier",
    ...
  }
}

Edit it, then update all locales in one go:

asc-client apps update-localizations com.example.MyApp --file localizations.json

Only the fields you include get updated. Omit a field to leave it unchanged.

Everything Else

Beyond the release pipeline, asc-client covers the corners of App Store Connect that are annoying to deal with in the browser:

  • Phased releases — enable, pause, resume, or complete a phased rollout from the terminal
  • Territory availability — add or remove countries with --add CHN,RUS or --remove CHN
  • Age ratings — view or update from a JSON file
  • Encryption declarations — view or create, so you stop getting that compliance popup
  • Custom EULAs — set from a text file or delete
  • In-app purchases and subscriptions — list and inspect (read-only for now)
  • Rate limit monitoring — see how much of your hourly API quota you’ve used

Provisioning from the Terminal

As of v0.4.0, asc-client covers the full Apple provisioning stack: devices, signing certificates, bundle identifiers, and provisioning profiles. Every command supports interactive mode. Run it without arguments and you get guided prompts, or pass all options explicitly for scripting and CI/CD.

Devices

# List registered devices
asc-client devices list
asc-client devices list --platform IOS --status ENABLED

# Register a new device (interactive prompts if options omitted)
asc-client devices register
asc-client devices register --name "My iPhone" --udid 00008101-XXXXXXXXXXXX --platform IOS

# Update name or disable a device
asc-client devices update "My iPhone" --status DISABLED

Signing Certificates

Creating certificates is usually a multi-step process: generate a CSR in Keychain Access, upload it to the Developer portal, download the certificate, import it back. asc-client does all of that in one command:

asc-client certs create --type DISTRIBUTION

This auto-generates an RSA key pair and CSR, sends it to the API, downloads the signed certificate, and imports both the private key and certificate into your login keychain. If you already have a CSR from another tool, pass it with --csr my-request.pem.

# List and inspect certificates
asc-client certs list --type DISTRIBUTION
asc-client certs info "Apple Distribution: My Team"

# Revoke a certificate (interactive picker if serial omitted)
asc-client certs revoke

Bundle Identifiers

# List and register bundle IDs
asc-client bundle-ids list --platform IOS
asc-client bundle-ids register --name "My App" --identifier com.example.MyApp --platform IOS

# View details including capabilities
asc-client bundle-ids info com.example.MyApp

# Enable or disable capabilities (interactive pickers if omitted)
asc-client bundle-ids enable-capability com.example.MyApp --type PUSH_NOTIFICATIONS
asc-client bundle-ids disable-capability com.example.MyApp

After enabling or disabling a capability, asc-client detects existing provisioning profiles for that bundle ID and offers to regenerate them, a step that’s required for the capability change to take effect but easy to forget.

Provisioning Profiles

# Create a profile (fully interactive if options omitted)
asc-client profiles create
asc-client profiles create --name "My Profile" --type IOS_APP_STORE \
  --bundle-id com.example.MyApp --certificates all

# Download a profile
asc-client profiles download "My App Store Profile" --output ./profiles/

The --certificates all flag automatically selects every certificate of the matching family: distribution certs for App Store profiles, development certs for development profiles, Developer ID certs for Direct Distribution. This matches how Xcode’s automatic signing works and means any team member can sign with the resulting profile.

Reissuing Profiles

Provisioning profiles become invalid when a certificate expires or a capability changes. Instead of manually recreating each one, profiles reissue handles it in bulk:

# Reissue all invalid profiles
asc-client profiles reissue --all-invalid

# Reissue all profiles regardless of state (e.g. after cert renewal)
asc-client profiles reissue --all

# Use specific certificates instead of auto-detect
asc-client profiles reissue --all --to-certs ABC123,DEF456

Each profile is deleted and recreated with the same name, bundle ID, devices, and profile type, but with fresh certificates. The command shows a summary table and asks for confirmation before making changes.

Why Swift?

I work almost exclusively in Swift and Xcode for iOS apps, so building the tooling in the same language was a natural choice. It also means asc-client is a native binary. Startup is instant, there’s no runtime to manage, and it compiles to a single ~64 MB executable for Apple Silicon.

Under the hood, it uses Aaron Sky’s excellent asc-swift library, which provides type-safe access to the entire App Store Connect API. Apple’s xcrun altool handles the actual binary uploads, the same tool Xcode uses, while asc-client orchestrates everything around it.

Built with Claude Code

I developed asc-client with Claude Code, Anthropic’s CLI coding agent. It turned out to be a great fit for this kind of project. Wrapping a well-documented API, handling many similar-but-different subcommands, and dealing with the edge cases of Apple’s API (like endpoints returning null in non-optional fields, or territory pagination capped at 50 items) were handled efficiently while I focused on workflow design and user experience.

Get Started

asc-client is open source under the MIT license.

Install via Homebrew:

brew tap keremerkan/tap
brew install asc-client

Or download the binary from GitHub Releases.

Or build from source:

git clone https://github.com/keremerkan/asc-client.git
cd asc-client
swift build -c release
strip .build/release/asc-client
cp .build/release/asc-client /usr/local/bin/

You’ll need an App Store Connect API key. Create one in Users and Access with the App Manager role. Then run asc-client configure and you’re set.

Full documentation is in the README on GitHub.



Comments

Leave a Reply

Your email address will not be published. Required fields are marked *