Appium Python Client 3.x introduced typed options classes — UiAutomator2Options for Android and XCUITestOptions for iOS. They replace the old dictionary-based capability approach with Python dataclasses that have IDE autocompletion and snake_case property names.
UiAutomator2Options
from appium.options import UiAutomator2Options
options = UiAutomator2Options()
# Required
options.device_name = "emulator-5554"
options.platform_version = "13"
options.app = "/absolute/path/to/app-debug.apk"
# Useful defaults
options.auto_grant_permissions = True
options.no_reset = True
options.new_command_timeout = 60
# App-specific (skip options.app if these are set)
options.app_package = "com.example.myapp"
options.app_activity = ".MainActivity"Properties are snake_case in Python — auto_grant_permissions, not autoGrantPermissions. The client translates them to the correct W3C capability names before sending to the server.
XCUITestOptions
from appium.options import XCUITestOptions
options = XCUITestOptions()
options.device_name = "iPhone 15"
options.platform_version = "17.0"
options.app = "/path/to/MyApp.app" # simulator build (.app)
options.no_reset = True
options.wda_startup_retries = 3
options.simulator_startup_timeout = 120_000 # millisecondsFor real devices:
options.udid = "00008030-001A234567890012"
options.xcode_org_id = "TEAM_ID_HERE"
options.xcode_signing_id = "Apple Development"Capability dictionary — the old way
For context, this is the pre-3.x approach:
# Old — avoid in new code
desired_caps = {
"platformName": "Android",
"appium:deviceName": "emulator-5554",
"appium:automationName": "UiAutomator2",
"appium:app": "/path/to/app.apk",
}
driver = webdriver.Remote("http://127.0.0.1:4723", desired_capabilities=desired_caps)The typed options classes are preferred because typos in capability names are caught at IDE time, not at session creation time.
Passing options to the driver
from appium import webdriver
driver = webdriver.Remote(
command_executor="http://127.0.0.1:4723",
options=options
)The command_executor is the Appium server URL. With appium --base-path /, this is http://127.0.0.1:4723 (no /wd/hub suffix).
Loading options from a JSON config
For teams that change device configurations frequently, read options from a JSON file:
{
"android": {
"device_name": "emulator-5554",
"platform_version": "13",
"app": "apps/app-debug.apk",
"auto_grant_permissions": true,
"no_reset": true
},
"ios": {
"device_name": "iPhone 15",
"platform_version": "17.0",
"app": "apps/MyApp.app",
"no_reset": true
}
}import json
from appium.options import UiAutomator2Options, XCUITestOptions
def load_options(platform: str):
with open("config/devices.json") as f:
config = json.load(f)
data = config[platform.lower()]
if platform.lower() == "android":
options = UiAutomator2Options()
for key, value in data.items():
setattr(options, key, value)
return options
else:
options = XCUITestOptions()
for key, value in data.items():
setattr(options, key, value)
return optionsApp reset strategies
| Property | Effect |
|---|---|
no_reset=True | Skip uninstall/reinstall — fastest |
no_reset=False (default) | Reinstall the app each session |
full_reset=True | Uninstall + wipe app data |
For most tests, no_reset=True combined with activate_app() / terminate_app() in test setup gives fast session creation while still starting the app fresh.
BrowserStack capabilities
When running on BrowserStack, add the bstack:options capability:
from appium.options import UiAutomator2Options
options = UiAutomator2Options()
options.device_name = "Samsung Galaxy S23"
options.platform_version = "13.0"
options.app = "bs://your-app-hash"
options.load_capabilities({
"bstack:options": {
"projectName": "Mobile Suite",
"buildName": "CI Build",
"networkLogs": True,
"deviceLogs": True,
}
})load_capabilities() lets you set arbitrary capability keys that don't have typed properties.