How device_utils.py Structures xcrun simctl Commands in the iOS Simulator Skill
The build_simctl_command function generates xcrun simctl commands as a Python list of strings (e.g., ["xcrun", "simctl", "launch", "booted", "com.app.bundle"]), ready for immediate execution via subprocess.run without shell interpolation.
The conorluddy/ios-simulator-skill repository centralizes all simulator automation logic through a single utility module that defines the specific structure and format of xcrun simctl commands. By returning structured data rather than raw shell strings, the codebase eliminates parsing errors and ensures consistent device targeting across all iOS simulator operations.
Command Structure and Format
According to the source code in ios-simulator-skill/scripts/common/device_utils.py (lines 58-66), every generated command follows a strict three-part sequence:
- Base prefix – The immutable foundation
["xcrun", "simctl", <operation>]. - Device identifier – Either the supplied
udidor the literal"booted"whenudidisNone. - Additional arguments – Any extra parameters stringified and appended in order.
Base Prefix and Operation
The command list always begins with the Xcode command-line tool invocation followed by the specific simctl subcommand:
cmd = ["xcrun", "simctl", operation]
Valid operations include standard subcommands such as launch, install, boot, or shutdown.
Device Targeting Logic
The function implements automatic fallback logic to target the currently running simulator when no explicit UUID is provided:
cmd.append(udid if udid else "booted")
When the udid parameter is None, the string "booted" is appended, instructing simctl to act upon the active device. Otherwise, the specific device identifier is inserted at this position.
Argument Appending
Remaining positional arguments are safely converted to strings and extended into the list:
cmd.extend(str(arg) for arg in args)
This allows file paths, bundle identifiers, or numeric flags to be passed without manual type conversion or concatenation.
Implementation in device_utils.py
The complete construction logic resides in ios-simulator-skill/scripts/common/device_utils.py and builds the command list as follows:
cmd = ["xcrun", "simctl", operation] # base
cmd.append(udid if udid else "booted") # device
cmd.extend(str(arg) for arg in args) # extra args
Because the function returns a plain list, callers can pass it directly to subprocess.run:
from ios_simulator_skill.scripts.common.device_utils import build_simctl_command
import subprocess
# Launch an app on the currently booted simulator
cmd = build_simctl_command("launch", None, "com.example.myapp")
subprocess.run(cmd, check=True)
# Install an app on a specific device by UDID
cmd = build_simctl_command("install", "123E4567-F89A-0BCD-EF12-3456789ABC0D", "/tmp/MyApp.app")
subprocess.run(cmd, check=True)
# Shut down a specific device
cmd = build_simctl_command("shutdown", "123E4567-F89A-0BCD-EF12-3456789ABC0D")
subprocess.run(cmd, check=True)
Documented Command Examples
The function’s docstring (lines 45-56) provides concrete illustrations of the final list structure:
| Function Call | Resulting Command List |
|---|---|
build_simctl_command("launch", None, "com.app.bundle") |
["xcrun", "simctl", "launch", "booted", "com.app.bundle"] |
build_simctl_command("launch", "ABC123", "com.app.bundle") |
["xcrun", "simctl", "launch", "ABC123", "com.app.bundle"] |
build_simctl_command("install", "ABC123", "/path/to/app.app") |
["xcrun", "simctl", "install", "ABC123", "/path/to/app.app"] |
These examples demonstrate the automatic "booted" substitution and the preservation of argument order.
Integration with Subprocess
Scripts such as app_launcher.py, simctl_boot.py, and simctl_shutdown.py consume this builder to ensure consistent command construction. The list format provides inherent safety by bypassing shell interpretation, preventing injection vulnerabilities that would arise from string concatenation.
Summary
- List Format: The function returns a
list[str]suitable forsubprocess.run(cmd, ...)withoutshell=True. - Three Components: Commands are assembled as
[base] + [device] + [arguments]. - Automatic Fallback: A
Nonevalue forudidautomatically resolves to the"booted"target. - Type Safety: Non-string arguments are coerced via
str(arg)before appending. - Source Location: All logic is contained in
ios-simulator-skill/scripts/common/device_utils.py.
Frequently Asked Questions
What is the exact return type of build_simctl_command?
The function returns a Python list of strings (e.g., ["xcrun", "simctl", "launch", "booted", "com.app.bundle"]). This format is explicitly designed for direct consumption by the subprocess module, eliminating the need for shell tokenization or escaping.
How does the function handle an unspecified device identifier?
When the udid parameter is None, the function automatically appends the literal string "booted" as the device target. This convention targets whichever iOS simulator is currently running, simplifying automation scripts that do not track specific UUIDs.
Can the function process non-string arguments like integers or Path objects?
Yes. The implementation uses cmd.extend(str(arg) for arg in args) at line 66, which converts all additional arguments to strings before appending them to the command list. This allows Path objects, integers, or other types to be passed safely without pre-conversion.
Which scripts in the repository utilize this command builder?
The build_simctl_command utility is imported by multiple execution modules, including app_launcher.py, simctl_boot.py, and simctl_shutdown.py. This centralized approach ensures that all xcrun simctl invocations across the codebase maintain identical structural conventions and device resolution behavior.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →