
Ios Simulator Skill
Run a token-efficient accessibility audit on the current iOS Simulator screen and get prioritized critical, warning, and info fixes before ship.
Overview
ios-simulator-skill is an agent skill most often used in Ship (also Build frontend) that audits the iOS Simulator screen for accessibility rule violations and returns prioritized fixes.
Install
npx skills add https://github.com/conorluddy/ios-simulator-skill --skill ios-simulator-skillWhat is this skill?
- Python accessibility_audit.py scans live simulator UI via shared common helpers
- Critical rules: missing labels on buttons/links, empty buttons, images without alt text
- Warning rules for sliders and text fields missing hints
- JSON output tuned for minimal token use with configurable label truncation and top-issue limits
- Severity buckets: critical, warning, info with rule id and suggested fix per element
- 3 critical accessibility rule families
- Top issues capped via IOS_SIM_A11Y_TOP_ISSUES (default 10)
- 3 severity levels: critical, warning, info
Adoption & trust: 1.6k installs on skills.sh; 1.1k GitHub stars; 2/3 security scanners passed (skills.sh audits); trending (+100% hot-view momentum).
What problem does it solve?
You cannot quickly see which simulator elements fail labeling and hint rules without spelunking raw accessibility trees by hand.
Who is it for?
Indie developers iterating SwiftUI or UIKit flows who want automated a11y regression checks in the simulator during agent-assisted builds.
Skip if: Physical-device-only testing, Android projects, or teams that need certified WCAG/legal attestations without human VoiceOver validation.
When should I use this skill?
You have a booted iOS Simulator showing the screen to test and need a fast accessibility compliance scan with actionable fixes.
What do I get? / Deliverables
You receive a compact JSON issue list with severity, rule name, element type, and fix hints scoped to the current simulator screen.
- JSON accessibility issue report
- Per-element severity and fix recommendations
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Automated a11y scanning is a quality gate best shelved under Ship testing, even though you invoke it repeatedly while building UI. The skill executes simulator-side checks against accessibility trees—classic pre-release and regression testing, not distribution or infra.
Where it fits
After adding a new settings form, run the audit to catch TextFields and Sliders missing accessibility hints before merging.
Gate a release candidate by scanning checkout and onboarding screens for unlabeled buttons and images.
Re-run the auditor on reproduced bug screens when users report VoiceOver navigation failures.
How it compares
Simulator-local checker script—not an App Store Connect MCP or cloud device farm.
Common Questions / FAQ
Who is ios-simulator-skill for?
Solo and indie iOS builders using AI coding agents who already run the iOS Simulator and want scripted accessibility audits on the current screen.
When should I use ios-simulator-skill?
In Build frontend iterations after UI changes, and in Ship testing before release, by running the accessibility audit against a booted simulator with your app on the target screen.
Is ios-simulator-skill safe to install?
It runs local Python against your simulator via subprocess helpers; review the Security Audits panel on this Prism page and inspect scripts before granting shell access to your agent.
SKILL.md
READMESKILL.md - Ios Simulator Skill
#!/usr/bin/env python3 """ iOS Simulator Accessibility Audit Scans the current simulator screen for accessibility compliance issues. Optimized for minimal token output while maintaining functionality. Usage: python scripts/accessibility_audit.py [options] """ import argparse import json import subprocess import sys from dataclasses import asdict, dataclass from typing import Any from common import flatten_tree, get_accessibility_tree, resolve_udid from common.env_config import env_int A11Y_LABEL_MAX = env_int("IOS_SIM_A11Y_LABEL_MAX", 80) A11Y_TOP_ISSUES = env_int("IOS_SIM_A11Y_TOP_ISSUES", 10) @dataclass class Issue: """Represents an accessibility issue.""" severity: str # critical, warning, info rule: str element_type: str issue: str fix: str def to_dict(self) -> dict: """Convert to dictionary for JSON serialization.""" return asdict(self) class AccessibilityAuditor: """Performs accessibility audits on iOS simulator screens.""" # Critical rules that block users CRITICAL_RULES = { "missing_label": lambda e: e.get("type") in ["Button", "Link"] and not e.get("AXLabel"), "empty_button": lambda e: e.get("type") == "Button" and not (e.get("AXLabel") or e.get("AXValue")), "image_no_alt": lambda e: e.get("type") == "Image" and not e.get("AXLabel"), } # Warnings that degrade UX WARNING_RULES = { "missing_hint": lambda e: e.get("type") in ["Slider", "TextField"] and not e.get("help"), "missing_traits": lambda e: e.get("type") and not e.get("traits"), } # Info level suggestions INFO_RULES = { "no_identifier": lambda e: not e.get("AXUniqueId"), "deep_nesting": lambda e: e.get("depth", 0) > 5, } def __init__(self, udid: str | None = None): """Initialize auditor with optional device UDID.""" self.udid = udid def get_accessibility_tree(self) -> dict: """Fetch accessibility tree from simulator using shared utility.""" return get_accessibility_tree(self.udid, nested=True) @staticmethod def _is_small_target(element: dict) -> bool: """Check if touch target is too small (< 44x44 points).""" frame = element.get("frame", {}) width = frame.get("width", 0) height = frame.get("height", 0) return width < 44 or height < 44 def _flatten_tree(self, node: dict, depth: int = 0) -> list[dict]: """Flatten nested accessibility tree for easier processing using shared utility.""" return flatten_tree(node, depth) def audit_element(self, element: dict) -> list[Issue]: """Audit a single element for accessibility issues.""" issues = [] # Check critical rules for rule_name, rule_func in self.CRITICAL_RULES.items(): if rule_func(element): issues.append( Issue( severity="critical", rule=rule_name, element_type=element.get("type", "Unknown"), issue=self._get_issue_description(rule_name), fix=self._get_fix_suggestion(rule_name), ) ) # Check warnings (skip if critical issues found) if not issues: for rule_name, rule_func in self.WARNING_RULES.items(): if rule_func(element): issues.append( Issue( severity="warning", rule=rule_name, element_type=element.get("type", "Unknown"), issue=self._get_issue_description(rule_name), fix=self._get_fix_suggestion(rule_name), ) ) # Check info level (only if verbose or no other issues) if not issues: for rule_name, rule_func in self.