mcpField
I
ios-simulator

ios-simulator

github.com/dpearson2699测试与质量1.2k3.1k 次安装13 周前更新
代码仓库

关于 ios-simulator

面向 iOS 开发者的 Simulator 命令参考技能,介绍使用 xcrun simctl 管理设备、安装和测试应用。还涵盖日志、截图、视频及模拟器能力限制,适用于本地调试和 CI 测试。

主要功能

  1. 01模拟器设备生命周期管理
  2. 02应用安装、启动与终止
  3. 03推送、位置和权限模拟
  4. 04截图与视频录制
  5. 05日志流与筛选
  6. 06模拟器能力限制说明

适用场景

  1. 01在 CI 中启动并清理模拟器
  2. 02测试推送载荷和深层链接
  3. 03模拟位置和隐私权限
  4. 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 容器路径检查沙盒数据

参考资料

评论

支持 Markdown