Files
ios/PLAN.md
T

251 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-подпись и управление настоящим ПК ещё не проверены.