skip to content

In Appium on iOS, which attribute must a row report before XCUITest will tap it, and how do you get there?

level: seniorimportance: must knowfreq 58%

answer

  1. listed is not the same as reachable
  2. distance is only one blocker
  3. hit test at the activation point
  4. hittable, not visible

basics

~20 s

Hittable. XCUITest acts on an iOS element only when a hit test at its activation point resolves to that element. mobile: scrollToElement scrolls its container until it is hittable; an overlay such as a sticky bar must be cleared instead.

solid answer

~40 s

On iOS, presence, visibility and reachability are three different states. WebDriverAgent's snapshot lists elements that are laid out but off screen, so a find can succeed for a row nobody can see; `visible` says the element intersects the screen, and `hittable` says a hit test at its activation point actually resolves to it. XCUITest interacts on the strength of `hittable`, not presence. `mobile: scrollToElement` is the command that closes the distance: hand it the element and it scrolls the enclosing scroll view until the element is hittable, erroring when it cannot. What it cannot do is move something else out of the way - a sticky **Book appointment** bar, an open keyboard or a banner over the row leaves it visible and still not hittable, and that one you fix by clearing the overlay.

code

python · 4 lines
python
row = driver.find_element(AppiumBy.IOS_PREDICATE, 'name == "Bella - Full Groom"')
if row.get_attribute('hittable') != 'true':
    driver.execute_script('mobile: scrollToElement', {'elementId': row.id})
row.click()

go deeper

for a junior

Remember that on iOS finding a row is not the same as being able to tap it, and that mobile: scrollToElement is what brings a located row into reach before you act on it.

for a middle

Explain the difference between an element being on screen and a hit test at its activation point resolving to it, and say which of the two XCUITest requires before it will interact.

for a senior

Show the diagnosis: check reachability rather than retrying, separate a distance problem from an overlay problem, and report the two as different defects instead of one flaky tap.

for a principal

Own the convention. Decide whether every interaction in the suite goes through a reachability-checked helper, and defend the cost of that discipline against the ambiguous failures it removes.

## Three states, not two On iOS an element can be in any of three quite different conditions, and conflating them is the most common source of "but the page source shows it" arguments: - **Present**: WebDriverAgent's snapshot contains the element, so a find returns a handle. This is true for plenty of elements laid out below the fold. - **Visible**: the element intersects the screen, reported by the `visible` attribute. - **Hittable**: a hit test at the element's activation point resolves to *that* element, reported by `hittable`. XCUITest drives real touches, so it acts on the third condition. Presence buys you an element handle and nothing more. In a pet-grooming appointment app, the row `Bella - Full Groom` can be found, addressed, have its `label` read - and still refuse a tap. ## Why visible is not enough Hittability is stricter than visibility for a specific reason: it asks who receives the touch. An element fully on screen is not hittable when something is drawn over the point the tap would land on. Everyday causes in a real app: 1. A sticky bottom action bar - a **Book appointment** button pinned below the schedule - covering the last row. 2. An open keyboard occupying the lower third of the screen after a search field was focused. 3. A modal, toast or promotional banner that appeared between the find and the tap. 4. A transparent overlay that intercepts touches while looking like nothing at all. In every one of these the element reports `visible` true and `hittable` false, and a test that only checked visibility fails on the tap with no obvious cause. ## What mobile: scrollToElement promises `mobile: scrollToElement` takes the element you already located and scrolls its enclosing scroll view until that element is hittable, erroring if it cannot get there. Its contract is reachability, which makes it exactly right for the first problem - distance - and useless for the rest: - **Fixes**: a row below or above the visible band inside a scrollable container. - **Does not fix**: an overlay covering the row's activation point. - **Does not fix**: an element whose container does not scroll, where there is no distance to close. - **Does not fix**: an element that has left the hierarchy between the find and the scroll. ## Reading it before you act The attribute is readable through the ordinary attribute request on an element, so a step can decide rather than guess: ```python row = driver.find_element(AppiumBy.IOS_PREDICATE, 'name == "Bella - Full Groom"') if row.get_attribute('hittable') != 'true': driver.execute_script('mobile: scrollToElement', {'elementId': row.id}) ``` That ordering matters. Scrolling unconditionally is wasteful and can move a list that was already positioned correctly; tapping unconditionally produces a failure whose message is about the tap rather than about reachability. Checking first turns an ambiguous flake into a specific, reportable state. ## The ladder to follow when it is still not hittable When the scroll has run and the element is still not hittable, the blocker is not distance. Work down this ladder rather than retrying the tap: 1. Re-read the attribute after the scroll, confirming the state rather than assuming the command failed silently. 2. Look for an overlay: dismiss the keyboard, close the banner, or wait out the modal that appeared. 3. If a pinned bar covers the row, scroll further so the row leaves the covered strip rather than stopping the moment it is on screen. 4. Only then consider whether the element sits in a container that scrolls at all. ## The Android contrast, briefly Android's attribute vocabulary has no hittability twin; `displayed` answers a different, weaker question about being on screen. So the same overlay defect shows up differently there: the touch is dispatched and lands on whatever is on top, and the test fails later, somewhere else, with a confusing symptom. That asymmetry is worth saying in an interview, because it explains why an iOS suite tends to fail loudly at the point of the problem while an Android suite fails quietly a step or two downstream. ## What to take away On iOS, a scroll-into-view step is not finished when the element is on screen. It is finished when the element is hittable. That is the state `mobile: scrollToElement` scrolls towards, and it is the state to assert before acting - because the snapshot listing a row is no promise at all that you can tap it.

  • An iOS row reports visible true and hittable false after `mobile: scrollToElement` has run. What is your next move?
    Stop scrolling and look for an overlay, because distance is no longer the blocker. Dismiss the keyboard, close the banner or modal, or scroll further so the row leaves the strip a pinned action bar covers. Retrying the tap only turns a diagnosable state into a flaky failure.
  • Why is checking `hittable` before the tap better than letting the tap fail and retrying?
    The check reports the actual state - reachable or blocked - at the moment it matters, so the failure names the cause. A failed tap reports only that an interaction did not work, and a retry loop hides the difference between a row that needed scrolling and a row permanently under an overlay.

saying these in an interview costs you the question

  • Treats presence in the snapshot as tappability
  • Thinks visible true is enough to allow a tap
  • Assumes scrolling can clear an overlay
  • Applies Android's displayed reasoning to iOS
  • Retries the tap instead of reading hittable