AppiumBy extends Selenium's By class with mobile-specific locator strategies. Choosing the right strategy affects both stability and speed — accessibility ID is the gold standard, platform-specific selectors are the power tool.
Importing AppiumBy
from appium.webdriver.common.appiumby import AppiumByAccessibility ID — cross-platform locator
Accessibility IDs work on both Android (content-description) and iOS (accessibility identifier). They're the most stable locator because they're designed specifically for programmatic access:
from appium.webdriver.common.appiumby import AppiumBy
# Android: <View android:contentDescription="loginButton" />
# iOS: view.accessibilityIdentifier = "loginButton"
element = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "loginButton")
element.click()Use ACCESSIBILITY_ID as your first choice. Fall back to platform-specific strategies only when accessibility IDs aren't set on the element.
Android UIAutomator selector
ANDROID_UIAUTOMATOR accepts UIAutomator2's selector syntax as a string:
# By text
driver.find_element(
AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().text("Sign In")'
)
# By text contains
driver.find_element(
AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().textContains("Sign")'
)
# By resource-id
driver.find_element(
AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().resourceId("com.example.app:id/login_btn")'
)
# Scroll to element (finds off-screen elements)
driver.find_element(
AppiumBy.ANDROID_UIAUTOMATOR,
'new UiScrollable(new UiSelector().scrollable(true))'
'.scrollIntoView(new UiSelector().text("Terms of Service"))'
)The UiScrollable.scrollIntoView() pattern is the most practical approach for list items — scroll until found, then return.
iOS Predicate String
Predicate strings are evaluated natively by XCUITest, making them faster than XPath:
# By label
driver.find_element(
AppiumBy.IOS_PREDICATE,
"label == 'Sign In'"
)
# By name (accessibility identifier)
driver.find_element(
AppiumBy.IOS_PREDICATE,
"name == 'loginButton'"
)
# Multiple conditions
driver.find_element(
AppiumBy.IOS_PREDICATE,
"type == 'XCUIElementTypeTextField' AND placeholderValue == 'Email'"
)
# Contains
driver.find_element(
AppiumBy.IOS_PREDICATE,
"label CONTAINS 'Sign'"
)iOS Class Chain
Class chains allow parent-child navigation in the element hierarchy:
# Direct child
driver.find_element(
AppiumBy.IOS_CLASS_CHAIN,
"**/XCUIElementTypeButton[`label == 'Sign In'`]"
)
# Second button inside a table cell
driver.find_element(
AppiumBy.IOS_CLASS_CHAIN,
"**/XCUIElementTypeCell[1]/XCUIElementTypeButton[2]"
)XPath — last resort
XPath works but is slow. Avoid it unless no other strategy reaches the element:
# Android XPath (uses Android class names)
driver.find_element(By.XPATH, "//android.widget.Button[@text='Sign In']")
# iOS XPath (uses XCUIElement type names)
driver.find_element(By.XPATH, "//XCUIElementTypeButton[@name='Sign In']")Performance impact: on a complex screen with 200+ elements, find_element(By.XPATH, ...) can take 2–5 seconds. AppiumBy.ACCESSIBILITY_ID typically takes under 100ms.
Finding multiple elements
All strategies work with find_elements (plural):
items = driver.find_elements(
AppiumBy.ACCESSIBILITY_ID, "productItem"
)
print(f"Found {len(items)} products")
titles = [item.text for item in items]Locator tuples in page objects
Python tuple syntax keeps locators at the top of the page object class:
from appium.webdriver.common.appiumby import AppiumBy
class LoginPage(BasePage):
# Locator constants as tuples
EMAIL_FIELD = (AppiumBy.ACCESSIBILITY_ID, "emailInput")
PASSWORD_FIELD = (AppiumBy.ACCESSIBILITY_ID, "passwordInput")
LOGIN_BUTTON = (AppiumBy.ACCESSIBILITY_ID, "loginButton")
ERROR_BANNER = (AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().resourceId("com.example.app:id/error_text")')
def login(self, email: str, password: str):
self.wait_for_clickable(self.EMAIL_FIELD).send_keys(email)
self.wait_for_clickable(self.PASSWORD_FIELD).send_keys(password)
self.wait_for_clickable(self.LOGIN_BUTTON).click()
from pages.home_page import HomePage
return HomePage(self.driver)
def get_error_message(self) -> str:
return self.wait_for_visible(self.ERROR_BANNER).textwait_for_clickable(self.EMAIL_FIELD) passes the tuple as *locator to find_element(*locator). This is the cleanest Python pattern for Selenium-family locators.
Checking element presence without exception
def is_present(driver, locator_type, locator_value, timeout=2):
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
try:
WebDriverWait(driver, timeout).until(
EC.presence_of_element_located((locator_type, locator_value))
)
return True
except TimeoutException:
return False
# Usage
if is_present(driver, AppiumBy.ACCESSIBILITY_ID, "welcomeOverlay"):
driver.find_element(AppiumBy.ACCESSIBILITY_ID, "dismissWelcome").click()