> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mobster.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Simulators and iPhones

> Run every check on several simulators, on your iPhone, or across every plugged-in iPhone at once.

Every check runs on its own simulator unless you name others. `--sim` adds simulators, `--device` adds your iPhones, and one run writes one report with a column for each.

## Run on several simulators

```sh theme={"theme":"css-variables"}
mobster test --sim "iPhone 17 Pro" --sim "iPhone SE (3rd generation)@iOS 26.4" --parallel 2
```

Every check runs on every `--sim`: a device type, with an optional `@` runtime. To keep the list in the repository, write `.mobster/matrix.yaml` and leave out `--sim`:

```yaml theme={"theme":"css-variables"}
# .mobster/matrix.yaml: simulators only
simulators:
  - iPhone 17 Pro@iOS 26.4
  - iPhone SE (3rd generation)@iOS 26.4
  - { device: iPhone 16e, runtime: iOS 26.4 }
```

`--matrix FILE` reads another file. A matrix file names simulators only: an entry is always a device type, never looked up as one of your devices.

`--parallel N` runs up to N checks at once (at most 8). Each run takes its own simulator and holds it for the whole run, so two runs never share one. Mobster creates more simulators of a type when it needs them. Each takes about 1.6 GB of disk, and `mobster sim prune` removes unused ones after 14 days.

Mobster caps `--parallel` at what your Mac holds: one simulator per two cores and one per 4 GB of memory, never fewer than 1 or more than 8. A 16 GB Mac runs 4. When you ask for more, the run says so on its first line and uses the cap, for example `This Mac’s configured simulator limit is 4, so --parallel 8 runs 4.` `MOBSTER_TEST_MAX_PARALLEL=N` sets the cap yourself.

Each simulator has its own results. `results.json` lists the simulators under `devices` and gives each check one cell per simulator. `junit.xml` has one `<testsuite>` per simulator, the HTML report shows a check's verdict on each, and `mobster test --history` keeps the last 20 results of each check on each simulator, so a check that fails only on the iPhone SE shows as that. Real iPhones join the same run with `--device`. A phone takes one check at a time and every simulator keeps its own slot.

## Run checks in ordered phases

Save a scenario under `.mobster/scenarios/` when one check must pass before another starts. Name each actor with the exact ID of a distinct managed simulator from `mobster devices`:

```yaml theme={"theme":"css-variables"}
# .mobster/scenarios/checkout.yaml
version: 1
name: Checkout
actors:
  buyer: <managed simulator ID>
phases:
  - name: Start
    runs:
      - actor: buyer
        check: .mobster/checks/start.yaml
  - name: Continue
    runs:
      - actor: buyer
        check: .mobster/checks/continue.yaml
```

```sh theme={"theme":"css-variables"}
mobster test scenario .mobster/scenarios/checkout.yaml
```

Mobster reserves every actor before the first phase and keeps those simulators until the scenario ends. Checks in a phase can run together, and the next phase waits for all of them to pass. A failed or uncertain phase blocks later checks and appears in the same results, JUnit and HTML reports as other test runs.

