# MusicBridge iOS — план Обновлено: 2026-09-13. Пользователь разрешил начать нативное iOS-приложение. Репозиторий: `C:/Users/areso/iOS MusicBridge`; origin — dev.yukinoki.ru/musicbridge/ios. Windows-агент — соседний проект `C:/Users/areso/MusicBridge/MusicBridge.Agent`. ## Правила проекта - SwiftUI, без веб-клиента. Музыка играет на Windows. - Сверять контракт с Windows PROTOCOL.md; не менять серверный протокол молча. - Токены только в Keychain, проверка сертификата до сопряжения, запрет redirect. - Не повторять команды автоматически после потери ответа. - Не называть проверки Python сборкой Swift или запуском XCTest. - После этапа обновлять этот файл: реализация, проверки, ограничения, следующий шаг. ## Реализовано в исходниках - [x] Проект Xcode, shared scheme, iOS 17+, SwiftUI, без внешних зависимостей. - [x] Модели состояния и команд v1; валидация HTTPS IPv4 домашней сети и QR-строки. - [x] Ручное сопряжение, импорт приглашения, проверка SHA-256 DER-сертификата. - [x] Keychain: адрес, отпечаток и bearer-токен; удаление локального сопряжения. - [x] HTTPS-команды, обложка до 2 МиБ, поток WSS с ограничением размера сообщения. - [x] Экран трека, возможности кнопок, прогресс, перемотка, громкость, mute. - [x] Переподключение 1…16 секунд, watchdog потока, отключение в фоне, возврат. - [x] XCTest исходники: диапазоны адресов, отказ опасным URL, отпечатки, QR/срок/коды, JSON Windows, расчёт позиции, формат команд. - [x] GitHub Actions: сборка/тесты симулятора, логи, xcresult и артефакт приложения. ## Проверки и следующие шаги - [x] Первый успешный xcodebuild + XCTest на macOS: 7 тестов, 0 отказов. - [x] Подключён GitHub remote polskikh13/musicbridgeios; Actions и чтение логов через gh работают. - [x] Интеграционные тесты TLS: верный/чужой/истёкший сертификат, HTTPS и WSS. - [x] Пройдены интеграционные проверки redirect, 401, возврата из фона и загрузки обложки. - [ ] Проверить реальный Keychain после переустановки/блокировки и отзыв устройства на физическом iPhone. - [x] Установить на физический iPhone: пользователь подтвердил установку через iloader 2026-09-12. - [x] Первое подключение к настоящему Windows-агенту: пользователь подтвердил рабочий пилот на iPhone 2026-09-12. - [ ] Полная ручная проверка каждого действия, обложки и устойчивости в Яндекс Музыке и SoundCloud. - [x] Камера для QR, запрос доступа, сообщение при отказе, остановка камеры при закрытии/уходе в фон. - [x] Bonjour-поиск `_musicbridge._tcp`, список адресов ПК и восстановление адреса с сохранённым сертификатом. - [x] QR через камеру работает на физическом iPhone — подтверждено пользователем после установки 0.2.0. - [ ] Отдельно проверить оба входа (встроенный сканер и стандартная камера), отказ в доступе к камере, Bonjour и смену IP на физическом iPhone. - [x] Реализованы понятные ошибки сопряжения по кодам сервера, повтор загрузки обложки после 404 и ручной повтор. - [x] Иконка приложения и обновлённый интерфейс подключения/плеера. - [x] Просмотрены снимки 0.4.0: подключение, светлая/тёмная темы, iPhone SE и крупный текст. - [x] Пользователь подтвердил работу 0.4.0 на iPhone: «всё вроде работает». - [x] Локальная Live Activity и Dynamic Island: реализация отображения трека в 0.5.0. - [ ] Проверить Live Activity на экране блокировки и Dynamic Island после подписи через iloader. - [ ] App Intents: прямые действия из Live Activity и команды Siri. - [ ] APNs: отдельный проект обновлений при приостановленном приложении, если нужен постоянный актуальный трек в фоне. Ограничения: хранится один доверенный ПК; адрес при смене IP восстанавливается через Bonjour, если доступны объявления того же агента. Новый адрес сохраняется только после успешного HTTPS-запроса с прежним отпечатком и токеном. Нет фонового воспроизведения, App Store/TestFlight и подписанной IPA. Потеря ответа на успешное сопряжение может оставить устройство на ПК: удалить его в окне агента перед повтором. Облачный runner не имеет доступа к домашней LAN. ## Проверка на iPhone 2026-09-12 - Windows GUI запущен с `--tray --lan`, порт TCP 8765 слушает. - На iPhone первая попытка сопряжения завершилась ошибкой недействительного сертификата сервера. Установка приложения уже работает; это отдельная TLS-проблема. - Ошибка воспроизведена настоящим HTTPS-запросом в iOS Simulator: старый код возвращает NSURLErrorDomain -1202 даже с верным отпечатком. Одни ATS-исключения её не устранили. Исправление: обрабатывать server trust также в `URLSessionTaskDelegate.didReceive`, направляя его в общую проверку отпечатка и срока сертификата. После этого HTTPS и WSS проходят. - Подготовлены ATS-исключения только для RFC1918 CIDR: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`. Начиная с iOS 17, для IP нужны явные исключения. Клиент продолжает требовать HTTPS, точный SHA-256 отпечаток, действующий сертификат и запрещает redirect. Глобального отключения ATS нет. - Добавлены настоящие HTTPS/WSS проверки в симуляторе с сертификатом того же типа, что у агента: RSA 2048, самоподписанный, CA:FALSE, SAN localhost/127.0.0.1, срок пять лет. Отдельно проверяются чужой отпечаток и истёкший сертификат. `tools/tls_fixture.py` запускается в CI перед сборкой тестов; закрытые ключи создаются временно на runner и не включаются в приложение или артефакты. - Сообщение «непригодные данные» при импорте приглашения пока не разобрано: пользователь попросил отложить его. Следующий этап — исправить импорт и добавить сканер QR, затем поиск ПК и проверки устойчивости. - `93f7c9c`: [Actions 34694138394](https://github.com/polskikh13/musicbridgeios/actions/runs/34694138394) — 10 XCTest, 0 отказов, успешный Release archive. Версия 0.1.1 (build 2), IPA скачана в `build/iPhone-0.1.1/MusicBridge-iPhone.ipa`; локально проверены ZIP, iPhoneOS ARM64, версия и ATS-настройки внутри готовой IPA. После обновления пользователь подтвердил: «всё работает в пилотном режиме». Это подтверждает первую рабочую связку iPhone ↔ Windows; полная матрица ручных проверок и длительная работа пока не подтверждены. ## Карта - `MusicBridge/Models.swift`: контракт и проверка входных данных. - `BridgeClient.swift`: сертификат, HTTPS, WSS, ограничения ответов. - `KeychainStore.swift`: хранение сопряжения. - `PlayerStore.swift`: состояние, команды, жизненный цикл и восстановление. - `ContentView.swift`: подключение и плеер. - `QRScanner.swift`: камера AVFoundation, доступ, сканирование и жизненный цикл. - `DiscoveryStore.swift`: Bonjour, разрешение адресов, TXT и безопасный выбор адреса. - `ConnectionStorage.swift`: доступ к Keychain и подмена хранилища для изолированных тестов жизненного цикла. - `LiveActivityManager.swift`: запуск, обновление, устаревание и завершение одной локальной Live Activity. - `Shared/`: ограниченный публичный снимок трека и общие SwiftUI-компоненты карточки. - `MusicBridgeWidgets/`: расширение WidgetKit, экран блокировки и все представления Dynamic Island. - `MusicBridgeTests/`: XCTest и пример Windows JSON. - `tools/`: генератор проекта, структурные проверки, выбор симулятора для CI. - `.github/workflows/ios.yml`: CI на macOS. - `tools/package_ipa.sh`: Release archive для настоящего iPhone и упаковка Payload. - `tools/verify_ipa.py`: проверка ZIP, Info.plist, ARM64 и платформы Mach-O (iOS, не Simulator). ## QR и поиск ПК — 0.2.0 (build 3) - Добавлен сканер внутри приложения; после сканирования заполняются адрес, отпечаток и код, подключение запускает пользователь. Также зарегистрирована схема `musicbridge://`, приглашения принимаются через `onOpenURL`. Посторонний QR, повреждённый JSON и истёкший код дают понятную ошибку. - Регистрация URL-схемы устраняет отсутствие обработчика ссылки при чтении QR стандартной камерой. Исходное сообщение «непригодные данные» ещё нужно перепроверить на телефоне, точный источник той ошибки не был установлен. - Поиск использует NSBonjourServices и NetServiceBrowser. Объявления версии 1 принимаются только с допустимым SHA-256 и частным IPv4. При первом выборе найденного ПК пользователь сверяет полный отпечаток с его экраном. - При восстановлении адреса TXT служит только подсказкой: сертификат из сети не заменяет сохранённый. Чужой fingerprint не используется; токен отправляется только через соединение с прежним TLS pin. После успеха адрес обновляется в Keychain. - Поиск приостанавливается в фоне; адреса повторно разрешаются раз в 15 секунд, список ограничен 64 сервисами и 16 адресами на сервис. Есть повторный поиск и переход в настройки разрешений. - `397caf2`: Actions 34694893204 — 14 XCTest, 0 отказов, Release archive успешен. Проверены формат объявления Windows, разбор sockaddr, отказы чужим данным, сохранение pin/token при смене адреса и .NET timestamp с семью дробными цифрами. Камера и реальный multicast iPhone ↔ Windows не эмулируются этими тестами. - Итоговый `e384e9a`: [Actions 34695228108](https://github.com/polskikh13/musicbridgeios/actions/runs/34695228108) — 14 XCTest без отказов и успешный Release archive. Экран подключения просмотрен на снимке симулятора; исправлен статус при возвращении без сохранённого ПК. IPA скачана в `build/iPhone-0.2.0/MusicBridge-iPhone.ipa`; проверены ZIP, ARM64 iPhoneOS, версия 0.2.0 (3), разрешение камеры, Bonjour и обработчик URL в готовом пакете. Пользователь подтвердил работу QR через камеру на iPhone. Следующий шаг: проверить Bonjour/смену IP и устойчивость, улучшить ошибки и восстановление обложки. ## Следующие этапы — выполнять последовательно 1. **Устойчивость и ошибки — версия 0.3.0 (build 4).** Исправления и автоматические проверки завершены. Следующий ручной шаг — проверка на iPhone. 2. **Интерфейс — версия 0.4.0 (build 5).** Добавлены иконка, компактный экран QR, раскрываемые ручные поля, крупная play/pause, процент громкости и меню ПК. Сборка и 23 XCTest прошли; просмотрены темы, компактный экран и крупный текст. Пользователь подтвердил работу обновления на телефоне перед началом этапа 3. 3. **Системные интеграции — 0.5.0 (build 6).** Локальная Live Activity и Dynamic Island реализованы; 26 XCTest и Release archive прошли. Карточка показывает последний снимок с ПК, открывает плеер, помечает устаревшие данные. App Intents и APNs не реализованы. Не имитировать фоновое аудио. Не обещать постоянный WSS при блокировке. 4. **Несколько сохранённых ПК** — последующее расширение, сейчас сохраняется один. Изменения третьего этапа — Live Activities 0.5.0: - Итоговый `ddd44e8`: [Actions 34747772034](https://github.com/polskikh13/musicbridgeios/actions/runs/34747772034) — 26 XCTest без отказов, сборка приложения и WidgetKit-расширения, успешный Release archive. IPA скачана в `build/iPhone-0.5.0/MusicBridge-iPhone.ipa`; проверены оба ARM64 iPhoneOS исполняемых файла, структура `.appex`, версии и NSSupportsLiveActivities. Светлый/тёмный предпросмотр карточки просмотрен: текст и отметка устаревания читаемы. Установка/регистрация Live Activity на физическом iPhone остаются ручной проверкой. - ActivityKit без push: старт только кнопкой пользователя при активном приложении и медиасессии. Восстанавливается существующая активность, дубли завершаются; после удаления пользователем автоматического пересоздания нет. - WidgetKit-расширение встроено в приложение. В attributes/content нет адреса ПК, токена, fingerprint или обложки; только публичные данные трека, ограниченные по UTF-8 (по 256 байт для названия и исполнителя). - Обычные позиции объединяются до одного обновления в пять секунд; смена трека, длительности или состояния воспроизведения отправляется сразу. Прогресс статический, соответствует последнему снимку. В фоне/без сети помечается устаревшим; `staleDate` через 15 секунд страхует внезапную остановку приложения. - Нет медиасессии, отзыв доступа, «Забыть компьютер» или ручное выключение — завершение. Нажатие карточки открывает `musicbridge://player`, не попадает в импорт QR. - Новые XCTest: позиция относительно startSeconds, нулевая длительность/пауза, чрезмерные Unicode-метаданные и размер Codable payload. Проверка IPA требует встроенную ARM64 iPhoneOS `.appex`, совпадающие версии и поддержку Live Activities. - CI-предпросмотр общей разметки карточки проверяет светлую/тёмную темы и устаревшие данные. Это не проверка системной регистрации ActivityKit/Island. На физическом iPhone проверить старт, блокировку, раскрытие Island, переход в плеер, возврат, удаление карточки, отсутствие автопересоздания, окончание сессии и отзыв доступа. - Следующие отдельные задачи: LiveActivityIntent с безопасным выполнением команды (проверить доступность Keychain/локальной сети при блокировке); Siri; APNs, если требуются актуальные данные после приостановки приложения. Постоянный WSS в фоне не реализован и не обещается. Windows-протокол не менялся. - Основание: [Apple ActivityKit](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities) и [WidgetKit extension](https://developer.apple.com/documentation/widgetkit/creating-a-widget-extension). Изменения второго этапа: - Итоговый `aa0b0f0`: [Actions 34746477145](https://github.com/polskikh13/musicbridgeios/actions/runs/34746477145) — 23 XCTest без отказов и успешный Release archive. Итоговая IPA скачана в `build/iPhone-0.4.0/MusicBridge-iPhone.ipa`; проверены ZIP, iPhoneOS ARM64, версия 0.4.0 (5), AppIcon в Info.plist и скомпилированный каталог Assets.car. Снимки находятся в `build/iPhone-0.4.0/final-preview`. На маленьком экране основные кнопки видны сразу; для нижней части громкости может требоваться прокрутка. - `cbd3c0f`: добавлены оригинальная иконка 1024×1024 и каталог AppIcon, карточка QR, раскрываемые ручные поля, меню ПК, крупная play/pause и процент громкости. - `aa0b0f0`: после просмотра iPhone SE уменьшена обложка по доступной высоте окна; размеры заданы явно, чтобы заглушка оставалась квадратной. Исполнитель выровнен по центру. - В Debug есть изолированный предпросмотр с примером трека и памятью вместо Keychain; в Release этот режим недоступен. CI сохраняет светлый/тёмный плеер, крупный текст, компактный экран и подключение. Ошибка Keychain -34018 на снимке подключения относится к неподписанному запуску в CI; доступ к Keychain на телефоне требует подписи. - Ручная проверка обновления: установить IPA через iloader, проверить иконку, сохранённое сопряжение, QR, меню ПК, перемотку и громкость; повторить с крупным системным шрифтом и тёмной темой. Реальную камеру и домашнюю сеть CI не проверяет. Изменения первого этапа: - Итоговый `bde6f7a`: [Actions 34712051233](https://github.com/polskikh13/musicbridgeios/actions/runs/34712051233) — 23 XCTest без отказов, Release archive успешен. IPA 0.3.0 (build 4) скачана в `build/iPhone-0.3.0/MusicBridge-iPhone.ipa`; проверены ZIP, платформа iPhoneOS, ARM64 и версия. Экран подключения просмотрен на снимке симулятора. - Четыре попытки загрузить обложку, паузы 2/4/8 секунд, отдельное сообщение и кнопка ручного повтора. После 404 перечитывается состояние, чтобы заметить смену трека. Старая обложка не должна подменять новую после запоздавшего ответа. - При уходе в фон закрываются текущие соединения и незавершённое сопряжение; его поздний ответ не сохраняется. Если сервер уже создал запись устройства, пользователь получает указание удалить её перед повторным сопряжением. - Ответ 401 переводит приложение в состояние повторного сопряжения, останавливает автоматические повторы и сохраняет этот статус при возвращении из фона. - Понятные сообщения для invalid_code, pairing_closed, device_limit, storage_failed, HTTP-ошибок, сетевых сбоев и TLS. Текст отказа команды не стирается обновлением прогресса; сообщение можно скрыть. Команды не повторяются автоматически. - Успех команды и неудача последующего чтения состояния различаются в интерфейсе. После недоступности Keychain при запуске чтение повторяется при активации приложения. - Реальные HTTPS/WSS тесты используют временный сервер CI с задержками, 404/401/503, redirect, ошибками сопряжения и сменой обложки. Реальный пользовательский Keychain в этих тестах не изменяется. Сон ПК, Wi-Fi и системную блокировку надо проверить отдельно на физических устройствах. ## Результаты 2026-09-10 - `07322c2`: успешный [Actions run 34445566212](https://github.com/polskikh13/musicbridgeios/actions/runs/34445566212). Добавлен Release archive и неподписанный IPA для Sideloadly. Скачан в `build/Sideloadly/MusicBridge-iPhone.ipa` (112334 байта). Локальная проверка ZIP, Info.plist и Mach-O подтвердила iPhoneOS ARM64, минимум iOS 17.0. Подпись и установка выполняются пользователем в Sideloadly; на физическом iPhone ещё не проверено. Инструкция добавлена в README.md. - `a761431`: успешный [Actions run 34444563869](https://github.com/polskikh13/musicbridgeios/actions/runs/34444563869), macos-15, Xcode 16.4, Swift 6.1.2, симулятор iOS 18.5; 7 XCTest прошли. Артефакты: MusicBridge-simulator и ios-test-results (лог и xcresult), хранение 7 дней. - Первый CI-прогон не дошёл до компиляции: исправлены регистр UDID и выбор runtime по версии активного SDK. Две регрессионные Python-проверки прошли локально. - Структурная проверка Python прошла: генератор детерминирован, ссылки проекта, состав исходников, Info.plist, схема и JSON согласованы. - В CI есть предупреждения инструментов о подписанных библиотеках XCTest, отсутствии AppIntents и Node 20 в checkout/upload-artifact v4; тесты прошли. Миграция Actions на актуальные версии — отдельное небольшое обновление. - Следующий шаг: интеграционные проверки TLS/WSS и UI, затем сканер QR/Bonjour. Физический iPhone, Apple-подпись и управление настоящим ПК ещё не проверены.