关于 ios-simulator
面向 iOS 开发者的 Simulator 命令参考技能,介绍使用 xcrun simctl 管理设备、安装和测试应用。还涵盖日志、截图、视频及模拟器能力限制,适用于本地调试和 CI 测试。
主要功能
- 01模拟器设备生命周期管理
- 02应用安装、启动与终止
- 03推送、位置和权限模拟
- 04截图与视频录制
- 05日志流与筛选
- 06模拟器能力限制说明
适用场景
- 01在 CI 中启动并清理模拟器
- 02测试推送载荷和深层链接
- 03模拟位置和隐私权限
- 04捕获应用日志与截图
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill ios-simulator安装后在 Claude Code 中输入/ios-simulator确认已加载。
附带文件evals/evals.jsonreferences/simctl-commands.md
详细介绍
iOS 模拟器
需要完整的命令表、JSON 解析、隐私权限/状态栏值、Logger 筛选器设置或启动恢复命令时,请加载simctl 命令参考。
目录
设备生命周期
列出设备和运行时
# List all available simulators grouped by runtime
xcrun simctl list devices available
# List installed runtimes
xcrun simctl list runtimes
# List only booted devices
xcrun simctl list devices booted
# JSON output for scripting
xcrun simctl list -j devices available
以编程方式解析 JSON 输出,查找特定设备。有关 jq 解析示例,请参阅 references/simctl-commands.md。
创建设备
# Find available device types and runtimes
xcrun simctl list devicetypes
xcrun simctl list runtimes
# Create a device — returns the new UDID
xcrun simctl create "My Test Phone" "iPhone 16 Pro" "com.apple.CoreSimulator.SimRuntime.iOS-18-4"
本技能各示例中的设备类型和运行时标识符仅供参考。运行 simctl list devicetypes 和 simctl list runtimes,查找系统上可用的标识符。
后续命令中使用返回的 UDID。
启动、关机、抹掉、删除
# Boot a specific device
xcrun simctl boot <UDID>
# Boot if needed and wait until the device is ready
xcrun simctl bootstatus <UDID> -b
# Shutdown a running device
xcrun simctl shutdown <UDID>
# Factory reset — wipes all data, keeps the device
xcrun simctl erase <UDID>
# Delete a specific device
xcrun simctl delete <UDID>
# Delete all devices not available in the current Xcode
xcrun simctl delete unavailable
# Shutdown everything
xcrun simctl shutdown all
仅当恰好有一个模拟器正在运行时,才可将 booted 用作 UDID 的简写:
xcrun simctl shutdown booted
如果有多个模拟器已启动,booted 会以非确定性方式选取其中一个。并行运行模拟器时,优先使用明确的 UDID。
在脚本和 CI 中,xcrun simctl bootstatus <UDID> -b 是安装、启动、推送或位置命令之前,用于检查启动和就绪状态的标准门槛。
App 安装与启动
安装 App
# Build for simulator first
xcodebuild build \
-scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
-derivedDataPath build/
# Boot and wait for SpringBoard/services before install
xcrun simctl bootstatus <UDID> -b
# Install the .app bundle
xcrun simctl install <UDID> build/Build/Products/Debug-iphonesimulator/MyApp.app
路径必须指向为模拟器架构构建的 .app 目录,而不是 .ipa 文件。
启动和终止
# Launch by bundle ID
xcrun simctl launch booted com.example.MyApp
# Launch and stream stdout/stderr to the terminal
xcrun simctl launch --console booted com.example.MyApp
# Pass launch arguments
xcrun simctl launch booted com.example.MyApp --reset-onboarding -AppleLanguages "(fr)"
# Terminate a running app
xcrun simctl terminate booted com.example.MyApp
--console 适用于调试,可在终端中直接显示 print() 和 os_log 输出。
App 容器路径
# App bundle location
xcrun simctl get_app_container booted com.example.MyApp app
# Data container (Documents, Library, tmp)
xcrun simctl get_app_container booted com.example.MyApp data
# Shared app group container
xcrun simctl get_app_container booted com.example.MyApp group.com.example.shared
测试工作流
模拟推送通知
创建 JSON 载荷文件:
{
"aps": {
"alert": {
"title": "New Message",
"body": "You have a new message from Alice"
},
"badge": 3,
"sound": "default"
},
"customKey": "customValue"
}
将其发送到模拟器:
# Send push payload from file
xcrun simctl push booted com.example.MyApp payload.json
# Pipe payload from stdin
echo '{"aps":{"alert":"Quick test"}}' | xcrun simctl push booted com.example.MyApp -
这会测试本地载荷处理和通知 UI,而不是 APNs 投递。
位置模拟
# Set a fixed coordinate (latitude, longitude)
xcrun simctl location booted set 37.3349,-122.0090
# List available predefined scenarios
xcrun simctl location booted list
# Run a predefined scenario
xcrun simctl location booted run "City Run"
# Follow custom command-line waypoints
xcrun simctl location booted start --speed=15 --interval=1 \
37.3349,-122.0090 37.3317,-122.0307
# Read waypoints from stdin, one "lat,lon" pair per line
printf "37.3349,-122.0090\n37.3317,-122.0307\n" | \
xcrun simctl location booted start --distance=100 -
# Clear the simulated location
xcrun simctl location booted clear
使用 set 设置单个坐标,使用 run 指定预定义场景名称,使用 start 设置自定义路径点路线。命令的适用范围很重要:simctl location run 接受内置场景名称(例如“City Run”和“Freeway Drive”),而不接受 GPX 文件路径;对于自定义坐标路径点,应使用命令行方式 simctl location start。基于 GPX 的路线请使用 Xcode 的 Debug > Simulate Location 菜单。
位置模拟会影响已启动设备上所有使用 Core Location 的 App。完成后请清除位置设置,以免测试结果出现意外。
隐私权限
# Grant a permission
xcrun simctl privacy booted grant photos com.example.MyApp
# Revoke a permission
xcrun simctl privacy booted revoke microphone com.example.MyApp
# Reset all permissions for the app
xcrun simctl privacy booted reset all com.example.MyApp
常见服务名称:photos、microphone、contacts、calendar、reminders、location、location-always、motion、siri。完整列表请参阅 references/simctl-commands.md。
在 CI 中预先授予权限,可避免系统权限对话框阻塞自动化测试运行,但可能会掩盖缺失的使用说明键。请保留必要的 Info.plist 隐私字符串,并仍然测试正常的提示流程。
深层链接和 URL
# Open a URL (triggers universal links or custom URL schemes)
xcrun simctl openurl booted "https://example.com/product/123"
# Custom URL scheme
xcrun simctl openurl booted "myapp://settings/notifications"
要使用通用链接,必须配置 App 的关联域名权限。模拟器会使用该域名中的 apple-app-site-association 文件。
状态栏覆盖
# Set a clean status bar for screenshots
xcrun simctl status_bar booted override \
--time "9:41" \
--batteryState charged \
--batteryLevel 100 \
--cellularMode active \
--cellularBars 4 \
--wifiBars 3 \
--operatorName ""
# Clear all overrides
xcrun simctl status_bar booted clear
使用状态栏覆盖来生成一致的 App Store 截图。截取后务必清除覆盖设置,以免影响其他测试。
截图和视频录制
# Capture a screenshot
xcrun simctl io booted screenshot screenshot.png
# Record video (press Ctrl+C to stop)
xcrun simctl io booted recordVideo recording.mov
# Screenshot with specific display mask
xcrun simctl io booted screenshot --mask black screenshot.png
--mask 选项:ignored(默认,不使用遮罩)、alpha(透明圆角)和 black(黑色圆角)。截取显示设备外形的屏幕截图时,请使用 alpha 或 black。alpha 遮罩仅支持屏幕截图——录制视频时会回退到 black。
视频录制会持续进行,直到进程收到 SIGINT(Ctrl+C)。录制内容只会在停止后保存——使用 SIGKILL 终止进程会导致文件丢失。
日志流
基本日志流
# Stream all logs at debug level and above
xcrun simctl spawn booted log stream --level debug
# Filter by subsystem
xcrun simctl spawn booted log stream --level debug \
--predicate 'subsystem == "com.example.app"'
# Filter by subsystem and category
xcrun simctl spawn booted log stream --level debug \
--predicate 'subsystem == "com.example.app" AND category == "networking"'
# Filter by process name
xcrun simctl spawn booted log stream \
--predicate 'process == "MyApp"'
定义 Logger 子系统/类别以及匹配 log stream
谓词时,请参考Logger 和筛选后的流式传输。
编译时模拟器检测
使用 #if targetEnvironment(simulator) 排除无法在模拟器中运行的代码:
func registerForPush() {
#if targetEnvironment(simulator)
logger.info("Skipping APNs registration — running in Simulator")
#else
UIApplication.shared.registerForRemoteNotifications()
#endif
}
通过环境变量进行运行时检测:
var isSimulator: Bool {
ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil
}
优先使用编译时检查(#if targetEnvironment(simulator)),而非运行时检查。编译器会彻底剔除被排除的代码,从而避免因符号不可用而导致的链接器错误。
模拟器限制
请以此表作为权威分类。支持某种触发方式或代理,并不代表其行为与真实设备一致。
| 功能 | 模拟器支持情况 |
|---|---|
| APNs 推送送达 | 不支持——使用 simctl push 进行本地模拟 |
| 性能一致性 | 仅可作相对参考——模拟器无法准确反映 CPU/处理性能、网络速度、图形/Metal、内存带宽、帧时间、内存压力、Jetsam、着色器正确性或热行为;对性能敏感的工作请在硬件上验证 |
| Metal GPU 系列一致性 | 部分支持——模拟器使用宿主 Mac 的 GPU,而非设备 GPU;部分着色器和限制有所不同 |
| 相机硬件 | 不支持——使用照片图库注入,或模拟 AVCaptureSession |
| 音频输入/麦克风 | 不支持通用应用音频输入;可通过模拟器菜单激活 Siri |
| Secure Enclave | 不支持——kSecAttrTokenIDSecureEnclave 操作会失败 |
| App Attest(DCAppAttestService) | 不支持——isSupported 会返回 false |
| DockKit 电机控制 | 不支持——无法连接实体配件 |
| 加速度计/陀螺仪 | 不支持运动传感器;依赖运动数据的行为请使用真实设备 |
| 气压计 | 不支持 |
| NFC(Core NFC) | 不支持 |
| Bluetooth(Core Bluetooth) | 不支持——BLE 测试请使用真实设备 |
| CarPlay 显示模拟 | 支持通过模拟器的外接显示器/CarPlay 选项进行模拟;仍需在车辆或设备设置中验证 |
| Face ID/Touch ID 硬件 | 无硬件支持——使用模拟器中的 Features > Face ID / Touch ID 菜单 |
| 内存警告、位置更改、手动 iCloud 同步触发 | 支持通过模拟器菜单或 simctl 触发;手动同步可用于测试应用回调处理 |
| iCloud 自动传播和冲突 | 真实账户/设备、通知触发的同步、后台送达、冲突以及账户/设备状态差异均需要硬件 |
| 蜂窝网络条件 | 不支持——在 Mac 上使用 Network Link Conditioner |
常见错误
不要:在脚本中硬编码模拟器 UDID
删除并重新创建模拟器后,UDID 会发生变化。硬编码的值在其他机器和 CI 中会失效。
# WRONG — hardcoded UDID
xcrun simctl boot "A1B2C3D4-E5F6-7890-ABCD-EF1234567890"
# CORRECT — look up by name and runtime
UDID=$(xcrun simctl list -j devices available | \
jq -r '.devices["com.apple.CoreSimulator.SimRuntime.iOS-18-4"][] | select(.name == "iPhone 16 Pro") | .udid')
xcrun simctl boot "$UDID"
# CORRECT — use "booted" when one simulator is running
xcrun simctl install booted MyApp.app
不要:在已关机的模拟器上安装或启动应用
simctl install 和 simctl launch 需要设备处于已启动状态。在已关机的设备上执行时,它们可能静默失败,或返回不易理解的错误。
# WRONG — device is not booted
xcrun simctl install <UDID> MyApp.app # fails
# CORRECT — boot if needed and wait for readiness, then install
xcrun simctl bootstatus <UDID> -b
xcrun simctl install <UDID> MyApp.app
xcrun simctl launch <UDID> com.example.MyApp
不要:在 CI 中留下僵尸模拟器
每个已启动的模拟器都会占用内存和 CPU。创建模拟器却不进行清理的 CI 流水线会不断累积僵尸设备。
# WRONG — CI script creates and boots but never cleans up
xcrun simctl create "CI Phone" "iPhone 16 Pro" "com.apple.CoreSimulator.SimRuntime.iOS-18-4"
xcrun simctl boot "$UDID"
# ... tests run, pipeline exits ...
# CORRECT — always clean up in CI teardown
cleanup() {
xcrun simctl shutdown all
xcrun simctl delete "$UDID"
}
trap cleanup EXIT
不要:在卡住的模拟器上反复尝试启动
处于“Booting”状态的模拟器不会因重试 boot 而恢复。底层的 CoreSimulator 状态已损坏。
# WRONG — retry loop on a stuck device
xcrun simctl boot "$UDID" # "Unable to boot device in current state: Booting"
xcrun simctl boot "$UDID" # same error, forever
# CORRECT — shut down, erase, and retry
xcrun simctl shutdown "$UDID"
xcrun simctl erase "$UDID"
xcrun simctl boot "$UDID"
# If that fails, reset CoreSimulator entirely
xcrun simctl shutdown all
xcrun simctl erase all
# Last resort: rm -rf ~/Library/Developer/CoreSimulator/Caches
检查清单
- 创建模拟器设备时,明确指定设备类型和运行时标识符
- 脚本使用
booted或从 JSON 输出中解析的 UDID,而不是硬编码的值 - 开发期间通过
simctl push测试推送通知载荷 - 发布前在真实设备上验证推送通知是否送达
- 根据需要,使用固定坐标、预定义场景和自定义航点测试位置模拟
- 在 CI 中预先授予隐私权限,避免出现阻塞对话框
- 使用
#if targetEnvironment(simulator)守护在 Simulator 中不可用的 API - 截取屏幕后清除状态栏覆盖设置
- CI 流水线在清理阶段关闭并删除模拟器
- 配置基于子系统/类别谓词的日志流,以便有针对性地调试
- 调试时使用 App 容器路径检查沙盒数据