* A later check on the same actor must set `reset: none`, and can't reseed files, conditions or an app-state fixture.
* A scenario uses strict replay with no retries or repetitions. Record a complete approved path for each check with steps before the scenario. Launch-only checks need no path.
* A scenario needs every input pinned ([Retries](https://docs.mobster.dev/testing/flaky.md)) and refuses one it can't pin before it starts.
* A scenario has 1 to 4 distinct actors, within this Mac's simulator cap, and 1 to 12 phases.
* `--json` prints the suite result. Suite overrides such as `--device` and `--fresh` are refused.

## Run checks on your iPhone

```sh theme={"theme":"css-variables"}
mobster test --device "Test iPhone"
```

`--device` takes a device from `mobster devices`. In `mobster test`, this flag is the only way to pick a real iPhone: a check file and a matrix file can't name one. Keep the phone unlocked and connected, with its runner started in Mobster for Mac or by `mobster serve --manage-device`. On your iPhone:

* A check drives only its own app, and only a build you put there. Mobster installs the check's `app.path` when it's an `.ipa` or a build for devices, or it uses the build already on the phone when devicectl lists it as built by a developer (from Xcode or `xcrun devicectl`). An app from the App Store couldn't run: "Mobster tests your own apps on your iPhone. Instagram is from the App Store." Apps that are part of iOS couldn't run either, and neither can a build of another app than the check's `app.bundle`.
* Model steps stay in that app. If one opens another app (Messages, Photos), the check stops at once, couldn't run, and its report has no frame of the other app. A `launch.url` opens in the app itself, or the check couldn't run.
* Mobster never clears the app's data. A check with `reset: data` or `reinstall` runs with `reset: none`, and the report says so.
* Model steps, heals and replayed steps never take an action that [Ask before acting](https://docs.mobster.dev/safety.md#when-mobster-asks) asks about, whether or not the check's steps name it. Mobster refuses the step and asks nobody. The check ends `couldnt_run`, class `usage`, even when its expectations hold, because the step never happened. The report names the step and says to run the check on a simulator.
* Before each tap the rules allow, Mobster compares the control's label with the text Apple Vision reads from its pixels. It refuses a tap whose pixels name an act the label doesn't, such as a Done button that shows Delete. This runs where the screen-text helper is installed (Mobster for Mac has it) and the phone's video stream has a frame of that screen. Otherwise the label alone decides. A tap waits at most 0.15 s for the read.
* iOS's own dialogs get only the answers the check's `dialogs` names, and only while SpringBoard or Apple's sheet is in front ([System dialogs](https://docs.mobster.dev/testing/dialogs.md)).
* The iPhone runs one check at a time, beside any simulators.

On your iPhone a check refuses:

* a send, buy, post or delete
* Like, Unlike, Follow, Unfollow, Remove follower, Subscribe, Unsubscribe, Repost, Retweet, Super Like, Block, Unblock and Report
* Sign Out, Log Out, Leave and Leave group
* Cancel and what it cancels (Cancel order, Cancel account, Cancel and refund, Cancel all), Stop subscription, Turn off auto-renew, Finish cancellation, End ride and End membership, and Accept beside an amount someone asks you for
* a switch the steps don't name, and OK on an alert that says it can't be undone
* digits typed into a field for a one-time code, a PIN or a passcode, even when the check's steps hold them
* the money, door, alarm, vote, RSVP and call taps that [How Mobster tells](https://docs.mobster.dev/safety.md#how-mobster-tells) lists, such as Withdraw, Cash Out, Unlock, Disarm, Vote, RSVP and Answer&#x20;

Heart, Favorite, Star, Add friend, Invite and Join still go ahead. A bare Comment, Reply, Reply All, Forward, Share or Post sends text written in a box, so a check refuses it while text is written there. On an app Mobster reads from pixels, written text can't be told from other words, so a check refuses a bare Comment, Reply, Reply All, Forward and Share there. So do digits typed into a ZIP or postal code, promo, discount, coupon, area or country code field. Digits from the check's steps typed into another field, such as an employee ID, go ahead. When the model call that compiles the steps into a [task contract](https://docs.mobster.dev/architecture.md) fails, Mobster can't tell where those digits came from. It then refuses 4 to 10 digits, and a single digit in a box with no name. A simulator approves the sends, buys, posts and deletes Mobster can classify ([What a simulator may do](https://docs.mobster.dev/testing/dialogs.md#what-a-simulator-may-do-that-an-iphone-may-not)).

On a phone, `--repeat` refuses a check, and so does `--no-heal` on a check with steps, class `replay`. Mobster can't read the app's version on a phone through WebDriverAgent, so a fixed replay there can't hold a repeat to one build. Mobster refuses it before it takes the phone. Run it without `--repeat` or `--no-heal`, or from **Run checks** in Mobster for Mac: an ordinary phone check still replays its recorded steps.

### Let your agent drive a check

Over MCP, `verify_start` with your iPhone as `device` checks an app already on it. Mobster asks in Mobster for Mac before it opens an app you haven't let outside agents use. The check starts once you allow it.

Without `smart`, your coding agent drives the check with `tap`, `type_text`, `swipe`, `alert`, `open_url` and `launch_app` ([MCP server](https://docs.mobster.dev/mcp-server.md#drive-a-device-directly)). Each action passes the same permission check as when your agent drives the phone directly ([Who answers](https://docs.mobster.dev/safety.md#who-answers)):

* Mobster for Mac shows an approval card for a tap, a Return, a swipe or an alert's button that sends, buys, posts or deletes, a link that asks and a one-time code. Your agent can't answer it.
* Moving between screens and typing text go ahead without a card.

With `smart`, Mobster runs the steps by the rules [above](#run-checks-on-your-iphone), as in `mobster test`. On a simulator, Mobster for Mac doesn't ask. On an Android phone, your agent's own taps can't ask you, so `verify_start` there needs `smart`. Without it, use an Android emulator.

## Run on every plugged-in iPhone

```sh theme={"theme":"css-variables"}
mobster test --device all --sim "iPhone 17 Pro" --video
```

`--device all` is every iPhone that is ready on this Mac: plugged in, unlocked and set up in Mobster. Each phone runs its checks one at a time, beside the others and beside the simulators, and the run writes one report: every check down the side, every phone and simulator across the top. With none ready, it stops before anything runs.

## Spread checks across phones

```sh theme={"theme":"css-variables"}
mobster test --device "Test iPhone" --device "Test iPhone 2" --device "Test iPhone 3" --spread
```

Without `--spread`, every check runs on every device you name. With it, each check runs once, on whichever device is free: every device takes the next check from one queue, so three phones finish a suite about three times sooner than one. `--spread` needs two or more devices (phones, `--sim` types or both), and it stops before anything runs if you name fewer. Each phone still runs one check at a time, and only the command line or the runner's `.env` names a phone.

The report groups the checks by device. `results.json` has one result per check, with the device it ran on, and `junit.xml` has one `<testsuite>` per device. A check that never started appears under the device `Not run`.

If a phone stops answering while a check runs, that check is `couldnt_run` with class `device_lost`. Mobster doesn't run it again, on that phone or another: it can't tell how far the check got, and a step may already have sent something. The phone takes no more checks in this run, and the others carry on. Mobster passes over a phone that another copy of Mobster holds (class `busy`, before anything is tapped), and its check goes to another phone untouched. If every device is lost or busy, the checks left are `couldnt_run`, and the run exits 3.

A device that refuses a check (an App Store app, say) refuses only that check there. Mobster doesn't pass it to another phone, so name the same build on every phone.

## Test your TestFlight build

devicectl doesn't list a build from TestFlight as built by a developer, so it couldn't run, as an App Store app can't. When the app is yours, name its bundle ID with `--testflight`:

```sh theme={"theme":"css-variables"}
mobster test --device "Test iPhone" --testflight com.example.yourapp
```

Only the bundle IDs you name count, and only with `--device` on the command line. Apple's apps couldn't run even when named, and the app's data is still never cleared.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.