# iOS simulator apps

> Configure an iOS simulator app, enter text, tap controls and check the result.

Source: https://rehearsal.dev/retest/docs/platforms/ios

Use an iOS simulator target when a flow needs your native app. This page shows how to prepare the simulator, launch the app and check its accessibility elements through XCTest and WebDriverAgent.

> [!NOTE]
> **Requirements**
>
> You need Node.js 24.12 or later on an Apple silicon Mac, Xcode 26.5 build 17F42, iOS simulator runtime 26.5 build 23F77 and a simulator `.app` build of your app. Retest refuses a toolchain outside this pinned set.

## Prepare the executor

Build the pinned WebDriverAgent executor before running the suite. You need clean source checkouts of both WebDriverAgent and appium-mac2-driver because Retest checks licence files from both repositories.

```sh
npx retest install webdriveragent
```

This command builds from existing checkouts; it does not clone them. If they are missing, it prints the required paths, pinned commits and commands to prepare them. Follow those instructions, then run the install command again.

## Configure the app

Set the app bundle, simulator device type and runtime in `retest.config.ts`. This example uses TaskPhone from Retest's cross-platform fixture, which accepts arguments to reset its local state and choose its task service.

```ts filename="retest.config.ts"
import { defineConfig, env } from '@rehearsal-labs/retest'

const config = defineConfig({
  apps: {
    phone: {
      platform: 'ios-simulator',
      appPath: './TaskPhone.app',
      device: 'iPhone 17',
      runtime: '26.5',
      arguments: ['-reset', '-serviceURL', 'http://127.0.0.1:4310'],
    },
  },
  secrets: { password: env('TASK_APP_PASSWORD') },
  secretOrigins: { password: ['dev.retest.fixtures.taskphone'] },
})

export default config

declare module '@rehearsal-labs/retest' {
  interface Register {
    config: typeof config
  }
}
```

`appPath` is relative to the config file. Replace the fixture's arguments with settings your app understands, and use its own bundle identifier in `secretOrigins`. Native secrets are allowed by bundle identifier, rather than by web origin.

```sh
npx retest doctor
```

For a native target, doctor checks the cached executor and the app bundle's presence without starting them. A run checks Xcode, the runtime and the app itself, so a successful doctor check does not prove the simulator will launch.

## Write a native test

`getByTestId` finds a native element by its accessibility identifier. The following test signs in to TaskPhone, creates a task and checks the title the app shows after saving it.

```ts filename="tests/phone.retest.ts"
import { expect, secret, test } from '@rehearsal-labs/retest'

test('creates a task on the phone', { apps: ['phone'] }, async ({ phone }) => {
  await phone.getByTestId('account-field').fill('ada')
  await phone.getByTestId('password-field').fill(secret('password'))
  await phone.getByTestId('sign-in-button').tap()
  await expect(phone.getByTestId('signed-in-account')).toHaveText('ada')

  await phone.getByTestId('new-task-title-field').fill('Release checklist')
  await phone.getByTestId('create-task-button').tap()
  await expect(phone.getByTestId('created-task-title')).toHaveText('Release checklist')
  await expect(phone.getByTestId('created-task-state')).toHaveText('Open')
})
```

Registering the config gives the test a typed `phone` handle. Use `tap()` for controls and `fill()` for text fields. Checks read the native accessibility tree again until they pass or reach their deadline. Use `toHaveValue` when checking an editable field.

```sh
npx retest run tests/phone.retest.ts
```

## State and supported operations

Retest creates a fresh simulator for each test and deletes it during shutdown. Your backend data remains external, so prepare the account and task service separately. The fixture's `-reset` argument asks the app to reset; it does not reset the server.

You can also find elements by supported roles, labels and text, then scope a finder inside another element. CSS, placeholder lookup, web navigation and saved browser state are unavailable. A test may use one app per simulator device type and runtime.

> [!NOTE]
> **Gesture coverage**
>
> The API includes `swipe`, software keyboard helpers and labelled alert buttons. These helpers have been checked with a stand-in executor, but have not been exercised on a real simulator. Android and real iOS devices are not supported.

When an action has an unknown outcome, Retest records that result and does not resend the input. The run folder keeps native session details and available evidence, so you can investigate with [Debugging a failure](https://rehearsal.dev/retest/docs/essentials/debugging.md).

## Next steps

- [Signing in and secrets](https://rehearsal.dev/retest/docs/guides/signing-in.md): Give the app credentials through named secrets.
- [One test across platforms](https://rehearsal.dev/retest/docs/platforms/across-platforms.md): Check that a task syncs between native and web apps.
- [Test API](https://rehearsal.dev/retest/docs/reference/test-api.md): Look up native finders, actions and checks.
