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

# Physical iOS Device Setup

> Run mobile automation on your physical iPhone or iPad via USB

This guide covers the one-time setup required to automate physical iOS devices connected via USB using WebDriverAgent (WDA).

<Note>
  **Looking for other options?** Check out the [Local Quickstart](/docs/mobile-use-sdk/quickstart) for Android/iOS simulators or [Cloud Mobile Quickstart](/docs/mobile-use-sdk/cloud-quickstart) for zero-setup cloud devices.
</Note>

<Info>
  Make sure you've completed the [Installation](/docs/mobile-use-sdk/installation) steps before proceeding.
</Info>

## What You'll Need

<CardGroup cols={2}>
  <Card title="macOS with Xcode" icon="apple">
    Required for code signing and building WDA
  </Card>

  <Card title="Physical iOS Device" icon="mobile-screen">
    iPhone or iPad connected via USB cable
  </Card>

  <Card title="Node.js & npm" icon="node-js">
    For installing Appium and drivers
  </Card>

  <Card title="Apple Developer Account" icon="user">
    Free account works - needed for code signing
  </Card>
</CardGroup>

***

## One-Time Setup

<Steps>
  <Step title="Install Appium">
    Install Appium globally via npm:

    ```bash theme={null}
    npm install -g appium
    ```
  </Step>

  <Step title="Install XCUITest Driver">
    Install the XCUITest driver which includes WebDriverAgent:

    ```bash theme={null}
    appium driver install xcuitest
    ```

    This installs WebDriverAgent at:
    `~/.appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent/`.
  </Step>

  <Step title="Configure Code Signing">
    WebDriverAgent must be code-signed to run on physical devices. This is a **one-time setup**.

    ### Option A: Using Xcode (Recommended)

    ```bash theme={null}
    # Open the WebDriverAgent project in Xcode
    open ~/.appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent/WebDriverAgent.xcodeproj
    ```

    In Xcode:

    1. Open the Project Navigator (panel on the left), then select the **WebDriverAgent project** at the top.
    2. Select the **WebDriverAgentRunner** target (top-left dropdown)
    3. Go to **Signing & Capabilities** tab
    4. Check **"Automatically manage signing"**
    5. Select your **Team** (your Apple ID)
    6. Change the iOS **Bundle Identifier** to something unique:

       * Example: `com.yourname.WebDriverAgentRunner`
       * Must be unique across all Apple apps

           <Warning>
             **Don't forget**: Repeat the same steps for the **WebDriverAgentLib** and **IntegrationApp** targets.
           </Warning>
    7. Select your physical device as the run destination in Xcode, then build the IntegrationApp target once. This will trigger the code-signing process and install the required provisioning profile on your device.
       ### Option B: Using CLI (Advanced)

    If you prefer command-line signing:

    ```bash theme={null}
    # Set your development team ID
    TEAM_ID="YOUR_TEAM_ID"

    # Navigate to WDA directory
    cd ~/.appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent/

    # Update Bundle ID and Team in the project
    sed -i '' "s/PRODUCT_BUNDLE_IDENTIFIER = .*/PRODUCT_BUNDLE_IDENTIFIER = com.yourname.WebDriverAgentRunner;/" WebDriverAgent.xcodeproj/project.pbxproj
    sed -i '' "s/DEVELOPMENT_TEAM = .*/DEVELOPMENT_TEAM = $TEAM_ID;/" WebDriverAgent.xcodeproj/project.pbxproj
    ```

    <Note>
      You'll still need to get your Team ID from Xcode or your Apple Developer account.
    </Note>
  </Step>

  <Step title="Trust Developer Certificate on Device">
    After the first WDA build, trust the developer certificate on your iOS device:

    1. On your device: **Settings → General → VPN & Device Management**
    2. Tap on your developer certificate (your Apple ID)
    3. Tap **Trust**

    <Tip>
      You only need to do this once per developer certificate.
    </Tip>
  </Step>
</Steps>

***

## Using Your Physical iOS Device

After the one-time setup, mobile-use handles everything automatically:

