Skip to content

refactor(ios): safely(tag:default:) wrapper for the catch-log-and-default band-aid#660

Merged
thymikee merged 1 commit into
mainfrom
refactor/ios-runner-safely-wrapper
Jun 2, 2026
Merged

refactor(ios): safely(tag:default:) wrapper for the catch-log-and-default band-aid#660
thymikee merged 1 commit into
mainfrom
refactor/ios-runner-safely-wrapper

Conversation

@thymikee
Copy link
Copy Markdown
Member

@thymikee thymikee commented Jun 2, 2026

What

Consolidate the runner's repeated catch-log-and-default band-aid into one generic helper (RunnerTests+Exceptions.swift):

func safely<T>(_ tag: String, _ fallback: T, _ block: () -> T) -> T
func safely<T>(_ tag: String, _ block: () -> T?) -> T?   // nil-default convenience

11 uniform sites adopt it — SystemModal (4), Snapshot (2), TextEntry (2), Interaction (3). Each var x = default; catchException({ x = … }); if let m { NSLog("…_IGNORED_EXCEPTION=%@", m); return default }; return x shrinks to a single safely("TAG", default) { … }.

Why

20 catchException sites across the runner; ~15 followed the same silent-log-and-continue shape, copy-pasted with a per-site tag. One helper removes the drift, and gives the "silently logged and continued" path a single instrumentation point (per-tag exception-rate telemetry later).

Safety

  • Behavior-preserving: same defaults, same returned values, and the NSLog output is byte-identical — the tag arg reproduces the exact format (tag "MODAL_QUERY"AGENT_DEVICE_RUNNER_MODAL_QUERY_IGNORED_EXCEPTION=%@).
  • RunnerObjCExceptionCatcher.catchException is non-escaping, so block can still capture inout state (e.g. safeIsActionableCandidate's seen).
  • Left inline by design (not the uniform pattern): the silent _ = catchException KVC reads (elementHasFocus, snapshotHasFocus), the throw-on-AX snapshot path, executeOnMainSafely's retry-driving catch, and the bespoke pressKeyboardReturn fallback / performElementTap branches whose result depends on the exception message.
  • Verified: xcodebuild build-for-testing → TEST BUILD SUCCEEDED.

Scope

Candidate ② from the post-merge refactor re-scan. Independent of #659 (that's CommandExecution-only; this leaves the one CommandExecution catch — the retry driver — untouched). New file auto-included via the file-system-synchronized group.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

Size Report

Metric Base Current Diff
JS raw 1.1 MB 1.1 MB 0 B
JS gzip 360.8 kB 360.8 kB 0 B
npm tarball 463.7 kB 464.1 kB +338 B
npm unpacked 1.5 MB 1.5 MB -1.6 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 24.1 ms 23.7 ms -0.4 ms
CLI --help 35.4 ms 36.2 ms +0.8 ms

Top changed chunks: no changes in the largest emitted chunks.

…ault band-aid

Consolidate the repeated `var x = default; catchException({ x = expr }); if let m {
NSLog("..._IGNORED_EXCEPTION=%@", m); return default }; return x` shape used around
exception-prone XCUITest queries into one generic helper (RunnerTests+Exceptions.swift):

  func safely<T>(_ tag:, _ fallback:, _ block:) -> T
  func safely<T>(_ tag:, _ block: () -> T?) -> T?   // nil-default convenience

11 uniform sites adopt it: SystemModal (4), Snapshot (2), TextEntry (2), Interaction (3).
Each drops from ~7-12 lines to 1-3. The NSLog format is preserved byte-for-byte via the tag arg
(tag "MODAL_QUERY" -> "AGENT_DEVICE_RUNNER_MODAL_QUERY_IGNORED_EXCEPTION=%@"), so the
silently-logged-and-continued path keeps its searchable format and now has a single place to add
per-tag exception telemetry.

Left inline by design (not the uniform pattern): the silent `_ = catchException` KVC reads
(elementHasFocus, snapshotHasFocus), the throw-on-AX snapshot path, executeOnMainSafely's
retry-driving catch, and the bespoke pressKeyboardReturn fallback / performElementTap branches
whose result depends on the exception message.

Behavior-preserving: same defaults, same log output, same returned values. catchException is
non-escaping, so the inout capture in safeIsActionableCandidate is preserved.

Verified: xcodebuild build-for-testing -> TEST BUILD SUCCEEDED.
@thymikee thymikee force-pushed the refactor/ios-runner-safely-wrapper branch from 1014dce to d3a50ee Compare June 2, 2026 02:19
@thymikee
Copy link
Copy Markdown
Member Author

thymikee commented Jun 2, 2026

Verified on-device ✅

Ran examples/test-app/replays/gesture-lab.ad against the #660-built runner (iPhone 17 simulator, Metro 8081):

PASS gesture-lab.ad (44.0s) — 1 passed, 0 failed

The replay drives pinch/rotate/pan/fling and the snapshot + element-query + keyboard-frame paths (via its wait/assertions) — all of which now route through safely() — and every assertion held (pinch changed yes, rotate changed yes, pan/fling). So the refactor is behavior-preserving end-to-end, not just compile-clean.

Re: review

Thanks — all findings are low/optional and I agree:

  • safeSnapshotViewport nuance: correct that on main it logs-but-doesn't-early-return, yet it's equivalent here — the initial value and the safely fallback are both CGRect.infinite, and a successful snapshotViewport wins on the success path. (Noting it here for future readers, as suggested.)
  • fallback vs exception indistinguishability / per-tag telemetry: pre-existing; when telemetry is added I'll have safely surface a hadException signal rather than overloading the return value.
  • no automated log-format assertion: acknowledged; the UITest bundle is a weak unit-test host, so this stays on build-for-testing + a possible grep-based CI check on the tag strings as a follow-up.

@thymikee thymikee merged commit daaf71a into main Jun 2, 2026
18 checks passed
@thymikee thymikee deleted the refactor/ios-runner-safely-wrapper branch June 2, 2026 02:25
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-06-02 02:26 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant