| name | ios-settings |
| description | iOS Settings (com.apple.Preferences). Apple's first-party device-config app - open About, set language/region, toggle airplane mode, set dark mode, etc. |
iOS Settings
Bundle id: com.apple.Preferences. Apple's first-party settings app, available on every iOS device and simulator.
Available flows
| Function | Purpose |
|---|
settings_open_root() | Launch Settings, return root ui_tree |
settings_open_section(name) | Open a top-level row by name |
settings_open_about() | General → About, return full ExecutionResult |
settings_get_ios_version() | Read iOS version off About screen |
settings_get_device_name() | Read device name off About screen |
settings_set_dark_mode(on) | Display & Brightness → Light/Dark |
settings_toggle_airplane_mode(on) | Root list → Airplane Mode switch |
The first three are validated on iPhone 17 Pro / iOS 26.4. The toggles below them follow the same pattern but expect to refine predicates on first run.
Gotchas
text="General" is ambiguous
Two elements match on the Settings root: a small label at top (likely a search-keyword surface, ~y=73) and the actual cell row (~y=224). Both are XCUIElementTypeStaticText, neither is the cell.
tap(Predicate(text="General"))
tap(Predicate(text="General", index=1))
This pattern repeats for other top-level sections (Display & Brightness, Privacy & Security, etc.). Default to index=1 for any top-level row tap, or wrap via settings_open_section(name).
text="About" matches a Cell and a child Button
On the General submenu, the About row has both a XCUIElementTypeCell wrapping the text and an inner XCUIElementTypeButton carrying the same text. Tapping by plain text → AMBIGUOUS_MATCH.
tap(Predicate(text="About"))
tap(Predicate(text="About", type="cell"))
About screen value extraction
The About screen renders combined-label rows as a single Button text + two child StaticTexts:
[18] Button "iOS Version, 26.4" #SW_VERSION_SPECIFIER
[19] StaticText "iOS Version" (the label)
[20] StaticText "26.4" (the value)
The Button's .text ("iOS Version, 26.4") is the most stable extraction surface - neither StaticText alone is unique enough. Use a regex on the button text:
re.search(r'Button "iOS Version,\s*([^"]+)"', ui_tree)
The Name row uses the cell directly (Cell "Name, iPhone" #NAME_CELL_ID) and includes a stable accessibility id - prefer that anchor over text matching.
Stable accessibility ids on About
Worth pinning predicates to when available - these survive locale changes and Apple's wording tweaks:
| Row | Accessibility id |
|---|
| Name | NAME_CELL_ID |
| iOS Version | SW_VERSION_SPECIFIER |
Use via Predicate(accessibility_id="SW_VERSION_SPECIFIER") instead of the text-based regex when possible.
Recommended patterns
- Always start from root. Every flow function calls
open_app(SETTINGS_BUNDLE_ID) first. Do not assume the user left Settings on a particular pane - Apple may have collapsed/restored state, the device may have rebooted, etc.
wait_for(stable=True, timeout_ms=3000) after each navigation. Settings has subtle animations that hold input briefly.
- Localization breaks text predicates. If the device language is not English, every text-based predicate above will miss. Pin to
accessibility_id (where present) or bounds_hint for locale-resilient flows.
What does NOT work
text="Settings" as a generic anchor - appears in the nav bar of every sub-screen and also as the App's name in Spotlight. Never matches what you think.
- Tapping by coordinates. Settings adapts to device size, orientation, and dynamic type. Coordinate clicks are the wrong tool here even when the predicate is fiddly.
navigate("back") on iOS. iOS lacks a hardware back. Tap the BackButton predicate from the navigation bar instead, or rely on the < chevron in the top-left.
open_app alone is not deterministic. iOS preserves the last-visited Settings pane across app activations. Every flow function in this skill calls open_app(SETTINGS_BUNDLE_ID, fresh=True) followed by wait_for(stable=True, ...). The cost is ~200ms; the win is reproducibility across sessions.