On this page6 sections
CommandsIntermediate6-8 min reference

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 emulator

iOS-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.

  1. Start the Appium server (appium on port 4723 by default).
  2. Open Inspector, paste your capabilities, click Start Session.
  3. Tap an element in the screenshot pane → properties (id, label, xpath) appear on the right.
  4. 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"); // iOS

Common errors

ErrorLikely cause
Could not find a driver for automationNameDriver not installed (appium driver install uiautomator2) or capabilities missing the appium: prefix
An unknown server-side error occurred while processing the commandApp crashed; check logcat / device console
NoSuchSessionExceptionSession expired (raise newCommandTimeout) or app was force-killed
Element not found after findElement succeeded earlierElement 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.