Finding Elements with AppiumBy in Python

7 min read

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 AppiumBy

Accessibility 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).text

wait_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()

// tip to track lessons you complete and pick up where you left off across devices.