Skip to main content

IOSDriver API Reference

IOSDriver

class IOSDriver(DeviceDriver)
iOS device driver communicating via HTTP REST to the iOS Portal app. Status: iOS support is in beta with limited functionality compared to AndroidDriver. Key Limitations:
  • get_date() - Not available (returns empty string)
  • drag() - Not supported (not in supported set)
  • press_key() - Only HOME is mapped; BACK and ENTER have no iOS equivalent
  • input_text() - clear parameter is ignored

IOSDriver.__init__

def __init__(
    url: str,
    bundle_identifiers: List[str] | None = None
) -> None
Initialize the IOSDriver instance. Arguments:
  • url str - iOS Portal app URL (e.g., “http://192.168.1.100:8080”)
  • bundle_identifiers List[str] | None - Optional list of custom app bundle identifiers
Usage:
from droidrun.tools.driver import IOSDriver

# Connect to iOS device
driver = IOSDriver(url="http://192.168.1.100:8080")

# With specific bundle identifiers
driver = IOSDriver(
    url="http://192.168.1.100:8080",
    bundle_identifiers=["com.example.app1", "com.example.app2"]
)
Supported methods:
IOSDriver.supported = {
    "tap", "swipe", "input_text", "press_key",
    "start_app", "screenshot", "get_ui_tree",
    "list_packages", "get_apps",
}
Setup Requirements:
  1. Install Droidrun iOS Portal app on device
  2. Launch Portal app (starts HTTP server)
  3. Connect device and computer to same network
  4. Use displayed URL to initialize IOSDriver

Lifecycle Methods

IOSDriver.connect

async def connect() -> None
Create an HTTP client connection to the iOS Portal app.

IOSDriver.ensure_connected

async def ensure_connected() -> None
Connect if not already connected. Safe to call multiple times.

Input Action Methods

IOSDriver.tap

async def tap(x: int, y: int) -> None
Tap at absolute pixel coordinates. Arguments:
  • x int - X coordinate
  • y int - Y coordinate
Usage:
await driver.tap(200, 400)
Notes:
  • Stores tap coordinates internally for input_text() targeting

IOSDriver.swipe

async def swipe(
    x1: int,
    y1: int,
    x2: int,
    y2: int,
    duration_ms: float = 1000,
) -> None
Perform directional swipe gesture (up, down, left, right). Arguments:
  • x1 int - Starting X coordinate
  • y1 int - Starting Y coordinate
  • x2 int - Ending X coordinate
  • y2 int - Ending Y coordinate
  • duration_ms float - Duration in milliseconds (ignored on iOS)
Usage:
# Swipe up (scroll down)
await driver.swipe(200, 800, 200, 200)

# Swipe left
await driver.swipe(600, 400, 100, 400)
Notes:
  • iOS uses directional swipes, not precise coordinates
  • Direction calculated from coordinate delta (largest axis wins)
  • duration_ms parameter is ignored by iOS API

IOSDriver.input_text

async def input_text(text: str, clear: bool = False) -> bool
Input text into the currently focused element. Arguments:
  • text str - Text to input (supports Unicode)
  • clear bool - Not supported on iOS (ignored)
Returns:
  • bool - True if input succeeded, False otherwise
Usage:
# Tap text field first
await driver.tap(200, 400)

# Input text
success = await driver.input_text("Hello World")
Notes:
  • Must tap text field before calling this method
  • Uses last tapped coordinates for targeting
  • clear parameter is ignored on iOS

IOSDriver.press_key

async def press_key(keycode: int) -> None
Press a hardware key. Supported keycodes:
  • 3 (Android HOME) maps to iOS HOME button
Unsupported keycodes (BACK=4, ENTER=66, etc.) are silently ignored with a warning log. Arguments:
  • keycode int - Android-style keycode (translated to iOS internally)
Usage:
await driver.press_key(3)  # Home button

App Management Methods

IOSDriver.start_app

async def start_app(package: str, activity: str | None = None) -> str
Launch an app by bundle identifier. Arguments:
  • package str - Bundle identifier (e.g., “com.apple.MobileSMS”)
  • activity str | None - Ignored on iOS (for API compatibility)
Returns:
  • str - Result message
Common bundle identifiers:
  • Messages: com.apple.MobileSMS
  • Safari: com.apple.mobilesafari
  • Settings: com.apple.Preferences
  • Mail: com.apple.mobilemail
  • Calendar: com.apple.mobilecal
  • Photos: com.apple.mobileslideshow
  • Maps: com.apple.Maps
  • Contacts: com.apple.MobileAddressBook
Usage:
result = await driver.start_app("com.apple.MobileSMS")
result = await driver.start_app("com.apple.mobilesafari")

IOSDriver.list_packages

async def list_packages(include_system: bool = False) -> List[str]
List known bundle identifiers. Arguments:
  • include_system bool - Include system apps (default: False)
Returns:
  • List[str] - List of bundle identifiers
Notes:
  • Returns union of bundle_identifiers + system apps (if included)
  • Does not query device for installed apps

IOSDriver.get_apps

async def get_apps(include_system: bool = True) -> List[Dict[str, str]]
Return known apps as list of dicts. Since iOS has no app listing endpoint, this returns bundle identifiers as both ‘package’ and ‘label’. Arguments:
  • include_system bool - Include system apps (default: True)
Returns:
  • List[Dict[str, str]] - List of dicts with ‘package’ and ‘label’ keys (both set to bundle identifier)

State and Observation Methods

IOSDriver.screenshot

async def screenshot(hide_overlay: bool = True) -> bytes
Capture device screen as raw PNG bytes. Arguments:
  • hide_overlay bool - Unused on iOS (for API compatibility)
Returns:
  • bytes - Raw PNG image data
Usage:
png_bytes = await driver.screenshot()
with open("screenshot.png", "wb") as f:
    f.write(png_bytes)

IOSDriver.get_ui_tree

async def get_ui_tree() -> Dict[str, Any]
Return raw iOS accessibility data and phone state. Returns: Dictionary with:
  • a11y_raw - The raw accessibility tree text from the portal
  • phone_state - Dict with currentApp and keyboardVisible
Usage:
tree = await driver.get_ui_tree()
print(tree["phone_state"]["currentApp"])

IOSDriver.get_date

async def get_date() -> str
Not available on iOS. Returns an empty string.

Unsupported Methods

The following DeviceDriver methods are not in supported and will raise NotImplementedError:

drag()

Not supported on iOS. Not declared in supported set.

install_app()

Not supported on iOS. Not declared in supported set.

Instance Properties

driver.url                    # iOS Portal URL
driver.bundle_identifiers     # Custom bundle IDs
driver.supported              # Set of supported method names

iOS vs Android Differences

FeatureIOSDriverAndroidDriver
Back buttonNot availableFull Android keycodes
SwipeDirection-basedCoordinate-based
DragNot supportedDeclared (not yet implemented)
App IDsBundle identifiersPackage names
Key codesHOME onlyFull Android keycodes
ConnectionHTTP (Portal app)ADB over USB/TCP
get_date()Returns emptyVia ADB shell
input_text() clearIgnoredSupported
Setup differences: Android: USB or adb connect + Portal APK iOS: Portal app + same network + HTTP connection

Example Usage

import asyncio
from droidrun.tools.driver import IOSDriver

async def main():
    # Initialize and connect
    driver = IOSDriver(url="http://192.168.1.100:8080")
    await driver.connect()

    # Launch Messages
    result = await driver.start_app("com.apple.MobileSMS")
    print(result)

    # Get UI tree
    tree = await driver.get_ui_tree()
    print(tree["phone_state"]["currentApp"])

    # Tap at coordinates
    await driver.tap(200, 400)

    # Input text
    await driver.input_text("Hello from Droidrun!")

    # Take screenshot
    png_bytes = await driver.screenshot()
    with open("screenshot.png", "wb") as f:
        f.write(png_bytes)

asyncio.run(main())
Note: For higher-level interactions with element indexing and structured results, use action functions with ActionContext (see DroidAgent) rather than calling the driver directly.

See Also