Appium's `-ios class chain` is iOS-only — how do you keep one locator layer?
answer
- no shared string is possible
- one accessor, two resolved locators
- keep the branch at one seam
- portable strategy first, path as escape hatch
basics
~20 sOnly Appium's XCUITest driver accepts a class chain, so no path can be shared. Keep one method per element, resolve its locator per platform behind that method, and let the divergence live at a single named seam.
solid answer
~50 sStart from the measured fact: `-ios class chain` is declared by the **XCUITest driver** and by nothing on the Android side — UiAutomator2 and Espresso publish their own strategy lists, and neither takes a path expression of this shape. There is no clever way to write one string that works on both, so the only real question is *where* the two dialects meet. The workable answer is a per-element accessor — `ringButtonFor(ringer)` on a bell-ringing tower roster screen — whose locator is chosen once per session from `platformName`, with the class chain on the Apple branch and the Android driver's own strategy on the other. What that buys is tests that never branch, a reviewer who can see both dialects side by side, and the iOS-only reach into a container by its content still available where it earns its keep.
go deeper
Know that a class chain only works on Apple-platform sessions, and that if you find one in a shared helper it must sit behind something that picks a locator per platform rather than be used directly.
Explain why no helper can translate a class chain to Android — it is a path over Apple element types — and describe how a per-element accessor keeps the branch out of the test bodies.
Argue where the seam goes and what it costs to hold: two dialects, one-sided breakages, and screen files owned by someone who notices when the iOS path count keeps climbing.
Own the direction of travel: the class chain earns its place on screens the app does not identify, and a rising count is a signal to invest in the app's addressability rather than in more paths.
## The fact that forces the design Appium's **XCUITest driver** declares `-ios class chain` among its native strategies for Apple platforms. The Android drivers declare their own, shorter lists, and nothing of this shape appears on them. That is not a gap waiting to be filled by a helper: a class chain is a path over `XCUIElementType` names, and an Android hierarchy has no such type vocabulary to walk. There is therefore no shared string, and any design that pretends otherwise is hiding a branch rather than removing one. So the decision is not *how do we share the locator*. It is *where does the divergence live, and who is accountable for each half*. ## Three shapes teams actually use 1. **Per-element accessor with a platform-resolved locator.** One method — `ringButtonFor(ringer)` — and a table mapping it to a class chain on the Apple branch and an Android strategy on the other, resolved once from `platformName` at construction. Tests never see either dialect. 2. **Branch inside the test.** A platform conditional in each case. It works, and it is the shape that ages worst: the branch multiplies with every case, and nobody can answer *what does this suite address on iOS* without reading all of it. 3. **Lower both sides onto one portable strategy.** Push identifiers into the app so a single strategy addresses the element on both platforms, and keep the class chain as an escape hatch for screens the app does not identify. Whether the app should carry those identifiers is a separate subject with its own owner; the point here is that this removes the class chain from the shared path rather than making it portable. Shape 1 is the default worth defending. Shape 3 is the direction to push over time. Shape 2 is what a suite decays into when nobody owns the question. ## What belongs on each side of the seam | lives above the seam | lives below it | |---|---| | the element's name and meaning | the class chain path, on the Apple branch | | what the case asserts | the Android driver's own strategy | | waiting, retries, assertions | which `XCUIElementType` names the iOS hierarchy uses | The test above the seam should read the same to a reviewer who has never opened an iOS build. Everything that mentions an element type, a step separator or an index belongs below it, in one file per screen, so that a change to the Apple hierarchy touches one place. ## What the iOS branch actually buys - **Containment addressing.** A class chain can match a container by a descendant and then step down to the control inside it — the case where the roster row carries nothing and the ringer's name lives on a text within it. - **Hierarchy without a general tree language.** A short path says *this button, in this row* rather than *some button somewhere*. - **One find instead of three.** The row and the control come out of a single path, so there is no intermediate element to hold, re-find or let go stale. Those are real, and they are why the iOS branch will not simply collapse into the Android one. A suite that bans the strategy outright pays for it on exactly the screens where the app gives no handles. ## What it costs, and how to keep the cost visible - Two dialects means two things to teach, two review vocabularies, and a fix that lands on one platform and not the other. - A class chain encodes hierarchy knowledge, so an iOS refactor that changes nothing visible can still break a path — and only the Apple lane goes red, which reads like flake to anyone not watching. - Failures do not look alike across the seam. A path that stops matching after a wrapper view is added surfaces as a missing element, not as a message about the wrapper. - Keep the count honest: if the number of class chains keeps climbing, that is a signal about how addressable the app is, not a verdict on the strategy. The governance answer is unglamorous — one owner per screen file, class chains reviewed as code rather than pasted out of an inspector, and a standing preference for the portable strategy wherever the app cooperates. ## Where this stops being a locator question Which devices and platform versions a suite covers, whether the app should ship stable identifiers as a contract, and how a mixed set of selectors gets migrated are all owned elsewhere and should be bridged to, not re-argued inside the locator layer. The question this leaf answers is narrower and worth answering precisely: the class chain is an Apple-platform path language with no Android twin, so it is a branch by construction, and the design work is choosing the one place that branch is allowed to exist.
- How would you review a change that adds ten new iOS class chains to the roster suite?Ask what each one is compensating for. A handful on screens the app cannot identify is fine; ten at once usually means an addressability problem being solved in the test layer. Then check the mechanics: content-addressed rather than positional, short paths, and no long direct-child chains mirroring today's layout.
- Does keeping the class chain behind one accessor make the suite genuinely cross-platform?It makes the tests cross-platform, not the coverage. Both branches still have to be exercised on both platforms, because a locator that is only resolved on the Apple lane is only ever proven there. The seam contains the divergence; it does not remove the need to run both.
saying these in an interview costs you the question
- Claims a class chain can be made to work on Android with a helper
- Puts a platform conditional inside every test case
- Treats an iOS-only path as evidence the suite is cross-platform
- Copies class chains out of an inspector without reviewing them
- Bans the strategy outright and leaves unaddressable screens untested