PrompTugal for iOS

Release status: summit app

Release owns two things today: App Store screenshots from screens.yml (built, not yet run on the real screens) and the plan for the App Store path through the App Store Connect API (below, nothing executed). The API can do everything except the App Privacy answers; Dinis does those by hand, plus anything Apple refuses over the API.

Branch: split/release · Updated: 8 Nov, 11:40 UTC

Screenshots (built)

  • screens.yml, run by hand with app=promptugal, store=true (optional locales, default en-US,pt-PT,pt-BR).
  • One job per display size, all locales inside the job. The device decides the pixel size, so nothing is scaled:
SizeSimulator (first one the runner has)PixelsASC display type
iPhone 6.9"iPhone 17 Pro Max, then 16 Pro Max1320×2868APP_IPHONE_69
iPhone 6.5"iPhone 14 Plus, 13 Pro Max, 11 Pro Max, XS Max1284×2778 or 1242×2688APP_IPHONE_65
iPad 13"iPad Pro 13-inch (M5), then (M4)2064×2752APP_IPAD_PRO_3GEN_129
  • store-check.py fails the job on any size Apple does not take. Output is JPEG (no alpha channel), light appearance, status bar 9:41 full battery.
  • Artifact appstore-screens-<sha>-promptugal: <locale>/<display type>/<NN>-<screen id>.jpg. The order of the files is the order on the store page.
  • Locales come from the app's own strings: the job passes -testLanguage and -testRegion to the screens test, so pt-PT and pt-BR show once i18n has landed.
  • Needs from others: the PrompTugal project's PrompTugalScreens scheme must run a screens test that attaches one screenshot per destination (like Team's ScreensTests); apps or design own it. Apple takes at most 10 screenshots per set: ux picks the ten and the order; until then the artifact has every destination.
  • Inputs: ids (ux's list, in store order: now,team.schedule,explore,explore.talk,news.home,explore.people,explore.graph), ids_required (default now,team.schedule,explore,news.home: a missing one fails the job, any other missing id is skipped with a warning), hide_term (default Web Summit; reaches the test as PT_HIDE_TERM, which the test adds as the DEBUG-only -PTHideTerm <text> when not empty: the app hides every agenda event and news item whose title or place contains it, so no third-party trademark is in a store image), seed_picks (the five pick ids ux chose, comma-separated; reaches the test as PT_SEED_PICKS, which the test adds as -PTSeedPicks <value> when not empty) and now (default 2026-11-10T11:00:00Z: the pretend time). now reaches the screens test as the environment variable PT_NOW (set through TEST_RUNNER_PT_NOW); the test must pass it on as a launch argument, an empty value meaning real time:
var args = ["-PTDestination", id, "-PTSampleData"]
if let now = ProcessInfo.processInfo.environment["PT_NOW"], !now.isEmpty { args += ["-PTNow", now] }
if let t = ProcessInfo.processInfo.environment["PT_HIDE_TERM"], !t.isEmpty { args += ["-PTHideTerm", t] }
if let p = ProcessInfo.processInfo.environment["PT_SEED_PICKS"], !p.isEmpty { args += ["-PTSeedPicks", p] }
app.launchArguments = args
  • Plan: one run on dev at about 15:40 UTC, screens.yml with app=promptugal store=true (about 25 minutes with three sizes in parallel); needs this workflow merged to dev first.
  • Not yet run: the first run waits for the screens test and for the CI queue.

Plan: App Store through the API (nothing executed)

Everything uses the existing key and scripts/release/asc.py (it already has has-record, wait-build, compliance, whats-new for TestFlight, and submit). New subcommands would be added there, each a dry run unless --yes, and nothing runs without the conductor's go. The app is the record for news.promptugal.reader.

#StepAPICan the API do it?
1Pick the build (version, build number) and wait until processing is VALIDGET /v1/buildsYes (wait-build)
2Export compliance answeredPATCH /v1/builds/{id} usesNonExemptEncryption=falseYes (compliance)
3Create the App Store version for that marketing version (e.g. 0.4.n), release type manualPOST /v1/appStoreVersionsYes
4Attach the build to the versionPATCH /v1/appStoreVersions/{id}/relationships/buildYes
5Name "PrompTugal: Lisbon Summit", subtitle, privacy policy URL, per localeGET /v1/apps/{id}/appInfos, then PATCH /v1/appInfoLocalizations/{id} (create with POST for pt-PT and pt-BR)Yes, while the app info is editable. Apple checks that the name is free when it is saved; if it refuses, Dinis sets it by hand
6Description, keywords, promotional text, what's new, support URL, marketing URL, per locale (en-US, pt-PT, pt-BR)POST/PATCH /v1/appStoreVersionLocalizationsYes
7ScreenshotsPOST /v1/appScreenshotSets per locale and display type, then POST /v1/appScreenshots (reserve), upload the file parts to the returned URLs, PATCH with uploaded=true and the MD5Yes. Ten per set, in file order
8Categories (primary, secondary)PATCH /v1/appInfos/{id} relationshipsYes
9Age rating answersPATCH /v1/ageRatingDeclarations/{id}Yes (review prepares the answers)
10Content rights declaration: the app shows third-party content, so USES_THIRD_PARTY_CONTENTPATCH /v1/apps/{id} contentRightsDeclarationYes
11Price: free, and territoriesPOST /v1/appPriceSchedules, territory availabilityYes
12Copyright linePATCH /v1/appStoreVersions/{id} copyrightYes
13Review contact, notes for the reviewer, demo account (none)POST/PATCH /v1/appStoreReviewDetailsYes. The phone number comes from the secret REVIEW_CONTACT_PHONE, never from git
14App Privacy answers (data types collected, tracking)none in the public APINo: Dinis, by hand, in App Store Connect (review prepares every answer)
15Advertising identifier question (answer: No) and similar prompts shown at submissionnot in the public API; they come back as errors on submit if unansweredMaybe by hand: we find out on the dry run of step 16
15bUntick Mac (Apple silicon) and Apple Vision Pro availability (iPhone and iPad only)none in the public API (only territory availability is)By hand (review passes it to Dinis)
16Submit for reviewPOST /v1/reviewSubmissions, POST /v1/reviewSubmissionItems, PATCH ... submitted=trueYes (submit, with confirm). Apple refuses it with a list of what is missing, which tells us if step 14 or 15 is open
17Release after approval (manual or automatic)PATCH /v1/appStoreVersions/{id} releaseType; POST /v1/appStoreVersionReleaseRequestsYes

Order and checks:

  • Steps 3 to 13 can run as soon as the build exists; steps 5, 6 and 13 read their text from review's files in git (store/listing-<locale>.md, review-notes.txt, what-to-test.txt), so the store page matches what review approved; the Languages and Ask lines stay in all three languages. Every step first reads what is already there and changes only what differs, so it can be run again.
  • A dry run prints what each step would change. The first real run is steps 3 to 13 with the conductor's go; the submit (16) is a separate go.
  • A new build number after the submission starts needs steps 1, 2 and 4 again, and a new submission.
  • The public TestFlight link (testflight's part) is separate: external group, betaAppReviewSubmissions, betaGroups.publicLinkEnabled. Not in this plan.

What only Dinis can do by hand

  • App Privacy answers (step 14).
  • Mac (Apple silicon) and Vision Pro availability (step 15b).
  • Anything Apple refuses over the API: most likely the name if "PrompTugal: Lisbon Summit" is taken (step 5), and any submission prompt that the API cannot answer (step 15).
  • The REVIEW_CONTACT_PHONE secret.