In Appium, why does a `class name` locator that works on Android hit the wrong control on iOS?
answer
- the filter always had one match by luck
- role vocabulary is coarser than class names
- first match wins, silently
- Other swallows unclassified views
- count matches before trusting one
basics
~20 sBecause the two vocabularies have different resolution. An Android class string such as android.widget.EditText is often narrow enough to hit one control, while the iOS equivalent XCUIElementTypeTextField is a coarse role shared by many, so the find returns the first of several.
solid answer
~40 sThe strategy is doing the same thing on both platforms — an exact match on a type string — but the strings come from vocabularies with very different resolution. On **Android** the value is a fully qualified widget class from an open set, so a screen often contains exactly one `android.widget.EditText` and the locator appears to identify a control. On **Apple platforms** the value is an `XCUIElementType`, a closed vocabulary of roles, so the same screen may hold five `XCUIElementTypeTextField` elements and every custom container collapses to `XCUIElementTypeOther`. The Android locator was never an identity; it was a filter that happened to have one match. Ported unchanged, the same filter has several matches and the driver hands back the first, which is a silent wrong-element bug rather than a failure.
go deeper
Be ready to say that class name selects a kind of control and that several elements can match, so the driver returning one element does not mean only one existed.
Explain why the Apple type vocabulary is coarser than Android's class vocabulary, and what XCUIElementTypeOther means for a locator that lands on it.
Demonstrate the diagnosis: count matches on the failing device, read the live source, and argue why this is a locator defect rather than something a retry should absorb.
Own the standard your suite holds locators to, and be able to justify requiring identity-based addressing on the platform where role vocabularies cannot disambiguate.
## The symptom A suite addresses a control by `class name`. On Android it is green and has been for months. The same locator on Apple platforms taps something else — the wrong text field, the wrong row, a wrapper view — and the assertion that follows fails somewhere far from the cause. Nothing errored: the find succeeded, it just succeeded on the wrong element. ## The mechanism is identical; the resolution is not Both native drivers implement `class name` as an exact comparison against a single type attribute — `class` on Android, `type` on Apple platforms — and both return the first match in tree order for a single-element find. That part does not diverge. What diverges is how much a type string narrows the tree. - Android values are **fully qualified Java class names** from an **open** set: `android.widget.EditText`, `android.widget.ImageButton`, `androidx.recyclerview.widget.RecyclerView`. Libraries add their own, so the vocabulary of a screen is unusually specific. - Apple values are **`XCUIElementType`** members from a **closed** set: `XCUIElementTypeTextField`, `XCUIElementTypeButton`, `XCUIElementTypeStaticText`, `XCUIElementTypeCell`, `XCUIElementTypeOther`. The set describes roles, not implementations. - A role vocabulary is coarse by design. Two controls implemented completely differently share one type when they do the same job. - Anything the accessibility layer cannot classify becomes `XCUIElementTypeOther`, which is why that value is usually the most common type in the tree and never a useful locator. So the Android locator was **never an identity**. It was a filter with one match on that screen, in that build, in that locale. That is a property of the screen, not of the strategy, and it does not survive the port. ## Why the failure is silent rather than loud A single-element find takes the first match. When the filter has one match, first and only are the same thing and the locator looks correct. When the filter has five, the driver still answers in milliseconds with a perfectly valid element handle. The test then types into the wrong field or taps the wrong row, and the first visible sign is an assertion failure downstream — or worse, an assertion that still passes because the screen looked right. | | Android (UiAutomator2) | Apple platforms (XCUITest) | |---|---|---| | attribute compared | `class` | `type` | | vocabulary | open, implementation-shaped | closed, role-shaped | | typical matches per screen | often one, by accident | commonly several | | unclassified views | keep a class of their own | collapse to `XCUIElementTypeOther` | | failure mode when ported | — | first-of-many, silently wrong | ## Diagnosing it 1. **Count the matches, do not assume them.** Run the same locator as a multi-element find on the Apple device and look at how many handles come back. One is a coincidence you are relying on; more than one is your bug. 2. **Pull the live tree** with `GET /session/:sessionId/source` for the failing screen and read the `type` values. If your target is `XCUIElementTypeOther`, the locator can never be trusted no matter how you tune it. 3. **Compare like with like.** Put the Android `class` values and the Apple `type` values for the same screen next to each other; the collapse from many distinct classes to a handful of types is usually visible at a glance. 4. **Check whether the Android side is equally fragile.** A screen that gains a second `android.widget.EditText` in a redesign fails the same way — the platform difference is how soon it happens, not whether it can. ## Fixing it without moving the problem - Address the control by an **identity attribute** rather than by its kind, and accept that the identity attribute differs per platform. - When only a type is available, **scope the search**: find a container element first and search within it, so the filter runs over a handful of nodes instead of the whole tree. - Treat a first-of-many match as a **defect to fix, not a flake to retry**. Retrying a locator that is ambiguous returns the same wrong element every time, so a retry policy will not surface it. - Write the platform into the locator's name so nobody ports it again by copying a constant. ## What an interviewer is listening for The weak answer is "iOS locators are flakier". The strong answer separates the strategy from the tree: `class name` behaves identically on both platforms, and the difference lives in the vocabularies — one shaped by implementation classes and one by accessibility roles. Anyone who can say that will also spot the corollary, which is that the Android locator was already ambiguous in principle and had simply never been tested on a screen where it mattered.
- Why will a retry policy not surface this bug?Because it is deterministic. An ambiguous locator resolves to the same first match on every attempt, so retrying reproduces the wrong element rather than a different one. Retries hide timing problems; this is a specification problem in the locator, and the only thing that surfaces it is counting matches or asserting on something that identifies the intended control.
- Is the Android locator safe if it happens to have exactly one match today?No, only lucky. It expresses a kind, not an identity, so any redesign that adds a second control of that kind changes which element comes first. Treat a single match as a fact about this build of this screen, not as a property of the locator.
saying these in an interview costs you the question
- Calls it flakiness rather than an ambiguous locator
- Adds a retry instead of counting the matches
- Assumes iOS types are as specific as Android classes
- Trusts a locator matching XCUIElementTypeOther
- Believes the Android side cannot break the same way