* ✅ Starts **iproxy** for USB port forwarding
* ✅ Builds and runs **WebDriverAgent** via xcodebuild
* ✅ Connects to WDA and waits for it to be ready
* ✅ Cleans up processes when your script exits

### Basic Example

```python physical_device_demo.py theme={null}
import asyncio
from minitap.mobile_use.sdk import Agent

async def main():
    # Create agent - automatically detects connected iOS device
    agent = Agent()
    
    # Initialize - WDA, iproxy, and xcodebuild start automatically
    await agent.init()
    
    # Run tasks on your physical device
    result = await agent.run_task(
        goal="Open Safari and search for 'mobile automation'",
    )
    
    print(f"Task completed: {result.status}")

if __name__ == "__main__":
    asyncio.run(main())
```

<Tip>
  The agent automatically detects your connected iOS device. No UDID needed unless you have multiple devices connected!
</Tip>

### Multiple Devices

If you have multiple devices connected, specify which one to use:

```python multiple_devices.py theme={null}
from minitap.mobile_use.sdk import Agent
from minitap.mobile_use.sdk.builders import Builders

async def main():
    config = (
        Builders.AgentConfig
        .for_device("YOUR_DEVICE_UDID", platform="ios")
        .build()
    )
    
    agent = Agent(config=config)
    await agent.init()
    
    # Your automation here
```

<Accordion title="How to find your device UDID">
  ```bash theme={null}
  # Option 1: Using idevice_id
  idevice_id -l

  # Option 2: Using Xcode
  # Window → Devices and Simulators → Select your device
  # The UDID is shown in the device info
  ```
</Accordion>

***

## Advanced Configuration

### Managing WDA Externally

For debugging or custom setups, you can disable auto-start and manage iproxy/WDA manually:

```python manual_wda_setup.py theme={null}
from minitap.mobile_use.sdk import Agent
from minitap.mobile_use.sdk.builders import Builders
from minitap.mobile_use.clients.ios_client_config import IosClientConfig, WdaClientConfig

async def main():
    # Create config to disable auto-starting
    ios_config = IosClientConfig(
        wda=WdaClientConfig(
            auto_start_iproxy=False,  # Don't auto-start iproxy
            auto_start_wda=False,      # Don't auto-start WDA
            wda_url="http://localhost:8100"
        )
    )

    config = (
        Builders.AgentConfig
        .for_device("YOUR_DEVICE_UDID", platform="ios")
        .with_ios_client_config(ios_config)
        .build()
    )

    agent = Agent(config=config)
    await agent.init()
    
    # Your automation here
```

When auto-start is disabled, manually start the required processes:

<CodeGroup>
  ```bash Terminal 1: iproxy theme={null}
  # Start USB port forwarding
  iproxy 8100 8100 -u YOUR_DEVICE_UDID
  ```

  ```bash Terminal 2: WDA theme={null}
  # Build and run WebDriverAgent
  cd ~/.appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent/

  xcodebuild test \
    -project WebDriverAgent.xcodeproj \
    -scheme WebDriverAgentRunner \
    -destination 'id=YOUR_DEVICE_UDID' \
    -allowProvisioningUpdates
  ```
</CodeGroup>

<Accordion title="Available WDA Configuration Options">
  | Option                | Type          | Default                   | Description                             |
  | --------------------- | ------------- | ------------------------- | --------------------------------------- |
  | `auto_start_iproxy`   | `bool`        | `True`                    | Auto-start USB port forwarding          |
  | `auto_start_wda`      | `bool`        | `True`                    | Auto-build and run WDA via xcodebuild   |
  | `wda_url`             | `str`         | `"http://localhost:8100"` | WDA server URL                          |
  | `wda_project_path`    | `str \| None` | `None`                    | Custom path to WebDriverAgent.xcodeproj |
  | `wda_startup_timeout` | `float`       | `120.0`                   | Max seconds to wait for WDA startup     |
</Accordion>

***

## How It Works

