Status: Living. Android counterpart:
android-release-checklist.md.
Run this once per public-facing iOS release (TestFlight external, App Store) on both an iPhone and an iPad. Takes ~15 minutes per device once the build is installed.
Prerequisites
.puz or .ipuz file available — easiest: AirDrop one from your Mac, or
open from Files / Mail / MessagesKnown issues to skip (these are already filed; don’t re-report)
Much of sections 1, 3, 4, 5, 6, 7 is now covered by the Flutter
integration_test suite. Run it on a simulator before the manual pass:
scripts/run-ios-integration-tests.sh # auto-picks/boots a simulator
scripts/run-ios-integration-tests.sh "iPhone 16" # or a name / UDID
It boots a simulator, runs every crosscue/integration_test/*_test.dart, and
drops a final-frame screenshot per test into design/qa/ios-<git-sha>/. The
same suite runs on demand in CI via the Integration tests (iOS) workflow
(workflow_dispatch only — it’s costly, so it’s not part of the per-PR gate).
What the suite exercises today:
app_launch_test — boots to the first frame (§1, partial)seed_and_solve_test — seed a puzzle, open it, render the solve screen (§3)rebus_and_navigation_test — rebus entry via the long-press menu; Stats &
Settings render (§4, §6, §7)lifecycle_and_theme_test — background/resume persistence; live dark-mode
toggle (§5 partial, §8 partial)Still requires a human on a real device (OS-level, not automatable here): §2 import via the share sheet, §8 Dynamic Type, §9 iPad multitasking / rotation, §10 edge cases (Airplane Mode, calls), and true force-quit. A green suite means the manual pass below can focus on these.
.puz file from another app (Files, Mail, Messages) → Crosscue
appears in the share sheet.ipuz fileEST) and press the Enter button →
cell shows the rebus correctly, focus advanceshttps://atomictrxn.github.io/crosscue/privacy.html)Skip if testing on iPhone only.
16.0 per Runner.xcodeproj / Podfile) and confirm core
flows still workBest-effort, not guaranteed. The home-screen widget’s “today” tile is kept current by a
BGAppRefreshTask(and Android WorkManager). iOS decides when — or whether — it runs, based on how often the user opens the app; for low-engagement users it may fire rarely or not at all. This only affects the widget glance for someone who hasn’t opened the app in a day or two — the in-app on-open experience is always fresh (Crosshare auto-download + refresh). Don’t treat a stale tile after a long gap as a bug.
To force the iOS task during development (instead of waiting for the OS), pause
in the debugger right after launch and run:
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"dev.tomhess.crosscue.refresh"]
For each bug:
Block submitting for review if any item in sections 1-7 fails. Items in 8-10 are nice-to-have for v1.0 — file as enhancement issues rather than blockers.