一个纯浏览器的 iOS 真机远程镜像 / 控制 / 运维平台。无需安装任何客户端,即可在 Windows / macOS / Linux 上运行。已适配 iOS 26


一、项目概述

WebAppFlaskauto-iOS 是一款基于 Python + Flask + Flask-SocketIO 后端、Vue 3 + Vite 前端的 Web 端 iOS 投屏互动控制平台。启动浏览器即可使用:

  • 发现已连接到本机的 iPhone 真机
  • 实时镜像设备屏幕(MJPEG 主路 + 可选 WebRTC 低延迟)
  • 远程触控 / 滑动 / 文本输入 / 截图
  • 浏览设备文件、安装卸载应用、运行 UI 自动化定位
  • 查看实时系统日志、设备硬件信息

底层链路:

  • pymobiledevice3(纯 Python)负责设备发现、usbmux 端口转发、WDA HTTP 通信
  • WebDriverAgent (WDA) 负责实际的控制指令下发
  • go-ios(可选,仅 iOS 17+)走用户态 RSD 隧道拉起 WDA,免管理员权限

✨ 重点适配:iOS 17+ 起 RSD 隧道在 Windows 上通需常要管理员权限,本项目通过内置的 go-ios 用户态隧道彻底绕开;已在 iOS 26.5 真机验证可用

单设备界面


二、使用指南

1、环境准备

组件 版本要求
Python 3.10 及以上
Node.js 18 及以上(构建前端用)
iTunes / Apple Mobile Device Support Windows 用户必装(提供 usbmux 驱动)
浏览器 Chrome / Edge / Safari 最新版

iOS 设备侧:

  • iOS 17+ 需要在「设置 → 隐私与安全 → 开发者模式」打开开发者模式
  • 首次插线信任本机

2、安装启动

# 1. 克隆仓库
git clone https://github.com/zhoujun94511/WebAppFlaskauto-iOS.git
cd WebAppFlaskauto-iOS

# 2. 创建 Python 虚拟环境
python -m venv .venv
.venv\Scripts\activate     # Windows
# source .venv/bin/activate  # macOS / Linux

# 3. 安装依赖
pip install -r requirements.txt

# 4. 复制环境变量模板(默认值即可启动)
cp .env.example .env

# 5. 安装并构建前端
cd frontend && npm install && npm run build && cd ..

# 6. 一键启动(自动拉起后端 :5001 + Vite 开发服务 :5173)
python start_dev.py

打开浏览器访问 http://localhost:5001/ 即可。默认超级管理员账号

  • 用户名:superadmin
  • 密码:superadmin123

3、连接 iPhone 并开始操作

  1. 用数据线插上 iPhone,在 iOS 端弹窗中信任本机
  2. 登录后,左侧导航点「设备」即可看到列表
  3. 点击某台设备的「连接」按钮,等待约 10-20 秒(首次启动 WDA 较慢)
  4. 连接成功后切到「单屏」模式,屏幕画面即时呈现,光环代表"在线 streaming"
  5. 鼠标点击 / 拖动 = 触控 / 滑动;右侧面板可切换控制 / 应用 / 文件 / 日志 / 自动化

登录界面


三、功能特性

多设备界面

1、远程镜像与控制

  • MJPEG 主路 + 截图轮询兜底:跨平台、稳定可靠
  • 可选 WebRTC(aiortc):H264 优先、VP8 兜底,码率可调,宿主侧 libx264 软编
  • 触控全覆盖:tap / swipe / long-press / double-tap / 文本输入 / 截图
  • 系统按键:Home / 锁屏 / 音量 ± / 方向 D-pad(合成 WDA swipe)
  • 无障碍快捷开关:辅助触控、旁白(VoiceOver)、缩放(经 go-ios)

2、单设备视图 / 多设备网格双模式

  • 单设备模式:左侧设备信息卡 + 中央真机投屏 + 右侧可弹出的控制抽屉(控制 / 自动化 / 应用 / 文件 / 日志)
  • 多设备网格:自动按可用宽度排版,每台设备进网格时自动降到 70% 分辨率减负
  • 设备 streaming 中卡片自带双层蓝色光环(外层卡片描边 + 内层屏幕区域 pulsing 双线),视觉与 Android 端一致

3、设备占用(预约)机制

真机稀缺,多人争抢是日常痛点。本项目内置完整的预约系统:

  • 任意普通用户可对未被占用的设备发起占用,自选时长(默认 30 分钟)
  • 占用期间仅占用者可控制;其它用户只读可看不可点
  • 三种释放路径统一:本人取消 / 管理员强制 / 到期清扫线程自动回收
  • 数据库 device_id 主键 + UNIQUE 约束解决并发抢占(败者得 IntegrityError 而非半成功)

4、UI 自动化定位

  • 支持四种定位方式:accessibility id / predicate / xpath / class / name
  • 内置 find / tap / type 三类动作
  • 当前前台应用实时显示(bundleId + 进程名)
  • 页面源码(XML)格式化展示 — 一键拉取整页 XML,自动缩进、可折叠

5、文件浏览器

  • 可展开 / 收起的目录树
  • 点击文件直接下载到本机
  • 图片 / 音视频 / PDF / 文本类支持应用内预览(弹层)
  • 拖拽或选取文件即可上传到设备目录(目标目录与当前展开节点联动)

6、应用管理

  • 列举已安装应用(含 BundleId、版本、显示名)
  • 启动 / 结束应用
  • 安装 .ipa 包(拖拽到面板或文件选择)
  • 卸载应用(带二次确认)

7、账号体系与权限

三级角色:

角色 权限
超级管理员(super_admin) 全权限;管理所有用户的角色、邮箱、密码、启停状态
管理员(admin) 管理普通用户、强制释放设备占用
普通用户(user) 自助注册、自助占用 / 释放设备、控制已占用的设备
  • 服务端会话(cookie + 内存)+ 登录闸门:未登录访问任何 /api/* 都返回 401
  • 登录失败限流:连续失败 5 次 60 秒锁定
  • 管理面板(仅管理员可见):建号 / 改邮箱 / 重置密码 / 改角色 / 启停 / 删除

管理员界面

8、实时系统日志

  • 后端用 go-ios syslog 拉设备实时日志,通过 SSE 推到前端
  • 客户端支持正则 / 进程 / 级别三重过滤
  • 手动启停按钮 —— 不看就停,避免空闲时长连接占用资源

9、国际化与多主题

  • 三语:zh-CN 简体中文 / zh-TW 繁體中文 / en English
  • 切换语言时自动同步<html lang> + document.title(浏览器标签页)+ 所有界面文案
  • 浅 / 深双主题 + 自动跟随系统

10、环境需求与常见问题

问题 排查方向
设备列表为空 检查 iTunes 是否装、数据线是否原装、设备是否信任本机
连接超时 / WDA 起不来 开发者模式有没开;首次启动 WDA 耗时较长(10-20 秒)
Windows 报 wintun.dll 缺失 本仓库 resources/wintun/<arch>/ 已内置;go-ios 会自动加载
WebRTC 看不到画面 检查 .envIOS_ENABLE_WEBRTC=1;浏览器需要支持 H264
多人同时连接断流 看是否两个用户都拿了占用 — 设备占用机制限定同时仅一人可控

四、项目框架代码

1、项目框架

WebAppFlaskauto-iOS/
├── app.py                      # Flask + Socket.IO 入口;蓝图注册、登录闸门、SPA 托管
├── start_dev.py                # 跨平台一键启动器(后端 5001 + Vite 5173)
├── requirements.txt
│
├── api/                        # HTTP / Socket.IO 路由(薄适配层)
│   ├── auth_api.py             #   登录 / 注册 / 会话 / 用户管理
│   ├── reservations_api.py     #   设备占用 REST
│   ├── devices_api.py          #   设备列表 / 连接 / 断开 / WDA 状态
│   ├── control_api.py          #   tap / swipe / input / 截图 / 启动 / 弹窗 / 剪贴板
│   ├── stream_api.py           #   画面流启停 / 质量 / 分辨率
│   ├── files_api.py            #   文件树 / 下载 / 上传 / 内联预览
│   ├── apps_api.py             #   应用列举 / 安装 / 卸载
│   └── automation_api.py       #   WDA 选择器 find/tap/type
│
├── services/                   # 业务逻辑层
│   ├── app_db.py               #   SQLite 连接、建表、预置账号
│   ├── auth_service.py         #   口令哈希、会话令牌、角色
│   ├── reservation_service.py  #   设备占用:claim / release / sweep
│   ├── device_info.py          #   lockdown 信息聚合
│   ├── stream_bridge.py        #   画面帧 → Socket.IO
│   └── webrtc_bridge.py        #   aiortc 管线(JpegVideoTrack + 码率调优)
│
├── ios/                        # iOS 平台适配
│   ├── ios_adapter.py          #   统一门面(发现 / 连接 / 控制 / 流)
│   ├── go_ios.py               #   go-ios 封装(隧道 / runwda / fsync / apps)
│   ├── tunnel_manager.py       #   用户态 RSD 隧道生命周期
│   ├── port_forward.py         #   usbmux 端口转发
│   ├── device_marketing.py     #   ProductType → 营销名映射(iPhone 15 Pro Max 等)
│   └── screen_provider/        #   wda_mjpeg / wda_screenshot 插件
│
├── frontend/                   # Vue 3 + Vite 前端
│   └── src/
│       ├── components/         #   LoginView / DeviceStage / DeviceMatrix / ...
│       ├── composables/        #   useAuth / useDevices / useWebRTC / useUiI18n ...
│       └── locales/            #   zh-CN / zh-TW / en 三语 JSON
│
├── resources/
│   ├── executable/<os>/        #   内置 go-ios 二进制
│   └── wintun/<arch>/          #   Windows 隧道所需 wintun.dll
│
└── tests/                      # pytest 白盒测试

2、核心代码片段

(1)跨平台一键启动器
# start_dev.py(简化版)
import os, sys, signal, subprocess

def start_backend():
    return subprocess.Popen(
        [sys.executable, "app.py"],
        env={**os.environ, "PORT": "5001"},
    )

def start_frontend():
    return subprocess.Popen(
        ["npm", "run", "dev"],
        cwd="frontend",
        shell=(os.name == "nt"),
    )

if __name__ == "__main__":
    backend = start_backend()
    frontend = start_frontend()
    print("[start_dev] Ctrl+C to stop both.")
    try:
        backend.wait()
    finally:
        frontend.terminate()
(2)IOSAdapter:连接 / 断开核心流程
# ios/ios_adapter.py(节选)
class IOSAdapter:
    def connect(self, udid: str) -> None:
        # 1) 起用户态隧道(iOS 17+ 才需要,免管理员)
        self.tunnel_manager.ensure(udid)
        # 2) 通过 go-ios runwda 拉起 WebDriverAgent
        self.wda_launcher.start(udid)
        # 3) usbmux 端口转发到 WDA HTTP(8100)和 MJPEG(9100)
        self.port_forward.start(udid, 18100, 8100)
        self.port_forward.start(f"{udid}#mjpeg", 19100, 9100)
        # 4) HTTP probe,确认 WDA 起来了
        self.wda_controller.wait_ready(udid)

    def disconnect(self, udid: str) -> None:
        # 关 WebRTC peer 在前,撕端口转发在后 —— 避免读线程拿到 ECONNRESET
        with suppress(Exception):
            self.webrtc.stop_device(udid)
        self.stop_stream(udid)
        # 关 WDA 进程 + 控制器
        self.wda_launcher.stop(udid)
        # 最后撕端口转发
        self.port_forward.stop_forward(udid)
        self.port_forward.stop_forward(f"{udid}#mjpeg")
(3)ProductType 友好名映射(节选)
# ios/device_marketing.py(节选)
MARKETING_NAMES: dict[str, str] = {
    # iPhone 17 series (2025)
    "iPhone18,1": "iPhone 17 Pro",
    "iPhone18,2": "iPhone 17 Pro Max",
    "iPhone18,3": "iPhone 17",
    "iPhone18,4": "iPhone Air",
    "iPhone18,5": "iPhone 17e",

    # iPhone 16 series (2024)
    "iPhone17,1": "iPhone 16 Pro",
    "iPhone17,2": "iPhone 16 Pro Max",
    # ... 一直覆盖到 2007 年原代 iPhone(63 条)

    "iPhone1,1": "iPhone",
}

def marketing_name(product_type: str) -> str:
    """ProductType → 营销名;未知则原样返回 identifier。"""
    return MARKETING_NAMES.get(product_type, product_type)
(4)项目架构时序图
浏览器 (Vue3 SPA)
   │  HTTP /api/*         Socket.IO(设备/画面/控制/日志)   WebRTC(可选, 视频+控制 DataChannel)
   ▼                            ▼                              ▼
Flask + Flask-SocketIO (async_mode="threading")
   │  api/  →  services/  →  ios/ios_adapter.py
   ▼
IOSAdapter
 ├─ DeviceManager     pymobiledevice3:设备发现 / lockdown 信息              ┐ Flask 原生主路径
 ├─ PortForward       pymobiledevice3 usbmux:本地端口 → WDA 8100/9100      │ (无需隧道)
 ├─ WDAController     WebDriverAgent HTTP:tap/swipe/text/截图/应用/弹窗     ┘
 ├─ GoIOS + Tunnel    go-ios:用户态 RSD 隧道(免管理员) + `runwda`         ← iOS 17+ 拉起
 ├─ ScreenProvider    wda_mjpeg(主) / wda_screenshot(兜底)
 └─ StreamBridge      画面帧 → Socket.IO room=udid
                      WebRTCBridge → aiortc(JpegVideoTrack, 宿主侧 libx264)
   ▼
SQLite (账号 / 会话 / 设备占用)

五、结语

本项目的初衷是把 iOS 真机变成可调度、可审计、零客户端的共享资源。和市面常见的 Appium / tidevice 方案相比,主要区别在:

  1. 直接用 HTTP 与 WDA 通信 —— 不装 Appium Server,启动链路短;
  2. pymobiledevice3 + go-ios 双引擎互补 —— iOS 17+ 免管理员模式可真实运行;
  3. 业务层完备 —— 账号、占用、审计、面板一体,开箱即用;

后续规划:

  • 流媒体输出适配
  • 与 Android 姊妹项目 WebAppFlaskscrcpy 合并为统一跨平台运维台

如果觉得有用欢迎 Star ⭐:https://github.com/zhoujun94511/WebAppFlaskauto-iOS

有问题、想法或合作意向,欢迎 Issue 或评论区交流。

更多推荐