serve-sim-apple-simulator
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseserve-sim — Apple Simulator Hosting Tool
serve-sim — Apple模拟器托管工具
Skill by ara.so — Daily 2026 Skills collection.
serve-simnpx servesimctl io由ara.so提供的技能——Daily 2026技能合集。
serve-simnpx servesimctl ioRequirements
要求
- macOS with Xcode command line tools installed (must be available)
xcrun simctl - At least one booted iOS Simulator
- Node.js / npm (for )
npx
- 安装了Xcode命令行工具的macOS系统(必须能使用)
xcrun simctl - 至少有一个已启动的iOS模拟器
- Node.js / npm(用于)
npx
Quick Start
快速开始
sh
undefinedsh
undefinedStart preview server (auto-detects booted simulator)
启动预览服务器(自动检测已启动的模拟器)
npx serve-sim
npx serve-sim
→ Preview at http://localhost:3200
→ 预览地址:http://localhost:3200
undefinedundefinedCLI Reference
CLI参考
sh
serve-sim [device...] # Start preview server (default: localhost:3200)
serve-sim --no-preview [device...] # Stream only, no web UI
serve-sim gesture '<json>' [-d udid] # Send a touch gesture
serve-sim button [name] [-d udid] # Send a button press (default: home)
serve-sim rotate <orientation> [-d udid]
serve-sim ca-debug <option> <on|off> [-d udid]
serve-sim memory-warning [-d udid] # Simulate a memory warningsh
serve-sim [device...] # 启动预览服务器(默认:localhost:3200)
serve-sim --no-preview [device...] # 仅流传输,不显示网页UI
serve-sim gesture '<json>' [-d udid] # 发送触摸手势
serve-sim button [name] [-d udid] # 发送按键指令(默认:home键)
serve-sim rotate <orientation> [-d udid]
serve-sim ca-debug <option> <on|off> [-d udid]
serve-sim memory-warning [-d udid] # 模拟内存警告Options
选项
-p, --port <port> Starting port (preview default: 3200, stream default: 3100)
-d, --detach Spawn helper and exit (daemon mode)
-q, --quiet JSON-only output
--no-preview Skip the web UI; stream in foreground only
--list [device] List running streams
--kill [device] Kill running stream(s)
undefined-p, --port <port> 起始端口(预览默认:3200,流传输默认:3100)
-d, --detach 启动辅助程序后退出(守护进程模式)
-q, --quiet 仅输出JSON格式内容
--no-preview 跳过网页UI;仅在前台进行流传输
--list [device] 列出正在运行的流
--kill [device] 终止正在运行的流
undefinedCommon CLI Examples
常用CLI示例
sh
undefinedsh
undefinedTarget a specific device by name
通过名称指定特定设备
serve-sim "iPhone 16 Pro"
serve-sim "iPhone 16 Pro"
Start on a custom port
在自定义端口启动
serve-sim -p 4000
serve-sim -p 4000
Start as background daemon, returns JSON with stream info
以后台守护进程模式启动,返回包含流信息的JSON
serve-sim --detach
serve-sim --detach
List all running streams
列出所有正在运行的流
serve-sim --list
serve-sim --list
Kill all running helpers
终止所有运行中的辅助程序
serve-sim --kill
serve-sim --kill
Send home button press
发送home键按下指令
serve-sim button home -d <udid>
serve-sim button home -d <udid>
Rotate simulator
旋转模拟器
serve-sim rotate landscape_left -d <udid>
serve-sim rotate landscape_left -d <udid>
orientations: portrait | portrait_upside_down | landscape_left | landscape_right
可选方向:portrait | portrait_upside_down | landscape_left | landscape_right
Toggle CoreAnimation debug flags
切换CoreAnimation调试标志
serve-sim ca-debug slow-animations on -d <udid>
serve-sim ca-debug slow-animations on -d <udid>
options: blended | copies | misaligned | offscreen | slow-animations
可选选项:blended | copies | misaligned | offscreen | slow-animations
Simulate memory warning
模拟内存警告
serve-sim memory-warning -d <udid>
serve-sim memory-warning -d <udid>
Multiple simulators at once
同时启动多个模拟器
serve-sim "iPhone 16 Pro" "iPad Pro"
serve-sim "iPhone 16 Pro" "iPad Pro"
Send a touch gesture (JSON format)
发送触摸手势(JSON格式)
serve-sim gesture '{"type":"tap","x":200,"y":400}' -d <udid>
undefinedserve-sim gesture '{"type":"tap","x":200,"y":400}' -d <udid>
undefinedFeatures
功能特性
- 60 FPS MJPEG video stream in browser
- Touch, swipe, pinch (hold Option key) gestures
- Keyboard input and hotkeys forwarded to simulator (CMD+SHIFT+H = home)
- Simulator logs forwarded to browser
- Drag and drop images/videos onto simulator
- Apple Watch, iPad, and iOS support
- Connect-style middleware for embedding in existing dev servers
- 浏览器中60 FPS MJPEG视频流
- 支持点击、滑动、捏合(按住Option键)手势
- 键盘输入和快捷键转发到模拟器(CMD+SHIFT+H = 返回主页)
- 模拟器日志转发到浏览器
- 支持向模拟器拖拽图片/视频
- 支持Apple Watch、iPad和iOS设备
- 支持Connect风格中间件,可嵌入现有开发服务器
Architecture
架构
┌──────────────┐ simctl io ┌─────────────────┐ MJPEG / WS ┌─────────┐
│ iOS Simulator│ ────────────► │ serve-sim-bin │ ───────────► │ Browser │
└──────────────┘ (Swift) │ (per-device) │ └─────────┘
└─────────────────┘
▲
state file in
$TMPDIR/serve-sim/
▲
┌──────────────────┐
│ serve-sim CLI / │
│ middleware │
└──────────────────┘State files are written to . The Swift binary is bundled in the npm package — no separate Xcode build needed at runtime.
$TMPDIR/serve-sim/┌──────────────┐ simctl io ┌─────────────────┐ MJPEG / WS ┌─────────┐
│ iOS Simulator│ ────────────► │ serve-sim-bin │ ───────────► │ Browser │
└──────────────┘ (Swift) │ (per-device) │ └─────────┘
└─────────────────┘
▲
state file in
$TMPDIR/serve-sim/
▲
┌──────────────────┐
│ serve-sim CLI / │
│ middleware │
└──────────────────┘状态文件存储在中。Swift二进制文件已捆绑在npm包中——运行时无需单独进行Xcode构建。
$TMPDIR/serve-sim/Integration Patterns
集成模式
Claude Code Desktop
Claude Code Desktop
Create in your project root:
.claude/launch.jsonjson
{
"version": "0.0.1",
"configurations": [
{
"name": "ios",
"runtimeExecutable": "npx",
"runtimeArgs": ["serve-sim"],
"port": 3200
}
]
}Claude will automatically start the simulator preview when you open the project.
在项目根目录创建:
.claude/launch.jsonjson
{
"version": "0.0.1",
"configurations": [
{
"name": "ios",
"runtimeExecutable": "npx",
"runtimeArgs": ["serve-sim"],
"port": 3200
}
]
}当你打开项目时,Claude会自动启动模拟器预览。
Expo / Metro Dev Server
Expo / Metro开发服务器
Customize to embed serve-sim at :
metro.config.jshttp://localhost:8081/.simjs
// metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const connect = require("connect");
const { simMiddleware } = require("serve-sim/middleware");
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
config.server = config.server || {};
const originalEnhanceMiddleware = config.server.enhanceMiddleware;
config.server.enhanceMiddleware = (metroMiddleware, server) => {
const middleware = originalEnhanceMiddleware
? originalEnhanceMiddleware(metroMiddleware, server)
: metroMiddleware;
const app = connect();
app.use(simMiddleware({ basePath: "/.sim" }));
app.use(middleware);
return app;
};
module.exports = config;Then run — simulator preview available at .
npx expo starthttp://localhost:8081/.sim自定义,将serve-sim嵌入到:
metro.config.jshttp://localhost:8081/.simjs
// metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const connect = require("connect");
const { simMiddleware } = require("serve-sim/middleware");
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
config.server = config.server || {};
const originalEnhanceMiddleware = config.server.enhanceMiddleware;
config.server.enhanceMiddleware = (metroMiddleware, server) => {
const middleware = originalEnhanceMiddleware
? originalEnhanceMiddleware(metroMiddleware, server)
: metroMiddleware;
const app = connect();
app.use(simMiddleware({ basePath: "/.sim" }));
app.use(middleware);
return app;
};
module.exports = config;然后运行——模拟器预览可在访问。
npx expo starthttp://localhost:8081/.simExpress / Connect Dev Server
Express / Connect开发服务器
ts
import express from "express";
import { simMiddleware } from "serve-sim/middleware";
const app = express();
// First start the helper in daemon mode
// $ npx serve-sim --detach
app.use(simMiddleware({ basePath: "/.sim" }));
app.listen(3000, () => {
console.log("Dev server at http://localhost:3000");
console.log("Simulator preview at http://localhost:3000/.sim");
});ts
import express from "express";
import { simMiddleware } from "serve-sim/middleware";
const app = express();
// 首先以守护进程模式启动辅助程序
// $ npx serve-sim --detach
app.use(simMiddleware({ basePath: "/.sim" }));
app.listen(3000, () => {
console.log("开发服务器地址:http://localhost:3000");
console.log("模拟器预览地址:http://localhost:3000/.sim");
});Vite Dev Server
Vite开发服务器
ts
// vite.config.ts
import { defineConfig } from "vite";
import { simMiddleware } from "serve-sim/middleware";
export default defineConfig({
server: {
middlewareMode: false,
},
plugins: [
{
name: "serve-sim",
configureServer(server) {
// Start helper first: npx serve-sim --detach
server.middlewares.use("/.sim", simMiddleware({ basePath: "/.sim" }));
},
},
],
});ts
// vite.config.ts
import { defineConfig } from "vite";
import { simMiddleware } from "serve-sim/middleware";
export default defineConfig({
server: {
middlewareMode: false,
},
plugins: [
{
name: "serve-sim",
configureServer(server) {
// 先启动辅助程序:npx serve-sim --detach
server.middlewares.use("/.sim", simMiddleware({ basePath: "/.sim" }));
},
},
],
});Next.js Custom Server
Next.js自定义服务器
ts
// server.ts
import { createServer } from "http";
import { parse } from "url";
import next from "next";
import { simMiddleware } from "serve-sim/middleware";
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const simHandler = simMiddleware({ basePath: "/.sim" });
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url!, true);
if (parsedUrl.pathname?.startsWith("/.sim")) {
return simHandler(req, res, () => handle(req, res, parsedUrl));
}
handle(req, res, parsedUrl);
}).listen(3000);
});ts
// server.ts
import { createServer } from "http";
import { parse } from "url";
import next from "next";
import { simMiddleware } from "serve-sim/middleware";
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const simHandler = simMiddleware({ basePath: "/.sim" });
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url!, true);
if (parsedUrl.pathname?.startsWith("/.sim")) {
return simHandler(req, res, () => handle(req, res, parsedUrl));
}
handle(req, res, parsedUrl);
}).listen(3000);
});Middleware API
中间件API
ts
import { simMiddleware } from "serve-sim/middleware";
// Mount options
simMiddleware({
basePath: "/.sim", // URL prefix for all serve-sim routes
});
// Middleware exposes:
// GET /.sim → Preview HTML UI
// GET /.sim/api → State JSON (connected devices, stream URLs)
// GET /.sim/logs → SSE log stream from simulatorThe middleware reads state from and proxies the browser to the live MJPEG + WebSocket endpoints. CORS is open on the helper, so no additional proxy config is needed.
$TMPDIR/serve-sim/ts
import { simMiddleware } from "serve-sim/middleware";
// 挂载选项
simMiddleware({
basePath: "/.sim", // 所有serve-sim路由的URL前缀
});
// 中间件提供以下接口:
// GET /.sim → 预览HTML UI
// GET /.sim/api → 状态JSON(已连接设备、流URL)
// GET /.sim/logs → 来自模拟器的SSE日志流中间件从读取状态,并将浏览器代理到实时MJPEG + WebSocket端点。辅助程序已开启CORS,因此无需额外的代理配置。
$TMPDIR/serve-sim/Daemon / Detach Mode
守护进程/分离模式
Use to start the helper as a background process and get JSON output for scripting:
--detachsh
undefined使用将辅助程序作为后台进程启动,并获取JSON输出用于脚本编写:
--detachsh
undefinedStart daemon and capture JSON output
启动守护进程并捕获JSON输出
STREAM_INFO=$(npx serve-sim --detach --quiet)
echo $STREAM_INFO
STREAM_INFO=$(npx serve-sim --detach --quiet)
echo $STREAM_INFO
{"udid":"...","mjpeg":"http://localhost:3100/stream","ws":"ws://localhost:3100/control"}
{"udid":"...","mjpeg":"http://localhost:3100/stream","ws":"ws://localhost:3100/control"}
List running streams as JSON
以JSON格式列出正在运行的流
npx serve-sim --list --quiet
npx serve-sim --list --quiet
Kill specific device stream
终止特定设备的流
npx serve-sim --kill "iPhone 16 Pro"
npx serve-sim --kill "iPhone 16 Pro"
Kill all streams
终止所有流
npx serve-sim --kill
undefinednpx serve-sim --kill
undefinedProgrammatic Usage (TypeScript)
程序化使用(TypeScript)
ts
import { simMiddleware } from "serve-sim/middleware";
import connect from "connect";
const app = connect();
// Attach middleware — requires serve-sim helper already running (--detach)
app.use(
simMiddleware({
basePath: "/.sim",
})
);
export default app;ts
import { simMiddleware } from "serve-sim/middleware";
import connect from "connect";
const app = connect();
// 挂载中间件——要求serve-sim辅助程序已启动(--detach模式)
app.use(
simMiddleware({
basePath: "/.sim",
})
);
export default app;Gesture JSON Format
手势JSON格式
When using :
serve-sim gesture '<json>'sh
undefined使用时:
serve-sim gesture '<json>'sh
undefinedTap at coordinates
在指定坐标点击
serve-sim gesture '{"type":"tap","x":200,"y":400}' -d <udid>
serve-sim gesture '{"type":"tap","x":200,"y":400}' -d <udid>
Swipe gesture
滑动手势
serve-sim gesture '{"type":"swipe","startX":200,"startY":800,"endX":200,"endY":200,"duration":0.3}' -d <udid>
undefinedserve-sim gesture '{"type":"swipe","startX":200,"startY":800,"endX":200,"endY":200,"duration":0.3}' -d <udid>
undefinedDevelopment Setup
开发环境设置
sh
git clone https://github.com/EvanBacon/serve-sim
cd serve-sim
bun installsh
git clone https://github.com/EvanBacon/serve-sim
cd serve-sim
bun installBuild JS bundles
构建JS包
bun run --filter serve-sim build
bun run --filter serve-sim build
Rebuild Swift helper binary
重新构建Swift辅助程序二进制文件
bun run --filter serve-sim build:swift
bun run --filter serve-sim build:swift
Watch mode for development
开发模式下的监听模式
bun run --filter serve-sim dev
undefinedbun run --filter serve-sim dev
undefinedTroubleshooting
故障排除
No simulator detected
sh
undefined未检测到模拟器
sh
undefinedCheck booted simulators
检查已启动的模拟器
xcrun simctl list devices booted
xcrun simctl list devices booted
Boot a simulator if none running
如果没有运行的模拟器,启动一个
xcrun simctl boot "iPhone 16 Pro"
**Port already in use**
```shxcrun simctl boot "iPhone 16 Pro"
**端口已被占用**
```shUse a different port
使用其他端口
serve-sim -p 4200
serve-sim -p 4200
Kill existing helpers first
先终止现有辅助程序
serve-sim --kill
**Stream not appearing in middleware**
```shserve-sim --kill
**中间件中未显示流**
```shEnsure helper is running first
确保辅助程序已启动
serve-sim --detach
serve-sim --detach
Verify state files exist
验证状态文件是否存在
ls $TMPDIR/serve-sim/
ls $TMPDIR/serve-sim/
Check running streams
检查正在运行的流
serve-sim --list
**Swift binary not found / won't execute**
```shserve-sim --list
**Swift二进制文件未找到/无法执行**
```shEnsure Xcode CLT are installed
确保已安装Xcode命令行工具
xcode-select --install
xcode-select --install
Verify simctl works
验证simctl是否可用
xcrun simctl list
**Multiple simulators — wrong device targeted**
```shxcrun simctl list
**多个模拟器——目标设备错误**
```shList all booted simulators with UDIDs
列出所有已启动模拟器的UDID
xcrun simctl list devices booted
xcrun simctl list devices booted
Target by UDID explicitly
明确通过UDID指定目标
serve-sim -d <udid>
**Rebuild Swift helper if binary is stale**
```sh
bun run --filter serve-sim build:swiftserve-sim -d <udid>
**如果二进制文件过时,重新构建Swift辅助程序**
```sh
bun run --filter serve-sim build:swift