with one click
appium-cross-platform-strategy
// Design cross-platform Page Object Models for Appium, sharing test logic across iOS and Android via inheritance, platform branching, and the unified WebDriver protocol.
// Design cross-platform Page Object Models for Appium, sharing test logic across iOS and Android via inheritance, platform branching, and the unified WebDriver protocol.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | appium-cross-platform-strategy |
| description | Design cross-platform Page Object Models for Appium, sharing test logic across iOS and Android via inheritance, platform branching, and the unified WebDriver protocol. |
| tech_stack | ["mobile-native"] |
| capability | ["e2e-testing"] |
| version | Appium 2.x |
| collected_at | "2025-01-01T00:00:00.000Z" |
Source: https://appium.io/docs/en/2.0/intro/drivers/, https://appium.io/docs/en/2.0/intro/, https://deepwiki.com/appium/java-client/3-element-location-and-page-objects
Appium's adoption of the W3C WebDriver Protocol as a single, unified API across all platforms is the foundation for cross-platform test architecture. Whether automating iOS (XCUITest driver) or Android (UiAutomator2 driver), the same protocol primitives apply — only the locators and driver session differ. This enables a single Page Object Model codebase to drive both platforms.
Define an abstract base page object with shared behavior and abstract locator methods. Create platform-specific subclasses that provide the actual locators via annotations.
// Abstract base — shared behavior, no locators
public abstract class LoginPage {
protected AppiumDriver driver;
public void login(String username, String password) {
getUsernameField().sendKeys(username);
getPasswordField().sendKeys(password);
getLoginButton().click();
}
protected abstract WebElement getUsernameField();
protected abstract WebElement getPasswordField();
protected abstract WebElement getLoginButton();
}
// iOS — annotation-driven locators
public class IOSLoginPage extends LoginPage {
@iOSXCUITFindBy(accessibility = "usernameField")
private WebElement usernameField;
// ... override getters to return annotated fields
}
// Android
public class AndroidLoginPage extends LoginPage {
@AndroidFindBy(id = "com.example:id/username")
private WebElement usernameField;
// ... override getters
}
# Python: select page object based on capabilities
def get_page(driver, page_name):
platform = driver.capabilities.get('platformName', '').lower()
registry = {
'ios': {'login': IOSLoginPage, 'home': IOSHomePage},
'android': {'login': AndroidLoginPage, 'home': AndroidHomePage},
}
return registry[platform][page_name](driver)
login_page = get_page(driver, 'login')
login_page.login("user", "pass")
// iOS
XCUITestOptions iosOpts = new XCUITestOptions()
.setDeviceName("iPhone 14").setPlatformVersion("16.0")
.setApp("/path/app.ipa").setAutomationName("XCUITest");
AppiumDriver iosDriver = new AppiumDriver(url, iosOpts);
// Android
UiAutomator2Options androidOpts = new UiAutomator2Options()
.setDeviceName("Pixel 6").setPlatformVersion("13.0")
.setApp("/path/app.apk").setAutomationName("UiAutomator2");
AppiumDriver androidDriver = new AppiumDriver(url, androidOpts);
The core POM engine. Annotated fields become lazy-loaded proxies:
AppiumElementLocatorFactory builds the strategyTimeout control:
@AndroidFindBy(id = "menu_button")
@iOSXCUITFindBy(accessibility = "menuButton")
@WithTimeout(time = 5, chronoUnit = ChronoUnit.SECONDS)
private WebElement menuButton;
Default AppiumFieldDecorator timeout is only 1 second — always set explicit timeouts for real mobile UIs.
// Switches locator based on native vs. webview context
@AndroidFindBy(id = "native_button")
@AndroidFindBy(className = "web-button", content = Content.WEB_VIEW)
private WebElement button;
iOS command path: test code → Appium client → HTTP → Appium server → XCUITest driver → WebDriverAgent (Objective-C, on-device) → Xcode → XCUITest → iOS → macOS
Android command path: test code → Appium client → HTTP → Appium server → UiAutomator2 driver → ADB + UiAutomator2 + helper app → Android
Many standard WebDriver commands (e.g., Click Element) are proxied directly to the underlying WebDriverAgent/SafariDriver with no Node.js driver code — useful to know when debugging.
AppiumFieldDecorator default is far too low. Always configure @WithTimeout.appium:automationName is required — this capability selects the driver ("XCUITest" or "UiAutomator2"); wrong value = no session.appium-locators for choosing the right locator strategy per platform (accessibility ID for shared, Predicate/UiAutomator for platform-specific)appium-waits-conditions to set appropriate explicit waits in page object methodsappium-parallel-grid — each platform gets its own driver session on its own deviceplatformName and returns the correct variant