- New: review-rejections, portfolio-originality, release-management, store-preflight, privacy-manifest, export-compliance, aso-basics, whats-new - Updated: explicit build numbers, first availability UI fallback, phased + manual release by default, IAP versions in review submissions (API 4.4.1, reported, verify), IAP description 45, privacy manifest cross-links, tool-neutral CLI instructions - docs/agents-core.md: shared rules for every agent
78 lines
6.9 KiB
Markdown
78 lines
6.9 KiB
Markdown
---
|
|
name: appstore-first-release
|
|
description: Step-by-step first (and later) App Store release of a mobile game - App Store Connect API key, bundle id, app record, signing and xcodebuild archive/upload with the API key, TestFlight, version + listing text in many locales, screenshots, age rating, App Privacy, review details, in-app purchases with the first version, and submitting for review. Use when the user wants to publish, release, upload or set up an iOS game/app on the App Store or TestFlight, or hits App Store Connect API errors.
|
|
---
|
|
|
|
# App Store: first release
|
|
|
|
The CLI is `storebound` (on PATH while the plugin is enabled; otherwise `bunx storebound` in a project that has it,
|
|
or `bun <toolchain>/cli/index.ts`; agents can also use the `storebound mcp` tools).
|
|
Config: `storebound.config.ts`; credentials: `ASC_KEY_ID`, `ASC_ISSUER_ID`, `ASC_P8` (env or
|
|
`.storebound/credentials.json`, gitignored). All commands are idempotent and have `--dry`. Run `storebound check` first.
|
|
|
|
**Ask the owner before submitting for review, releasing, or canceling a submission.** The final "Submit for Review"
|
|
of a first version is the owner's click. Shared agent rules: `docs/agents-core.md` of the toolchain.
|
|
|
|
## Steps (API = storebound, UI = App Store Connect / Xcode by hand)
|
|
|
|
1. **UI · API key.** App Store Connect > Users and Access > Integrations > App Store Connect API > Team Keys > +
|
|
(Admin or App Manager). Download `AuthKey_<KEYID>.p8` once, keep it in `~/.appstoreconnect/private_keys/`, note the
|
|
Key ID and Issuer ID. The same key signs builds (`-allowProvisioningUpdates`).
|
|
2. **API · Bundle id.** `storebound apple bundle` registers it (Certificates, Identifiers & Profiles).
|
|
3. **UI · App record.** Apps > + > New App: iOS, name, primary language, the bundle id, SKU. **No API for this.**
|
|
Then `storebound apple status` finds it (put the Apple ID in `apple.appId` to skip the lookup).
|
|
4. **Local · Info.plist.** `ITSAppUsesNonExemptEncryption` with the true answer (`export-compliance` skill; `NO` only
|
|
when the game uses nothing beyond OS / HTTPS encryption) so builds arrive answered;
|
|
`NSUserTrackingUsageDescription` if ads use the IDFA; privacy policy reachable inside the app (guideline 5.1.1).
|
|
5. **Local · Build + upload** (Mac, no Xcode account login needed, the API key does signing):
|
|
```sh
|
|
K="-allowProvisioningUpdates -authenticationKeyPath $ASC_P8 -authenticationKeyID $ASC_KEY_ID -authenticationKeyIssuerID $ASC_ISSUER_ID"
|
|
xcodebuild -project ios/App/App.xcodeproj -scheme App -configuration Release -destination generic/platform=iOS \
|
|
-archivePath build/App.xcarchive MARKETING_VERSION=1.0.0 CURRENT_PROJECT_VERSION=3 $K archive
|
|
xcodebuild -exportArchive -archivePath build/App.xcarchive -exportOptionsPlist ExportOptions.plist -exportPath build/export $K
|
|
```
|
|
ExportOptions.plist: `method` app-store-connect, `destination` upload, `teamID` <your team>, `signingStyle`
|
|
automatic. `MARKETING_VERSION` / `CURRENT_PROJECT_VERSION` overrides beat the project values. Take the build
|
|
number from `storebound apple next-build` right before archiving and **note it**: every later step selects that
|
|
exact build, never "the latest". Processing takes 5-30 min (`storebound apple status` shows VALID).
|
|
6. **UI · TestFlight.** Create an internal group with "access to all builds" (hasAccessToAllBuilds) once: every new
|
|
build reaches the testers automatically. Sandbox testers: Users and Access > Sandbox.
|
|
7. **API · Version + text.** Fill `store/appstore.md`, then `storebound apple listing --version 1.0.0 --build 3`
|
|
(version, copyright, MANUAL release, the build with exactly that number, name / subtitle / privacy URL,
|
|
description / keywords / promo / URLs). Then `storebound apple release --phased on`: manual release + phased
|
|
release is the default for every version (`release-management`).
|
|
8. **API · Screenshots.** `store-screenshots` skill, then
|
|
`storebound apple screenshots > .storebound/asc-shots.log 2>&1 &` (6.9" + 6.5" per locale, MD5-idempotent).
|
|
9. **API · App info.** `storebound apple appinfo`: categories, age rating declaration, content rights, Free price,
|
|
all territories. The **first** availability record of a new app may be refused by the public API (an error on
|
|
POST /v2/appAvailabilities); then set it once in App Store Connect > Pricing and Availability and re-run (later runs only read it).
|
|
10. **API · Review details.** `storebound apple review-details` (contact from `.storebound/credentials.json`
|
|
`reviewContact`, or `--contact-from <Apple ID of another app of the team>`; notes from the config).
|
|
11. **API + UI · In-app purchases.** `store-iap-setup` skill. First IAPs go to review **with** this version.
|
|
12. **UI · App Privacy.** `store-declarations` skill (nutrition labels), then **Publish**.
|
|
13. **Preflight.** `storebound validate --apple` until it shows no blocker (`store-preflight`).
|
|
14. **UI · Submit.** Version page > Add for Review (with the IAPs in the same draft submission) > Submit for Review
|
|
(owner). Release type MANUAL means the owner presses "Release" after approval.
|
|
15. **Verify + wait.** `storebound apple status`: version WAITING_FOR_REVIEW, IAPs WAITING_FOR_REVIEW, one review
|
|
submission. `storebound apple status --wait-review` polls until approved (exit 0) or rejected (exit 1,
|
|
`review-rejections`). Approved: the owner releases (`release-management`).
|
|
|
|
## Gotchas
|
|
|
|
- The app record cannot be created via API; the bundle id can.
|
|
- First consumable / non-consumable IAPs must be submitted together with an app version, only from the IAP page
|
|
("Add for Review" puts them into the draft submission). The API (`inAppPurchaseSubmissions`) answers
|
|
`FIRST_*_MUST_BE_SUBMITTED_ON_VERSION`. If the version was already submitted without them: cancel the review
|
|
submission (`storebound apple status --cancel-submission <id>`, PATCH canceled: true), wait a minute, add the IAPs
|
|
from their page, then resubmit.
|
|
- App Privacy (data types, purposes, linked, tracking) is UI-only and must be **published**, not just saved.
|
|
- Export compliance unanswered = "Missing Compliance": `storebound apple encryption` (`export-compliance`).
|
|
- ITMS-91053 / 91061 privacy manifest emails after upload: `privacy-manifest` skill.
|
|
- Screenshot sizes: 6.9" 1320 x 2868 (`APP_IPHONE_67`) is required; 6.5" 1284 x 2778 (`APP_IPHONE_65`) recommended.
|
|
PNG without alpha. Up to 10 per set. Apple processes uploads async; failures show as FAILED later.
|
|
- App Store icon (1024) must have **no alpha channel** (`storebound icons` writes rgb24).
|
|
- Limits: name 30, subtitle 30, promotional text 170, keywords 100 bytes (UTF-8, no spaces after commas),
|
|
description 4000. No "What's New" on a first version.
|
|
- Guideline 5.1.1: the privacy policy link must be inside the app too (Settings > Privacy), not only in the listing.
|
|
- `xcodebuild` with Capacitor needs a UTF-8 locale (`export LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8`).
|
|
- 500 / 503 from the API happen; storebound retries with backoff.
|