Shawn Mervin

From simulator to App Store

The Rental Pilot field app is on the App Store and on Android phones in the yard. This is how a change gets from my desk to a driver’s phone: looked at on a simulated iPhone first, out to testers through TestFlight in about ten minutes, then to everyone in stages. None of it ships because a build went green, and like everything I build, it’s designed mobile first.

iOS Android

  1. Changecommitted and pushed
  2. Simulatorrendered, screenshotted, read
  3. TestFlightone command, ~10 minutes
  4. App Reviewsubmitted through the API
  5. Phased releasea slice of users first
  6. Everyoneolder builds asked to update

Every change, on a simulated iPhone first

Xcode · iOS Simulator · simctl

A type-check passing or a build going green says the code compiles. It doesn’t say the screen is right. So every change to the app is built and opened in the iOS Simulator, on a current Pro-size iPhone, and a screenshot of the actual screen is read before anything moves on. Release builds get the same treatment: the exact build configuration that goes to Apple runs on the simulator first.

The simulator is driven from the command line with xcrun simctl: boot a device, install the build, open a deep link, take the screenshot. Because it’s scripted, an agent can do the looking and report what’s on screen, and it’s the same check every time.

TestFlight in one command

fastlane · App Store Connect

One script takes a clean checkout to testers’ phones: archive, sign, upload, wait for Apple to process it, and hand it to the TestFlight testers. It takes about ten minutes, and it refuses to run from anything but a clean, up-to-date copy of the code, so what testers get is exactly what’s in the repository.

That makes small builds cheap. On a busy day the field app goes out to testers six or eight times, each one a fix someone asked for that morning, instead of a big release every few weeks.

App Review and phased releases, through the API

App Store Connect API

Submitting for review, writing the “What’s New” notes, and swapping a newer build in for one still waiting in the queue all run through the App Store Connect API instead of clicking through the website. When a better build is ready before Apple has looked at the last one, the waiting submission is pulled and the new build goes in its place within minutes.

Approved versions release in phases, a small share of users first and growing over seven days, so a problem reaches a few phones instead of all of them. Once a release has proven itself it can be pushed to everyone early.

  • App Store Connect API
  • phased release

Android, proven on the emulator

Expo EAS · Android Emulator · adb

Android builds come out of Expo’s cloud build service as an installable app, shared with the people who need it by a single link. Before a link goes out, the new build is installed over the previous one on the Android emulator. That upgrade only succeeds if the signing key matches, so it’s the cheapest proof that a tester’s phone will update in place instead of refusing.

iPhone and Android share one build number, so the same code shows the same version on both, and a bug report always says exactly which build it came from.

Nobody stuck on an old build

minimum-version check

The app asks the server, on launch, what the oldest build it still supports is. A phone below that line gets a clear “update the app” screen that sends it to the right store, instead of an old build quietly working against a newer server. That’s what makes shipping small and often safe: the field never drifts far behind.

Got an operation that's outgrown its spreadsheets?

Tell me what your business does and where it's jamming up. If I'm not the right person for it, I'll say so and point you somewhere better.

What to send, and what happens next