```mermaid theme={null}
graph LR
    A[Python WDA Client] -->|HTTP :8100| B[iproxy]
    B -->|USB Port Forward| C[iPhone WDA Server :8100]
    
    style A fill:#4F46E5,stroke:#4338CA,stroke-width:2px,color:#fff
    style B fill:#059669,stroke:#047857,stroke-width:2px,color:#fff
    style C fill:#DC2626,stroke:#B91C1C,stroke-width:2px,color:#fff
```

<Steps>
  <Step title="iproxy Port Forwarding">
    **iproxy** forwards USB traffic from device port 8100 to localhost:8100
  </Step>

  <Step title="WDA Build & Deploy">
    **xcodebuild** builds WDA (if needed) and deploys it to your device
  </Step>

  <Step title="WDA Server Running">
    **WDA server** runs on the device, listening on port 8100
  </Step>

  <Step title="Python Client Connection">
    **Python client** connects to localhost:8100 via iproxy
  </Step>
</Steps>

<Info>
  The WDA wrapper automatically manages iproxy and xcodebuild processes, cleaning them up when your script exits.
</Info>

## Getting Your Device UDID

```bash theme={null}
# List connected devices
idevice_id -l

# Or use Xcode
# Window → Devices and Simulators → Select your device
```

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="&#x22;Untrusted Developer&#x22; error">
    **Solution**: Trust the developer certificate on your device

    1. Go to **Settings → General → VPN & Device Management**
    2. Tap on your developer certificate
    3. Tap **Trust**
  </Accordion>

  <Accordion title="&#x22;A valid provisioning profile was not found&#x22;">
    **Possible causes**:

    * No Team selected in Xcode
    * Bundle Identifier conflict
    * Device not registered in Apple Developer account

    **Solutions**:

    1. Open the WDA project in Xcode
    2. Verify a Team is selected for both targets
    3. Try a different Bundle Identifier (e.g., `com.yourname.WebDriverAgentRunner`)
    4. Check device registration in your Apple Developer account
  </Accordion>

  <Accordion title="&#x22;iPhone is locked&#x22; error">
    **Solution**: Keep your device unlocked

    * Unlock your device before running automation
    * Keep it unlocked during the initial WDA build
    * Consider disabling auto-lock temporarily: **Settings → Display & Brightness → Auto-Lock → Never**
  </Accordion>

  <Accordion title="Build failures or xcodebuild errors">
    **Debugging steps**:

    1. **First build takes time**: Initial builds can take 1-2 minutes
    2. **Check Xcode**: Open the project in Xcode for detailed error messages
    3. **Verify signing**: Ensure code signing is configured for both `WebDriverAgentRunner` and `WebDriverAgentLib` targets
    4. **Clean build**: In Xcode, go to Product → Clean Build Folder
    5. **Update Xcode**: Make sure you're running the latest version
  </Accordion>

  <Accordion title="Connection timeout or WDA not responding">
    **Troubleshooting**:

    1. Verify WDA is running on device (you should see the WDA screen)
    2. Check iproxy is running: `ps aux | grep iproxy`
    3. Test WDA manually: `curl http://localhost:8100/status`
    4. Restart your device and try again
    5. Increase timeout in config: `wda_startup_timeout=180.0`
  </Accordion>
</AccordionGroup>

<Note>
  For more troubleshooting help, check the [Troubleshooting Guide](/docs/mobile-use-sdk/troubleshooting) or join our [Discord community](https://discord.gg/6nSqmQ9pQs).
</Note>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Explore Examples" icon="flask" href="/docs/mobile-use-sdk/examples/simple-photo-organizer">
    Check out real-world automation examples
  </Card>

  <Card title="Learn Core Concepts" icon="lightbulb" href="/docs/mobile-use-sdk/core-concepts/overview">
    Understand tasks, profiles, and builders
  </Card>

  <Card title="Platform Integration" icon="chart-line" href="/docs/mobile-use-sdk/platform-quickstart">
    Add observability with Minitap Platform
  </Card>

  <Card title="SDK Reference" icon="code" href="/docs/mobile-use-sdk/sdk-reference/agent">
    Dive into the complete API documentation
  </Card>
</CardGroup>
