Skip to content

Commit 943ab0a

Browse files
thymikeeclaude
authored andcommitted
perf(ios): resolve text fields via typed queries, not full-tree enumeration
textInputAt used app.descendants(.any).allElementsBoundByIndex (snapshots EVERY element) to find the text input at a point. fill drove this repeatedly: once it has coordinates, resolveTextEntryElement re-runs textInputAt on every verify/repair poll iteration whenever the focused-field reference goes stale (e.g. the Settings search bar repositioning bottom->top), so the full-tree enum dominated fill latency. Query the text-input element types directly (app.textFields/secureTextFields/ searchFields/textViews) instead. Same matches, but XCUITest resolves typed queries without snapshotting the whole tree. Measured (iPhone 17 sim, warm runner): fill 25 chars ~14.5s -> ~4.5s (3.2x), 6/6 exact. Same primitive #632 killed for get text.
1 parent d0d2cc3 commit 943ab0a

1 file changed

Lines changed: 13 additions & 8 deletions

File tree

ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -337,17 +337,22 @@ extension RunnerTests {
337337
let point = CGPoint(x: x, y: y)
338338
var matched: XCUIElement?
339339
let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
340+
// Query the text-input element types directly instead of enumerating the entire tree
341+
// (app.descendants(.any).allElementsBoundByIndex snapshots every element and is ~10x
342+
// slower — it dominated fill latency because resolveTextEntryElement re-runs this on
343+
// each verify/repair poll once the focused field reference goes stale).
340344
// Prefer the smallest matching field so nested editable controls win over large containers.
341-
let candidates = app.descendants(matching: .any).allElementsBoundByIndex
345+
let candidates = [
346+
app.textFields,
347+
app.secureTextFields,
348+
app.searchFields,
349+
app.textViews,
350+
]
351+
.flatMap { $0.allElementsBoundByIndex }
342352
.filter { element in
343353
guard element.exists else { return false }
344-
switch element.elementType {
345-
case .textField, .secureTextField, .searchField, .textView:
346-
let frame = element.frame
347-
return !frame.isEmpty && frameContainsPoint(frame, point, tolerance: 2)
348-
default:
349-
return false
350-
}
354+
let frame = element.frame
355+
return !frame.isEmpty && frameContainsPoint(frame, point, tolerance: 2)
351356
}
352357
.sorted { left, right in
353358
let leftArea = max(1, left.frame.width * left.frame.height)

0 commit comments

Comments
 (0)