Custom Waiters in Appium: Ensuring Screen Load Before Interaction
Serhat Ozdursun

Serhat Ozdursun @serhat_ozdursun_03644ef56

About: 🚀 SDET | Test Automation Engineer | Passionate about Appium, Selenium, WebDriverIO & CI/CD. Sharing insights on test automation & quality. More at serhatozdursun.com

Location:
Turkey
Joined:
Mar 5, 2025

Custom Waiters in Appium: Ensuring Screen Load Before Interaction

Publish Date: Mar 5
0 2

Why Do We Need Custom Waiters?

In mobile automation with Appium, there are scenarios where standard explicit waits (e.g., elementToBeVisible) are not sufficient. Some elements may be present in the DOM but not fully loaded, especially when dealing with dynamic content updates. A common case is waiting for a specific label or text to appear before proceeding with interactions.

For instance, in a mobile application, a loading screen might disappear, but the main screen elements may still be rendering. If we try to interact with elements too soon, we may encounter NoSuchElementException or StaleElementReferenceException.

A custom waiter can help ensure that the screen is fully loaded before continuing test execution.

Example Scenario
Let's say we have a mobile screen where a title dynamically loads. Instead of just waiting for the element to be present, we should wait until the label is actually populated.

Java Example

public boolean isScreenFullyLoaded() {
    return wait.until(mobileDriver -> {
        String label = mobileDriver.findElementByName(screenTitle).getAttribute("label");
        return !label.isEmpty();
    });
}
Enter fullscreen mode Exit fullscreen mode

How This Helps
Ensures that the screen title has loaded before proceeding.
Avoids false positives where the element exists but is not ready.
Prevents flaky tests by ensuring interactions occur only when the screen is fully rendered.
TypeScript Example (WebdriverIO + Appium)

async function isScreenFullyLoaded(): Promise<boolean> {
    return await browser.waitUntil(async () => {
        const label = await $(`~${screenTitle}`).getAttribute("label");
        return label !== "";
    }, {
        timeout: 10000,
        timeoutMsg: "Screen did not fully load within the expected time"
    });
}
Enter fullscreen mode Exit fullscreen mode

How This Helps
Uses WebdriverIO's waitUntil function to check for the label.
Ensures that the text is present before interacting with the screen.
Provides a timeout message to improve debugging.
Python Example (Appium-Python-Client)

from selenium.webdriver.support.ui import WebDriverWait

def is_screen_fully_loaded(driver):
    return WebDriverWait(driver, 10).until(lambda mobile_driver:  
  mobile_driver.find_element_by_accessibility_id(screen_title).get_attribute("label") != "")
Enter fullscreen mode Exit fullscreen mode

How This Helps

Uses WebDriverWait with a lambda function to check if the label is populated.
Prevents interacting with elements before they are fully rendered.
Reduces flakiness in mobile automation.

Conclusion
Using a custom waiter like this improves test stability by ensuring that elements are not just present but fully loaded and ready for interaction. This approach is particularly useful when dealing with dynamically loaded content in mobile applications.

Comments 2 total

  • Piotr
    PiotrMar 14, 2025

    Why not something like that

    public WebElement waitForElementVisible(WebElement element, int timeoutInSeconds) {
            await().atMost(Duration.ofSeconds(timeoutInSeconds))
                    .pollInterval(Duration.ofMillis(100))
                    .pollDelay(Duration.ZERO)
                    .until(() -> element != null && element.isDisplayed());
            return element;
        }
    
    Enter fullscreen mode Exit fullscreen mode
    • Serhat Ozdursun
      Serhat OzdursunApr 17, 2025

      @piotr_b5f3fb16a99232443b1

      Thanks for the input! Your suggestion using Awaitility is definitely a clean and robust solution on the Java side—especially for web or native contexts where the element reference is reliable and the display state accurately reflects readiness.

      In my case, though, the challenge was slightly different. I was working in a JavaScript/Appium environment for mobile testing, and I observed that isDisplayed() or similar visibility checks sometimes returned true before the screen data was fully rendered—especially with asynchronous data loading or transitions.

      That’s why I implemented a custom waiter like this:

      async function isScreenFullyLoaded(): Promise<boolean> {
          return await browser.waitUntil(async () => {
              const label = await $(`~${screenTitle}`).getAttribute("label");
              return label !== "";
          }, {
              timeout: 10000,
              timeoutMsg: "Screen did not fully load within the expected time"
          });
      }
      
      Enter fullscreen mode Exit fullscreen mode

      This approach ensures not just visual visibility, but that the actual screen content (like the title label) is present—an important distinction in mobile UI testing.

      That said, your example would be great for scenarios where display state is a strong indicator of readiness. And if you have thoughts on how to improve the Appium/JS version or make it more generic, I’d love to hear them!

Add comment