Appium Commands
A practical reference for Appium automation across iOS and Android. Examples use the Java client; the same APIs exist in Python, JavaScript, C#, and Ruby with minor syntax differences.
Desired Capabilities
Capabilities tell the Appium server what device, OS, and app to drive. Use the W3C appium: prefix for vendor-specific keys.
Common (cross-platform)
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("platformName", "Android"); // or "iOS"
caps.setCapability("appium:deviceName", "Pixel 7");
caps.setCapability("appium:automationName", "UiAutomator2"); // iOS: "XCUITest"
caps.setCapability("appium:app", "/abs/path/to/app.apk"); // .ipa for iOS
caps.setCapability("appium:noReset", true);
caps.setCapability("appium:newCommandTimeout", 120);Android-specific
caps.setCapability("appium:appPackage", "com.example.app");
caps.setCapability("appium:appActivity", "com.example.app.MainActivity");
caps.setCapability("appium:noReset", true); // keep app data between sessions
caps.setCapability("appium:fullReset", false); // uninstall + reinstall
caps.setCapability("appium:autoGrantPermissions", true);
caps.setCapability("appium:avd", "Pixel_7_API_34"); // launch this emulatoriOS-specific
caps.setCapability("appium:bundleId", "com.example.app");
caps.setCapability("appium:udid", "00008030-001C2D8E12345678"); // real device
caps.setCapability("appium:xcodeOrgId", "ABCDEF1234");
caps.setCapability("appium:xcodeSigningId", "iPhone Developer");
caps.setCapability("appium:platformVersion", "17.4");
caps.setCapability("appium:autoAcceptAlerts", true);Mobile browser (web on device)
caps.setCapability("platformName", "Android");
caps.setCapability("browserName", "Chrome"); // iOS: "Safari"
caps.setCapability("appium:automationName", "UiAutomator2");W3C vs legacy JSONWP
Modern Appium servers (≥ 2.0) require W3C capabilities — vendor keys carry the appium: prefix. The pre-W3C JSONWP format (no prefix) is removed in Appium 2. If you see Could not find a driver for automationName 'UiAutomator2' after upgrading, the most common cause is missing appium: prefixes.
Locator Strategies
Pick the most stable, least implementation-coupled strategy your app exposes — usually accessibility ID or resource ID, not XPath.
By ID (Android resource-id)
driver.findElement(By.id("com.example.app:id/loginButton"));By Accessibility ID — best cross-platform option
Maps to content-desc on Android and accessibilityIdentifier on iOS.
import io.appium.java_client.AppiumBy;
driver.findElement(AppiumBy.accessibilityId("Submit"));By XPath — last resort, brittle on dynamic UIs
driver.findElement(By.xpath("//android.widget.Button[@text='Login']"));
driver.findElement(By.xpath("//XCUIElementTypeButton[@name='Login']"));By class name
driver.findElement(By.className("android.widget.EditText"));
driver.findElement(By.className("XCUIElementTypeTextField"));Android UIAutomator (powerful, Android-only)
driver.findElement(AppiumBy.androidUIAutomator(
"new UiSelector().text(\"Login\")"));
driver.findElement(AppiumBy.androidUIAutomator(
"new UiSelector().resourceId(\"com.example.app:id/loginButton\").enabled(true)"));
// scroll into view AND find — single line
driver.findElement(AppiumBy.androidUIAutomator(
"new UiScrollable(new UiSelector().scrollable(true))" +
".scrollIntoView(new UiSelector().text(\"Settings\"))"));iOS Predicate string
NSPredicate evaluation runs in-process — much faster than XPath.
driver.findElement(AppiumBy.iOSNsPredicateString("label == 'Submit'"));
driver.findElement(AppiumBy.iOSNsPredicateString(
"type == 'XCUIElementTypeButton' AND visible == true"));
driver.findElement(AppiumBy.iOSNsPredicateString(
"label CONTAINS[c] 'login'"));iOS Class Chain
XPath-style traversal but evaluated by the underlying iOS test framework — much faster than XPath.
driver.findElement(AppiumBy.iOSClassChain(
"**/XCUIElementTypeButton[`label == 'Login'`]"));
driver.findElement(AppiumBy.iOSClassChain(
"**/XCUIElementTypeCell[`name BEGINSWITH 'Order'`][1]"));Actions & Interactions
Basic interactions
WebElement el = driver.findElement(AppiumBy.accessibilityId("Email"));
el.click();
el.sendKeys("ada@example.com");
el.clear();
String text = el.getText();
String enabled = el.getAttribute("enabled");
boolean isDisplayed = el.isDisplayed();Tap by coordinates (W3C Actions)
The pre-W3C TouchAction API is deprecated. Use the PointerInput actions API:
import org.openqa.selenium.interactions.*;
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence tap = new Sequence(finger, 0)
.addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), 200, 600))
.addAction(finger.createPointerDown(0))
.addAction(finger.createPointerUp(0));
driver.perform(List.of(tap));Long press
Sequence longPress = new Sequence(finger, 0)
.addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), x, y))
.addAction(finger.createPointerDown(0))
.addAction(finger.createPointerMove(Duration.ofMillis(800),
PointerInput.Origin.viewport(), x, y))
.addAction(finger.createPointerUp(0));
driver.perform(List.of(longPress));Swipe
int startX = 500, startY = 1500;
int endX = 500, endY = 500; // swipe up
Sequence swipe = new Sequence(finger, 0)
.addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), startX, startY))
.addAction(finger.createPointerDown(0))
.addAction(finger.createPointerMove(Duration.ofMillis(300), PointerInput.Origin.viewport(), endX, endY))
.addAction(finger.createPointerUp(0));
driver.perform(List.of(swipe));Or use the executor shortcuts (much shorter):
// Android — UiAutomator2
driver.executeScript("mobile: swipeGesture", Map.of(
"left", 100, "top", 1000, "width", 800, "height", 500,
"direction", "up", "percent", 0.75));
// iOS — XCUITest
driver.executeScript("mobile: swipe", Map.of("direction", "up"));Pinch and zoom
driver.executeScript("mobile: pinchOpenGesture", Map.of(
"elementId", ((RemoteWebElement) imageEl).getId(),
"percent", 0.75));
driver.executeScript("mobile: pinchCloseGesture", Map.of(
"elementId", ((RemoteWebElement) imageEl).getId(),
"percent", 0.75));Keyboard
driver.hideKeyboard(); // both platforms
driver.isKeyboardShown();
((PressesKey) driver).pressKey(new KeyEvent(AndroidKey.BACK));
((PressesKey) driver).pressKey(new KeyEvent(AndroidKey.ENTER));Hybrid app — switching context
Set<String> contexts = driver.getContextHandles();
// → { "NATIVE_APP", "WEBVIEW_com.example.app" }
driver.context("WEBVIEW_com.example.app");
// now standard web Selenium APIs work — driver.findElement(By.cssSelector(...))
driver.context("NATIVE_APP");Assertions & Waits
Avoid Thread.sleep — use explicit waits.
Implicit wait (set once)
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));Explicit wait — WebDriverWait + ExpectedConditions
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement el = wait.until(ExpectedConditions.visibilityOfElementLocated(
AppiumBy.accessibilityId("Submit")));
wait.until(ExpectedConditions.elementToBeClickable(
AppiumBy.accessibilityId("Submit")));
wait.until(ExpectedConditions.presenceOfElementLocated(
AppiumBy.id("com.example.app:id/result")));
wait.until(ExpectedConditions.textToBePresentInElement(banner, "Welcome"));
wait.until(ExpectedConditions.invisibilityOfElementLocated(
AppiumBy.accessibilityId("Loading")));Custom condition
wait.until(d -> {
String state = d.findElement(byStateLabel).getText();
return "READY".equals(state);
});Element-state assertions
assertTrue(el.isDisplayed());
assertTrue(el.isEnabled());
assertTrue(el.isSelected());
assertEquals("Submit", el.getText());Device Interaction
Orientation
import org.openqa.selenium.ScreenOrientation;
driver.rotate(ScreenOrientation.LANDSCAPE);
ScreenOrientation orientation = driver.getOrientation();Device time and timezone
String time = driver.getDeviceTime(); // "2026-05-03T10:00:00+0000"
String iso = driver.getDeviceTime("YYYY-MM-DD");Files
// Push test fixture to the device
driver.pushFile("/sdcard/Download/seed.json", new File("./fixtures/seed.json"));
// Pull a generated file off the device
byte[] bytes = driver.pullFile("/sdcard/Documents/report.pdf");
Files.write(Path.of("./report.pdf"), bytes);App management
driver.installApp("/path/to/app.apk");
driver.isAppInstalled("com.example.app");
driver.activateApp("com.example.app"); // bring to foreground
driver.terminateApp("com.example.app"); // kill the process
driver.removeApp("com.example.app"); // uninstall
driver.runAppInBackground(Duration.ofSeconds(5));Network and system toggles
import io.appium.java_client.android.connection.ConnectionState;
import io.appium.java_client.android.connection.ConnectionStateBuilder;
// Android — set wifi+data+airplane in one call
((HasNetworkConnection) driver).setConnection(
new ConnectionStateBuilder()
.withWiFiEnabled()
.withDataEnabled()
.withAirplaneModeDisabled()
.build());
// Toggle individual flags
((HasNetworkConnection) driver).toggleAirplaneMode();
((HasNetworkConnection) driver).toggleWifi();
((HasNetworkConnection) driver).toggleData();
((HasLocation) driver).setLocation(new Location(40.7128, -74.0060, 10));Screenshots
File png = driver.getScreenshotAs(OutputType.FILE);
Files.copy(png.toPath(), Path.of("./screenshots/login-fail.png"),
StandardCopyOption.REPLACE_EXISTING);
String base64 = driver.getScreenshotAs(OutputType.BASE64);Appium Inspector & Debugging
Appium Inspector
The desktop GUI for inspecting the live UI hierarchy and trying selectors before committing them to test code.
- Start the Appium server (
appiumon port 4723 by default). - Open Inspector, paste your capabilities, click Start Session.
- Tap an element in the screenshot pane → properties (id, label, xpath) appear on the right.
- Click Tap and select to record actions.
Tips:
- Save your capabilities as a Cloud Provider preset to avoid retyping.
- Use Recorder to rough out a script, then clean it up by hand.
- Search on the right-hand pane lets you test a locator and see how many elements it matches.
Page source
String xml = driver.getPageSource();
Files.writeString(Path.of("./debug-page.xml"), xml);Useful when an element you can see in the Inspector still doesn't show up in findElement — diff the page source against what the Inspector shows.
Logging
Enable verbose driver logs by capability:
caps.setCapability("appium:printPageSourceOnFindFailure", true);Or grab logs after a failed step:
LogEntries logs = driver.manage().logs().get("logcat"); // Android
LogEntries crashLogs = driver.manage().logs().get("crashlog"); // iOSCommon errors
| Error | Likely cause |
|---|---|
Could not find a driver for automationName | Driver not installed (appium driver install uiautomator2) or capabilities missing the appium: prefix |
An unknown server-side error occurred while processing the command | App crashed; check logcat / device console |
NoSuchSessionException | Session expired (raise newCommandTimeout) or app was force-killed |
Element not found after findElement succeeded earlier | Element re-rendered — use a stale-element-aware retry or explicit wait |
Cannot start the 'com.example.app' application (iOS) | bundleId mismatch or signing issue — verify with xcrun simctl listapps booted |
Inspect the live session
Map<String, Object> sessionCaps = driver.getCapabilities().asMap();
System.out.println(sessionCaps);This is what the server actually sees — handy when capabilities aren't applying as expected.