SimBeam: Удалённое управление iOS-симулятором с айфона, айпада или из браузера

С начала года моя работа сильно поменялась — как, наверное, и у многих из вас. Я самостоятельно написал от силы 1000 строк кода, всё остальное делали агенты под моим чутким руководством. В какой-то момент я плотно подсел на эту иглу и одновременно работал над 3–4 проектами. Чуть не выгорел и задумался: чё вообще за хуйня? Нам вроде обещали будущее, где роботы работают за нас, а в итоге мы просто работаем больше.

Я решил сбавить обороты, не работать после работы, но FOMO, сука, уже никогда не даст вернуться в прошлый режим. Я вроде хочу гулять больше, но останавливаться полностью не хочу. Так я начал давать задания агентам удалённо, пока гуляю с собакой, сопровождаю жену на шоппинге или пью пиво с друзьями. Но я столкнулся с проблемой проверки результата, которая вообще не проблема для веб-разработчиков, но конкретная для iOS.

TL;DR

Я сделал Dev Tool, который позволяет удалённо подключаться к iOS-симулятору и управлять им. Подключиться можно как с айфона или айпада, так и через браузер.

🚨

Если не терпится попробовать:

📱 iOS-приложение🛰 Лендинг📦 Исходники

Установить демон: brew install --cask kei-sidorov/simbeam/simbeamd


Вот так выглядит мой походный сетап: айпад, терминал с агентом и живой симулятор с мака.

Чуть подробнее о проблеме

Представьте: вы сидите в очереди на очередную бюрократическую процедуру. Можно листать мемы, а можно повайбкодить (== дать команду агенту и дальше смотреть мемы). Не всегда есть возможность присесть с ноутбуком, и я полюбил делать это с телефона. Сидишь, раздаёшь команды агентам, разглядываешь diff — как по мне, намного интереснее простого залипания в ленту!

И если вы веб-разработчик, то у вас всё шикарно: агент закодил, быстренько пересобрал — и вот уже через браузер можно увидеть результат. Но если вы iOS-разработчик, результат вы увидите не скоро: проект, помимо сборки, нужно подписать, загрузить в TestFlight и дождаться, пока Apple его запроцессит, а потом ещё установить. Это десяток минут в лучшем случае. Никуда не годится! Хочется коротких циклов.

Альтернативные пути решения

Конечно, я попробовал срезать углы и не писать ничего сам. Самое очевидное — Remote Desktop для мака. Получилось очень медленно, нафиг не нужен целый экран, с телефона жутко неудобно.

После я нашёл проект ios-bridge, который мог стримить симулятор в браузер. Вот это уже было близко к тому, что я хочу. С горем пополам, через пару вечеров, у меня получилось заставить это работать и даже адаптировать под телефон. Но всё равно не хватало той лёгкости сетапа, которой хотелось. Я решил писать своё.

Источники вдохновения

Дело начиналось в январе, тогда ни у Клода, ни у Кодекса не было нормальных удалённых клиентов. Но был (и есть!) Happy!

Это модуль для агентов, который даёт доступ к чатам с агентами, с удобным интерфейсом, пушами и голосовым вводом. Всё, что требовалось, — установить npm-пакет и запустить сервер. Далее в консоли появлялся QR-код, который считываешь телефоном, и пейринг завершён: у вас есть стабильное соединение через их relay-сервер с end-to-end-шифрованием. Вот чего-то такого мне и захотелось.

К чему в итоге пришёл

Я поковырял проект ios-bridge, понял, что он использует фейсбуковский idb — попытку создать что-то похожее на ADB для Android — для управления симулятором и получения оттуда кадров. Подумал, что переиспользую (спойлер: в итоге выкинул и написал своё, об этом ниже).

Я принялся проектировать проект и пришёл к варианту с WebRTC. Я поднимаю signaling-сервер, который служит транспортом между маком и клиентом: один другому дозванивается, команды идут по одному каналу, видео обратно — по второму. Выглядит шикарно, работает вроде тоже неплохо :)

Ещё из технических решений: Go для демона и signaling-сервера, нативное приложение на Swift для iOS/iPadOS. Заодно решил сделать веб-клиент для удалённого доступа с десктопа или Android-сетапа.

Как это работает под капотом

Внутри три части: демон simbeamd на Go, который живёт на маке, лёгкий signaling-брокер simbeam-signal и клиенты — нативный на Swift и веб. Демон держит одно исходящее WSS-соединение с брокером, так что на маке не открыт ни один порт. Когда клиент хочет подключиться, брокер просто сводит стороны: пересылает ровно один SDP-handshake и дальше не участвует. Видео идёт по медиаканалу WebRTC, а тапы, свайпы и клавиатура — обратно по DataChannel, напрямую между устройствами. Всё это end-to-end зашифровано (DTLS-SRTP), даже когда трафик идёт через TURN-релей. При пейринге обе стороны пинят Ed25519-ключи друг друга, поэтому брокер — даже скомпрометированный — может максимум помешать соединению, но не подслушать и не притвориться вашим маком.

Самое интересное — захват экрана. С idb я быстро упёрся в потолок: его companion выдаёт H.264 с фиксированным GOP примерно в 10 секунд и не даёт управлять кейфреймами — при резкой смене картинки экран на несколько секунд рассыпается в артефакты. Да и последний релиз от Меты датирован 2022-м. В итоге написал свой маленький хелпер simbeam-control: он цепляется к фреймбуферу симулятора напрямую через приватные API CoreSimulator (IOSurface), кодирует аппаратно через VideoToolbox и сам решает, когда вставлять кейфреймы — примерно раз в секунду. Никакого ffmpeg и перекодирования: кадры из фреймбуфера сразу уходят в WebRTC-трек. Демон спавнит по такому хелперу на стрим и читает из него готовый H.264, а жизненный цикл симуляторов (список, загрузка, скриншоты в полном разрешении) — через обычный xcrun simctl.

Как это выглядит

Весь сетап — две команды:


brew install --cask kei-sidorov/simbeam/simbeamd
simbeamd serve

Дальше жмёшь P в терминале демона — открывается одноразовое окно пейринга с QR-кодом и ссылкой:

Сканируешь QR телефоном (или открываешь ссылку в браузере), подтверждаешь «Pair this Mac» — и всё, симулятор у тебя в руке. Клиент запоминает мак по публичному ключу, так что в следующий раз никакого QR — просто открываешь приложение и подключаешься:


☝🏻

На маке нужен полный Xcode (не только Command Line Tools) — simbeam-control ходит в приватные API CoreSimulator, да и симуляторы сами по себе без Xcode не живут.


Ключевые решения

  • Я старался сделать это настолько секьюрно, насколько возможно, поэтому выбрал WebRTC-стек: там из коробки есть шифрование и не нужно писать ничего своего. Две стороны обмениваются ключами через signaling, и далее канал зашифрованный.
  • Я старался сделать интро рабочим на старых ОС, поэтому iOS 16+ и macOS 14.0 / Xcode 15. Я целился в немолодые айпады — терминал в Claude Code + стриминг симулятора могут дать вторую жизнь некоторым планшетам.
  • Распространять решил через Homebrew. Во-первых, видно, из какого кода это собрано. Во-вторых, я использую приватные фреймворки, и в Mac App Store меня бы наверняка не пустили. А так как это тул для разработчиков, брю не должен быть проблемой.

Буду рад фидбеку и звезде на GitHub, если вам было полезно.

simbeam.dev

Share
Send
Pin
4 h  
Popular