An Appium `-ios predicate string` finds nothing though the ice-rink control is visible — how do you diagnose it?
answer
- an empty filter is not an error
- full class name, quoted literals
- halve the expression, then widen
- scope comes from the endpoint
basics
~20 sWork down the expression: full XCUIElementType name for type, quoted string literals, the attribute you actually meant, case and diacritics, and MATCHES matching the whole string. Then confirm the control is really in the session's source with those attributes.
solid answer
~50 sA predicate never reports a syntax opinion about your attribute choice — a clause nothing satisfies looks identical to a clause you spelled wrong — so triage in a fixed order on Apple platforms. Check `type` carries the full `XCUIElementType…` name, not `Button`. Check every string literal is quoted, since a bare word is parsed as another attribute. Check you named the attribute the control actually carries: an identifier lives in `name`, visible copy in `label`, a control's state in `value`. Relax the comparison with `[cd]` to rule out case and accents. If you used `MATCHES`, remember it matches the whole attribute. Then drop the predicate to a single clause — `type == 'XCUIElementTypeCell'` — and widen from there, and read the session's source to confirm the ice-rink control is present with the attributes you assumed.
go deeper
Learn the two beginner traps first: type needs the full XCUIElementType class name, and every string literal needs quotes or it is read as an attribute name.
Explain why the failure is silent — a filter that matches nothing is an empty result, not a syntax error — and show the bisection routine that isolates the offending clause.
Demonstrate an ordered triage that ends in evidence: reduce the predicate, check the scope of the find, then read the source before declaring the element missing.
Push the cost upstream. Decide what your suite must capture on a find failure so this triage is a log read rather than a rerun, and what identifier coverage removes the class of defect.
## Why a predicate fails silently An `-ios predicate string` in Appium's XCUITest driver is a filter, and a filter that matches nothing is not an error — it is an empty result. The driver reports the same no-such-element outcome whether you asked for an attribute that does not exist on that control, a literal that differs by one accent, or a control that genuinely is not on screen. Nothing in the failure tells you which. That is why triage here is a fixed sequence rather than a guess, and why halving the predicate is worth more than re-reading it. ## Read the expression back, clause by clause Four mistakes account for most of these on Apple platforms: - **A short type name.** `type` holds the full class name, so `type == 'XCUIElementTypeButton'` matches and `type == 'Button'` never does. This is the single most common one. - **An unquoted literal.** `name == rink.session.book` parses the right-hand side as another attribute name, not as a string. Quote every literal, with single or double quotes. - **A misspelled attribute.** A typo in an attribute name is a clause nothing satisfies, not a parse failure. - **A whole-string `MATCHES`.** `label MATCHES '18:30'` fails against `Book 18:30 public session`; anchor it as `.*18:30.*` or use `CONTAINS`. ## Then question the attribute you chose The control is on screen, but the string you are comparing may live somewhere else on it. | You compared | It holds | Ice-rink example | |---|---|---| | `name` | the identifying string the app sets | `rink.session.book` | | `label` | the human-visible copy, translated and restyled | `Book 18:30 public session` | | `value` | the control's current value | the skate size in a picker wheel | A predicate written against `name` on a control whose identifier was never set, or against `label` on a control whose text sits in a child element, fails exactly the way a typo fails. Try the same literal against a different attribute before you conclude the element is missing. ## Halve the predicate The fastest move is bisection. Reduce the expression to its weakest single clause and confirm it matches at all: 1. Start with `type == 'XCUIElementTypeCell'` alone and see how many elements come back from Find Elements. 2. Add one clause at a time, re-running after each, until the count drops to zero — the clause that zeroed it is the defect. 3. Before blaming the last clause, re-run it with `[cd]` appended to rule out case and diacritics. This takes three round trips and replaces an argument about syntax with a measurement. ## Then question the scope A predicate has no path steps, so its scope comes entirely from the endpoint you sent it to. `POST /session/:sessionId/element` filters from the session's root; `POST /session/:sessionId/element/:elementId/element` filters only the descendants of the element you already hold. A perfectly correct predicate returns nothing when it is scoped to the wrong ice-rink session cell, and the expression gives you no clue, because the restriction is not in the expression. ## Then prove the element is really there Only after the expression and the scope are cleared is it worth doubting the screen. Read the session's page source and look for the control by eye: confirm it is present, confirm the attribute you compared carries the value you assumed, and confirm you are looking at the right one when several ice-rink rows share copy. Two common outcomes: - The control is present but its useful string is on a child element, so the predicate needs a different attribute or a different element. - The control is genuinely absent — the session list had not finished rendering when the find ran — in which case the locator was never the problem. ## A worked repair On an ice-rink session list, `type == 'Button' AND label MATCHES 'Book 18:30'` fails three ways at once. Repaired it reads `type == 'XCUIElementTypeButton' AND label CONTAINS[cd] 'book 18:30'`: the full class name, a substring operator instead of a whole-string regex, and modifiers that survive both a title-case change and a translated build. Better still, once the app sets an identifier, it becomes `type == 'XCUIElementTypeButton' AND name == 'rink.session.book'`, and none of the three failure modes can recur.
- How do you tell a wrong predicate apart from an element that has not rendered yet?Reduce the predicate to its loosest clause — usually `type` alone — and run Find Elements. A non-zero count proves the screen is up and the fault is in the narrowing clauses; a zero count on the loosest clause points at the screen, not the expression. It converts a debate into one measurement.
- Your predicate is correct but returns the wrong ice-rink session row. What now?The predicate is under-specified, not broken. Find Element returns the first match, so add an `AND` clause that distinguishes the rows — an identifying `name`, or a `label` clause on the session time — or scope the find to the parent cell you already hold. Never fix it by depending on match order.
saying these in an interview costs you the question
- Assumes a no-match means the element is absent from the screen.
- Writes type == 'Button' and blames the driver for missing it.
- Rewrites the whole expression instead of bisecting it clause by clause.
- Forgets the find was scoped to a parent element already held.
- Uses MATCHES for a substring and never anchors the pattern.