serve-sim-apple-simulator

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

serve-sim — Apple Simulator Hosting Tool

serve-sim — Apple模拟器托管工具

Skill by ara.so — Daily 2026 Skills collection.
serve-sim
is the
npx serve
of Apple Simulators. It spawns a Swift helper that captures a booted iOS Simulator's framebuffer via
simctl io
, exposes it as an MJPEG stream + WebSocket control channel, and serves a React preview UI in your browser. Works with any booted iOS Simulator — no Xcode plugin, no app instrumentation required.
ara.so提供的技能——Daily 2026技能合集。
serve-sim
是Apple模拟器版的
npx serve
。它会启动一个Swift辅助程序,通过
simctl io
捕获已启动的iOS模拟器的帧缓冲区,将其作为MJPEG流+WebSocket控制通道暴露出来,并在浏览器中提供React预览UI。适用于任何已启动的iOS模拟器——无需Xcode插件,无需应用插桩。

Requirements

要求

  • macOS with Xcode command line tools installed (
    xcrun simctl
    must be available)
  • At least one booted iOS Simulator
  • Node.js / npm (for
    npx
    )
  • 安装了Xcode命令行工具的macOS系统(必须能使用
    xcrun simctl
  • 至少有一个已启动的iOS模拟器
  • Node.js / npm(用于
    npx

Quick Start

快速开始

sh
undefined
sh
undefined

Start preview server (auto-detects booted simulator)

启动预览服务器(自动检测已启动的模拟器)

npx serve-sim
npx serve-sim

→ Preview at http://localhost:3200

→ 预览地址:http://localhost:3200

undefined
undefined

CLI 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 warning
sh
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] 终止正在运行的流
undefined

Common CLI Examples

常用CLI示例

sh
undefined
sh
undefined

Target 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>
undefined
serve-sim gesture '{"type":"tap","x":200,"y":400}' -d <udid>
undefined

Features

功能特性

  • 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
$TMPDIR/serve-sim/
. The Swift binary is bundled in the npm package — no separate Xcode build needed at runtime.
┌──────────────┐   simctl io   ┌─────────────────┐  MJPEG / WS  ┌─────────┐
│ iOS Simulator│ ────────────► │ serve-sim-bin   │ ───────────► │ Browser │
└──────────────┘   (Swift)     │ (per-device)    │              └─────────┘
                               └─────────────────┘
                                  state file in
                                $TMPDIR/serve-sim/
                               ┌──────────────────┐
                               │ serve-sim CLI /  │
                               │ middleware       │
                               └──────────────────┘
状态文件存储在
$TMPDIR/serve-sim/
中。Swift二进制文件已捆绑在npm包中——运行时无需单独进行Xcode构建。

Integration Patterns

集成模式

Claude Code Desktop

Claude Code Desktop

Create
.claude/launch.json
in your project root:
json
{
  "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.json
json
{
  "version": "0.0.1",
  "configurations": [
    {
      "name": "ios",
      "runtimeExecutable": "npx",
      "runtimeArgs": ["serve-sim"],
      "port": 3200
    }
  ]
}
当你打开项目时,Claude会自动启动模拟器预览。

Expo / Metro Dev Server

Expo / Metro开发服务器

Customize
metro.config.js
to embed serve-sim at
http://localhost:8081/.sim
:
js
// 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
npx expo start
— simulator preview available at
http://localhost:8081/.sim
.
自定义
metro.config.js
,将serve-sim嵌入到
http://localhost:8081/.sim
js
// 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 start
——模拟器预览可在
http://localhost:8081/.sim
访问。

Express / 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 simulator
The middleware reads state from
$TMPDIR/serve-sim/
and proxies the browser to the live MJPEG + WebSocket endpoints. CORS is open on the helper, so no additional proxy config is needed.
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日志流
中间件从
$TMPDIR/serve-sim/
读取状态,并将浏览器代理到实时MJPEG + WebSocket端点。辅助程序已开启CORS,因此无需额外的代理配置。

Daemon / Detach Mode

守护进程/分离模式

Use
--detach
to start the helper as a background process and get JSON output for scripting:
sh
undefined
使用
--detach
将辅助程序作为后台进程启动,并获取JSON输出用于脚本编写:
sh
undefined

Start 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

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
undefined
npx serve-sim --kill
undefined

Programmatic 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
undefined

Tap 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>
undefined
serve-sim gesture '{"type":"swipe","startX":200,"startY":800,"endX":200,"endY":200,"duration":0.3}' -d <udid>
undefined

Development Setup

开发环境设置

sh
git clone https://github.com/EvanBacon/serve-sim
cd serve-sim
bun install
sh
git clone https://github.com/EvanBacon/serve-sim
cd serve-sim
bun install

Build 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
undefined
bun run --filter serve-sim dev
undefined

Troubleshooting

故障排除

No simulator detected
sh
undefined
未检测到模拟器
sh
undefined

Check 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**
```sh
xcrun simctl boot "iPhone 16 Pro"

**端口已被占用**
```sh

Use a different port

使用其他端口

serve-sim -p 4200
serve-sim -p 4200

Kill existing helpers first

先终止现有辅助程序

serve-sim --kill

**Stream not appearing in middleware**
```sh
serve-sim --kill

**中间件中未显示流**
```sh

Ensure 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**
```sh
serve-sim --list

**Swift二进制文件未找到/无法执行**
```sh

Ensure Xcode CLT are installed

确保已安装Xcode命令行工具

xcode-select --install
xcode-select --install

Verify simctl works

验证simctl是否可用

xcrun simctl list

**Multiple simulators — wrong device targeted**
```sh
xcrun simctl list

**多个模拟器——目标设备错误**
```sh

List 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:swift
serve-sim -d <udid>

**如果二进制文件过时,重新构建Swift辅助程序**
```sh
bun run --filter serve-sim build:swift