skip to content

Your fastlane screenshot lane produces dirty, run-to-run varying store shots — how do you make capture clean and repeatable on iOS and Android?

level: seniorimportance: should knowfreq 47%

answer

  1. three sources: machine, app data, artifacts
  2. one platform can erase its own machine
  3. status bar override exists on one side only
  4. seed fixtures through launch_arguments

basics

~20 s

Control three things separately: the machine, the app data and the artifacts. On iOS use erase_simulator, reinstall_app and override_status_bar; on Android use reinstall_app, stable filenames and ending_locale; and on both use clear_previous_screenshots plus launch_arguments to seed fixed data.

solid answer

~40 s

Screenshots drift for three reasons, and you fix each separately. **The machine**: `capture_ios_screenshots` owns its simulators, so `erase_simulator` gives a clean device and `override_status_bar` (with `override_status_bar_arguments`) replaces carrier, battery and clock with fixed values. `capture_android_screenshots` has neither, because it borrows a device or emulator — you reset that around the lane and use `ending_locale` to hand it back clean. **The app**: `reinstall_app` on both sides drops yesterday's state, and `launch_arguments` seeds a fixed sail order so the sailmaker measurement app always shows the same data. **The artifacts**: `clear_previous_screenshots` on both, plus turning off Android's `use_timestamp_suffix` so filenames are stable and reruns overwrite instead of accumulating.

go deeper

for a junior

Know that clear_previous_screenshots and reinstall_app exist on both capture actions, and that a screenshot shows whatever data was in the app, so fixtures matter as much as options do.

for a middle

Explain each option's mechanism: what erase_simulator wipes, what override_status_bar replaces, and why turning off Android's timestamp suffix makes reruns overwrite deterministically.

for a senior

Separate machine, app and artifact state out loud, and show that Android's missing equivalents move the work into emulator provisioning around the lane rather than eliminating it.

for a principal

Own the reliability budget: how much release time a flaky artwork lane may consume, whether capture blocks a release at all, and who is accountable when only one platform's listing gets refreshed.

## Why store screenshots drift A screenshot lane that reruns and produces different pixels has three independent sources of variation, and treating them as one problem is why teams end up hand-picking images out of an artifact directory. 1. **Machine state** — the simulator or device carries a status bar, a locale, previously granted permissions and leftover installs. 2. **App state** — the sailmaker measurement app shows whatever data is in it: last week's sail order, an empty list, a stale sync banner. 3. **Artifact state** — the output directory still holds the previous run's files, so a locale you dropped or a screen you renamed lingers on disk. Each has its own knobs, and the knobs are **not** the same on both platforms. ## iOS: the action owns the machine `capture_ios_screenshots` creates and drives simulators, so it can be authoritative about them: - `erase_simulator` wipes the simulator before the run, removing granted permissions, installed builds and stored data in one move. - `reinstall_app` reinstalls the app under test so it starts from a fresh install. - `override_status_bar` replaces the status bar with clean values, and `override_status_bar_arguments` pins exactly what it shows. Without it your listing carries a 47% battery and whatever the clock said. - `clear_previous_screenshots` deletes the previous run's images from `output_directory` first. - `dark_mode` decides appearance explicitly instead of inheriting it. - `concurrent_simulators` is the option that *adds* variance: parallel simulators speed up a big matrix but multiply resource contention and timing flakiness. `number_of_retries` and `stop_after_first_error` then decide whether a flaky pass is masked or surfaced. ## Android: the action borrows the machine `capture_android_screenshots` runs an instrumentation test APK on a device or emulator it does not own, and its much smaller option surface reflects that: - `reinstall_app` reinstalls the app and test APKs, which is the main state reset available inside the action. - `clear_previous_screenshots` clears the output directory, exactly as on iOS. - `use_timestamp_suffix` controls whether captured files get a timestamp appended. Turning it off gives stable filenames, so a rerun overwrites deterministically instead of accumulating near-duplicates. - `ending_locale` restores the device's system locale afterwards, because the next job inherits this machine. - `specific_device` and `device_type` pin which connected device is used, so a lane does not silently capture on whatever happens to be plugged in. - `exit_on_test_failure` decides whether a failing capture test stops the run or lets it continue and produce a partial set. There is **no** `erase_simulator` and **no** status-bar override here. If you want a clean Android status bar or a fresh machine, that is emulator lifecycle work in the job around the action — a fresh or wiped emulator image — not an option on the action. ## The divergence in one table | concern | iOS | Android | |---|---|---| | wipe the machine | `erase_simulator` | no equivalent — reset the emulator around the lane | | clean status bar | `override_status_bar`, `override_status_bar_arguments` | no equivalent in the action | | stale app data | `reinstall_app` | `reinstall_app` | | stale image files | `clear_previous_screenshots` | `clear_previous_screenshots` | | stable filenames | fixed by the test's capture names | turn off `use_timestamp_suffix` | | leftover system state | the simulator is disposable | `ending_locale` restores the locale | | which hardware ran | `devices`, `ios_version` | `specific_device`, `device_type` | ## The half no option can fix: your data Both actions expose `launch_arguments`, and this is what separates a repeatable lane from a lucky one. Pass an argument that boots the sailmaker measurement app into a fixed fixture — one named loft, one known sail order, fixed measurements, a frozen date — instead of whatever the shared account happened to contain. Everything else on this page controls the machine; only this controls what the screen actually says. ## A checklist for the sailmaker lane 1. Pin the hardware: an explicit `devices` list on iOS, `specific_device` on Android. 2. Reset the machine: `erase_simulator` on iOS, a fresh emulator around the Android lane. 3. Reset the app: `reinstall_app` on both. 4. Seed the data: `launch_arguments` carrying the fixture flag on both. 5. Clean the chrome: `override_status_bar` on iOS; on Android, provision the device's chrome or crop it. 6. Clean the output: `clear_previous_screenshots` on both, stable filenames on Android. 7. Restore the machine: `ending_locale` on Android; discard the simulator on iOS. 8. Decide failure policy explicitly with `stop_after_first_error` or `exit_on_test_failure` rather than shipping a partial set silently. ## What interviewers listen for The strong answer separates machine, app and artifacts, and knows the two actions are not equally powerful: the iOS action can erase and re-dress its own simulator, the Android one cannot, so the equivalent work moves into the job that provisions the emulator. Naming `override_status_bar` and noting it has no Android counterpart is the detail that shows real lane ownership rather than a memorised option list.

  • How do you get a clean status bar in Android store screenshots when the action has no override option?
    You do it outside `capture_android_screenshots`. The action borrows a device, so the clean-up belongs to the job that provisions it: start from a fresh emulator image, set the clock and network indicators as part of that provisioning, or crop the status bar during framing. On iOS the same result is one option, `override_status_bar`, because the action owns its simulators.
  • Why can concurrent_simulators make an iOS capture lane less reliable rather than more?
    It runs several simulators at once on one host, which cuts wall-clock time on a large device and language matrix but multiplies CPU, memory and I/O contention. Contention shows up as timing flakiness in UI tests, so screens get captured mid-animation or before data loads. Reach for it after the lane is deterministic, not as a fix for a slow one.

Store screenshots are product photography, not candid shots: you clear the bench and set the lights before you press the shutter. The capture options are that studio setup, and anything you leave to the device ends up in the picture.

saying these in an interview costs you the question

  • Blames flaky screenshots on the tooling rather than seeded state
  • Assumes erase_simulator or a status-bar override exists on Android
  • Never clears the previous run's output directory
  • Ships whatever data the shared test account happens to hold
  • Adds parallel simulators to fix a lane that is already unstable