In Appium, what does the `id` locator strategy match on Android, and what on iOS?
answer
- same keyword, two different attributes
- one platform reads a build resource
- the other reads an accessibility attribute
- resource-id versus name
- no prefix on Apple platforms
basics
~20 sOn Android the id strategy matches an element's resource-id, the package-qualified identifier fixed in the build. On iOS it is an alias for the name attribute, which XCUITest fills from the accessibility identifier. Same keyword, two unrelated attributes.
solid answer
~40 s`id` is a locator strategy both native drivers accept, but each resolves it against a different attribute. On **Android**, the UiAutomator2 driver matches the element's `resource-id` — the identifier fixed in the app's build and reported in the page source in package-qualified form such as `com.example.app:id/login_button`; a bare name is prefixed with the package under test before matching. On **Apple platforms**, the XCUITest driver aliases `id` onto its `name` attribute, which it fills from the element's `accessibilityIdentifier`. So the same string is a build resource on one platform and an accessibility attribute on the other. They are written by different app-side code and are equal only when someone deliberately made them equal, so an `id` locator is platform-specific until you prove otherwise.
code
bash · 11 linesSESSION="$1"
# Android (UiAutomator2): 'id' compares against the resource-id attribute
curl -s -X POST "http://127.0.0.1:4723/session/$SESSION/element" \
-H 'Content-Type: application/json' \
-d '{"using": "id", "value": "com.example.app:id/login_button"}'
# Apple platforms (XCUITest): 'id' is an alias for the name attribute
curl -s -X POST "http://127.0.0.1:4723/session/$SESSION/element" \
-H 'Content-Type: application/json' \
-d '{"using": "id", "value": "loginButton"}'go deeper
Be ready to say plainly which attribute id matches on each platform: resource-id on Android, the name attribute on iOS. Knowing the two are different is the whole ask at this level.
Explain the mechanics: the package-qualified form on Android and the automatic prefixing that hides it, and the alias from id onto name on Apple platforms with its accessibility-identifier source.
Show how you diagnose an id locator that resolves on one platform and returns nothing on the other, using the live page source per device rather than reasoning from the app's source code.
Own the tradeoff of letting a shared locator constant imply that two unrelated app-side fields agree, and say what you would require of a codebase before allowing that assumption.
## What `id` is at the wire level A find in Appium is an HTTP request: `POST /session/:sessionId/element`, carrying a body of `{"using": "id", "value": "..."}`. `id` is one of the strategy names both native drivers declare — it is on the UiAutomator2 driver's list for Android and on the XCUITest driver's list for iOS, iPadOS and tvOS. **Appium's core does not define what `id` means.** The driver that owns the session decides which attribute of the on-screen element the string is compared against, and the two mobile drivers picked attributes that have nothing to do with each other. That shared strategy name is exactly what makes the trap convincing. The request is well formed on both platforms, the server answers on both, and one page object compiles and runs everywhere. Nothing in the protocol tells you that the two runs consulted different data. ## Android: `id` reads the `resource-id` attribute On Android the UiAutomator2 driver matches the element's **`resource-id`** attribute — the same value the page source and the inspector display for that node. - The full form is package-qualified, for example `com.example.app:id/login_button`. - Given a bare `login_button`, the driver prepends the package under test before matching, which is why most suites get away with writing the short name. - The UiAutomator2 setting `disableIdLocatorAutocompletion` turns that prefixing off, which is what you need when the identifier was authored without a package. - The value is fixed when the app is built, so it is **not localised** — the same string in every language and every locale. - It carries **no uniqueness guarantee**. A list can render the same `resource-id` on every row, so a single-element find returns the first match and tells you nothing about the other twenty. - It is not part of the accessibility tree. A control can be perfectly addressable by `id` and still be unlabelled for a screen reader. ## Apple platforms: `id` is an alias for `name` The XCUITest driver resolves `id` against its **`name`** attribute, which it fills from the element's **`accessibilityIdentifier`**. Two consequences follow, and both surprise people arriving from Android: 1. `id`, `name` and `accessibility id` are **three strategy names for one attribute** here. Swapping between them changes nothing about which element comes back. 2. The value lives in the **accessibility layer**, so it exists only where app code put it there. A button with no identifier has no `id` to match, however obvious its caption looks on screen. There is one further wrinkle worth carrying: when the identifier is empty, XCUITest falls back to the element's accessibility label when it computes `name`, so an `id` find on Apple platforms can quietly match a **visible, translated** string and pass in English while failing in German. Nothing equivalent happens on Android, where `resource-id` is never a caption. ## The two side by side | | Android (UiAutomator2) | Apple platforms (XCUITest) | |---|---|---| | attribute matched | `resource-id` | `name` | | source of the value | the app's build resources | `accessibilityIdentifier` | | written form | `com.example.app:id/login_button` | a bare string, no prefix | | automatic prefixing | yes, switched off by `disableIdLocatorAutocompletion` | none | | relation to `accessibility id` | a different matcher on a different attribute | the same matcher — both alias `name` | | can match a translated caption | no | yes, when the identifier is unset | ## Writing the locator honestly Because the meaning is per-platform, the locator has to be per-platform too, and the discipline is small: 1. **Name the platform wherever the locator is defined.** A constant called `LOGIN_ID` used by both runs is a claim that the two attributes agree; make that claim explicit or split the constant. 2. **Read the value out of the live source, not out of the app's code.** `GET /session/:sessionId/source` shows exactly what the driver will compare against on that device. 3. **Expect the Android value to be long and the Apple value to be short.** If your iOS locator contains a colon and a slash, someone pasted an Android identifier into it. 4. **Do not assume one find equals one element** on either platform; check how many matches a multi-element find returns before trusting the first. ## The failure modes to expect - An `id` that resolves on Android and returns nothing on iOS, because app code set the resource identifier and never set `accessibilityIdentifier`. - An `id` that resolves on iOS and returns nothing on Android, because the identifier exists only in the accessibility layer. - An `id` find on iOS that passes in one language and fails in another, because it landed on the label fallback rather than a real identifier. - An `id` find on Android that returns the wrong row, because every row in the list shares the identifier and the driver handed back the first. The leaf-level thesis is short: `id` is portable as a **keyword** and not as a **meaning**. Treat it as two strategies that happen to share a spelling, and the surprises stop.
- On Android, why can an `id` locator match more than one element?Because `resource-id` is not required to be unique on screen. A list, a tab bar or a repeated row template can render the same identifier many times, and a single-element find simply returns the first match in tree order. Ask for all matches when you need to know, and scope the search to a container element rather than trusting position.
- On iOS, what happens to an `id` lookup when the control has no accessibility identifier?XCUITest computes `name` from the identifier, and when that is empty it falls back to the element's accessibility label. So the find either misses entirely or silently matches the visible caption. The first case is a clean failure; the second is worse, because the test passes until the app is translated or the copy is reworded.
saying these in an interview costs you the question
- Claims id means the same attribute on Android and iOS
- Thinks an Android resource-id also exists on an iOS build
- Assumes an id match is always unique on screen
- Believes iOS id and accessibility id are different matchers
- Treats a shared id constant as automatically portable
- Expects the package prefix to be required on Apple platforms