DEV Community

Cover image for XCUIElement: Finding and Interacting with UI Elements
QAPulse by SK
QAPulse by SK

Posted on Originally published at skakarh.com

XCUIElement: Finding and Interacting with UI Elements

XCUIElement is the core XCUITest object for locating and interacting with UI elements in an iOS application. For SDETs, understanding XCUIElement means moving beyond simple tap() calls toward reliable element queries, accessibility identifiers, synchronization, state validation, and maintainable page-object architecture.

What is XCUIElement?

XCUIElement represents a UI element in the application hierarchy exposed through XCUITest. It provides APIs for querying, inspecting, and interacting with elements such as buttons, text fields, labels, images, switches, cells, and other controls.

A basic interaction looks like this:

let app = XCUIApplication()

app.launch()

let loginButton = app.buttons["loginButton"]

loginButton.tap()
Enter fullscreen mode Exit fullscreen mode

The architecture is:

XCTestCase
    ↓
XCUIApplication
    ↓
XCUIElementQuery
    ↓
XCUIElement
    ↓
Interaction / Assertion
Enter fullscreen mode Exit fullscreen mode

XCUIApplication controls the application.

XCUIElement represents the specific UI object that the test needs to inspect or manipulate.

Key Points

  • XCUIElement represents a UI element.
  • Elements are normally obtained through XCUIElementQuery.
  • Accessibility identifiers provide stable selectors.
  • exists checks whether an element currently exists.
  • isHittable checks whether an element can currently receive interaction.
  • tap() performs a tap interaction.
  • typeText() enters text into supported elements.
  • clearText() removes existing text from supported text fields.
  • waitForExistence(timeout:) provides condition-based synchronization.
  • XCUIElement supports assertions against UI state.
  • Reliable selectors are more important than simply finding an element.

XCUIElement vs XCUIElementQuery

These concepts are closely related but have different responsibilities.

XCUIElementQuery

A query identifies elements:

let loginButton = app.buttons["loginButton"]
Enter fullscreen mode Exit fullscreen mode

XCUIElement

The resulting object represents the element that the test can inspect or interact with:

loginButton.tap()
Enter fullscreen mode Exit fullscreen mode

Conceptually:

XCUIElementQuery
       ↓
Find
       ↓
XCUIElement
       ↓
Inspect / Interact
Enter fullscreen mode Exit fullscreen mode

This distinction becomes important when designing scalable XCUITest frameworks.

Finding UI Elements

XCUITest provides several query strategies.

Buttons

let loginButton = app.buttons["loginButton"]
Enter fullscreen mode Exit fullscreen mode

Text Fields

let emailField = app.textFields["emailField"]
Enter fullscreen mode Exit fullscreen mode

Secure Text Fields

let passwordField = app.secureTextFields["passwordField"]
Enter fullscreen mode Exit fullscreen mode

Static Text

let welcomeMessage = app.staticTexts["Welcome"]
Enter fullscreen mode Exit fullscreen mode

Images

let logo = app.images["appLogo"]
Enter fullscreen mode Exit fullscreen mode

Switches

let notificationsSwitch = app.switches["notificationsSwitch"]
Enter fullscreen mode Exit fullscreen mode

Cells

let firstCell = app.cells.element(boundBy: 0)
Enter fullscreen mode Exit fullscreen mode

The selector should describe the UI element’s automation identity rather than relying on its visual position.

Accessibility Identifiers

For production-grade automation, accessibility identifiers are usually the preferred way to create stable selectors.

In the application:

loginButton.accessibilityIdentifier = "loginButton"
Enter fullscreen mode Exit fullscreen mode

In XCUITest:

let loginButton = app.buttons["loginButton"]

loginButton.tap()
Enter fullscreen mode Exit fullscreen mode

This is significantly more maintainable than depending on visible text:

app.buttons["Log In"].tap()
Enter fullscreen mode Exit fullscreen mode

Why?

The visible text might change because of:

  • Localization
  • Product wording changes
  • A/B testing
  • Branding changes
  • UX redesign

The automation identifier can remain stable.

Recommended Selector Hierarchy

For maintainable XCUITest automation, prefer selectors roughly in this order:

Accessibility Identifier
        ↓
Accessibility Label / Exact Text
        ↓
Predicate Query
        ↓
Hierarchy-Based Query
        ↓
Index-Based Query
Enter fullscreen mode Exit fullscreen mode

The further down the list you go, the more fragile the selector can become.

For example:

app.buttons["loginButton"]
Enter fullscreen mode Exit fullscreen mode

is generally preferable to:

app.buttons.element(boundBy: 3)
Enter fullscreen mode Exit fullscreen mode

because the third button can change when the UI changes.

Interacting With XCUIElement

Once an element has been located, XCUITest provides interaction APIs.

Tap

let loginButton = app.buttons["loginButton"]

loginButton.tap()
Enter fullscreen mode Exit fullscreen mode

Double Tap

loginButton.doubleTap()
Enter fullscreen mode Exit fullscreen mode

Long Press

loginButton.press(forDuration: 2)
Enter fullscreen mode Exit fullscreen mode

Swipe

app.swipeUp()
Enter fullscreen mode Exit fullscreen mode

For element-specific gestures:

loginButton.swipeLeft()
Enter fullscreen mode Exit fullscreen mode

The interaction should normally occur only after the test has established that the element is available.

Waiting for an Element

One of the most important synchronization APIs is:

waitForExistence(timeout:)
Enter fullscreen mode Exit fullscreen mode

Example:

let loginButton = app.buttons["loginButton"]

XCTAssertTrue(
    loginButton.waitForExistence(timeout: 10)
)

loginButton.tap()
Enter fullscreen mode Exit fullscreen mode

This is better than:

sleep(5)

loginButton.tap()
Enter fullscreen mode Exit fullscreen mode

The difference is fundamental.

sleep()
    ↓
Wait fixed duration

waitForExistence()
    ↓
Wait until condition becomes true
Enter fullscreen mode Exit fullscreen mode

Condition-based synchronization generally produces more resilient automation.

Checking Element Existence

You can inspect:

if loginButton.exists {
    loginButton.tap()
}
Enter fullscreen mode Exit fullscreen mode

For assertions:

XCTAssertTrue(
    loginButton.exists
)
Enter fullscreen mode Exit fullscreen mode

However, exists is an immediate state check.

For asynchronous UI loading, prefer:

XCTAssertTrue(
    loginButton.waitForExistence(timeout: 10)
)
Enter fullscreen mode Exit fullscreen mode

Checking Whether an Element Is Hittable

An element can exist without being interactable.

Use:

XCTAssertTrue(
    loginButton.isHittable
)
Enter fullscreen mode Exit fullscreen mode

👉 Continue reading the full article on skakarh.com →

Originally published at skakarh.com/xcuielement-finding-ios-ui-elements.
Subscribe to QA Pulse by SK
weekly signal for QA, Test Automation and AI in Software Engineering.

Top comments (0)