Close revoked remote sessions and document installation requirements
iOS build and tests / simulator (push) Has been cancelled
iOS build and tests / simulator (push) Has been cancelled
This commit is contained in:
@@ -32,8 +32,10 @@ struct ContentView: View {
|
||||
Label(systemPlayer.running ? "Выключить системный пульт" : "Включить системный пульт iOS", systemImage: "play.rectangle")
|
||||
}.disabled(systemPlayer.busy || (!systemPlayer.running && (!store.connected || store.state?.hasSession != true)))
|
||||
if systemPlayer.busy { ProgressView() }
|
||||
Text("Музыка ПК в Пункте управления. Звук остаётся на компьютере.")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
Text("Музыка ПК в Пункте управления. Звук остаётся на компьютере.")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
Text("Без фоновых push-обновлений трек обновляется при открытом приложении или после команды пульта.")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
if let error = systemPlayer.error { Text(error).font(.caption).foregroundStyle(.red).textSelection(.enabled) }
|
||||
}.frame(maxWidth: .infinity, alignment: .leading).bridgeCard()
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ final class SystemPlayerManager: ObservableObject {
|
||||
do {
|
||||
try SystemPlayerCredentials.save(connection, id: newID)
|
||||
_ = try SystemPlayerCredentials.load(newID)
|
||||
UserDefaults.standard.set(newID, forKey: savedIDKey)
|
||||
let session = try await RemoteMediaSession.start(attributes: SystemPlayerAttributes(id: newID, state: state))
|
||||
guard generation == operation else { try? await session.end(); return }
|
||||
storage = session
|
||||
|
||||
@@ -45,7 +45,8 @@ final class PCMediaSession: RemoteMediaSessionRepresentable {
|
||||
}
|
||||
|
||||
var commands: [MediaCommand] {
|
||||
[
|
||||
guard attributes.hasSession else { return [] }
|
||||
return [
|
||||
.play { try await self.send(.play) }.enabled(attributes.capabilities.play),
|
||||
.pause { try await self.send(.pause) }.enabled(attributes.capabilities.pause),
|
||||
.next { try await self.send(.next) }.enabled(attributes.capabilities.next),
|
||||
@@ -62,7 +63,15 @@ final class PCMediaSession: RemoteMediaSessionRepresentable {
|
||||
}
|
||||
|
||||
private func send(_ action: SystemPlayerAction, value: Double? = nil) async throws {
|
||||
let state = try await transport.send(action, value: value, sessionID: id)
|
||||
attributes = SystemPlayerAttributes(id: id, state: state)
|
||||
do {
|
||||
let state = try await transport.send(action, value: value, sessionID: id)
|
||||
attributes = SystemPlayerAttributes(id: id, state: state)
|
||||
} catch BridgeError.unauthorized {
|
||||
try SystemPlayerCredentials.delete(id)
|
||||
attributes.hasSession = false
|
||||
attributes.playing = false
|
||||
attributes.volume = nil
|
||||
throw BridgeError.unauthorized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,38 @@
|
||||
# MusicBridge iOS — план
|
||||
|
||||
Обновлено: 2026-09-13. Пользователь разрешил начать нативное iOS-приложение.
|
||||
Обновлено: 2026-09-14. Пользователь разрешил начать нативное iOS-приложение.
|
||||
Репозиторий: `C:/Users/areso/iOS MusicBridge`; origin — dev.yukinoki.ru/musicbridge/ios.
|
||||
Windows-агент — соседний проект `C:/Users/areso/MusicBridge/MusicBridge.Agent`.
|
||||
|
||||
## Системный пульт 0.7.0 (8) — текущий этап
|
||||
|
||||
- [x] Реализация NowPlaying RemoteMediaSession и отдельного ExtensionKit target для iOS 27.
|
||||
- [x] Кнопка включения/выключения в приложении; музыка продолжает играть на ПК.
|
||||
- [x] Метаданные, обложка, play/pause, предыдущий/следующий трек, перемотка, громкость ПК.
|
||||
- [x] Перед командой проверяются свежие capabilities и действующее сопряжение; команды не повторяются автоматически.
|
||||
- [x] Токен хранится в общем Keychain, не передаётся в атрибутах системной сессии.
|
||||
- [x] Перемотка переводится из позиции относительно трека в диапазон Windows API.
|
||||
- [x] Выключение и удаление сопряжения удаляют доступ расширения; возврат запоздавшего restore не восстанавливает сессию.
|
||||
- [ ] Успешная итоговая сборка и XCTest на Xcode 27; первая попытка 34783380221 выявила Double/Float и исправлена.
|
||||
- [ ] Установка с подписью обоих расширений и общим Keychain; проверка на физическом iPhone.
|
||||
- [ ] Пункт управления: кнопки, перемотка, громкость; блокировка телефона, сон ПК, отзыв токена, повторная подпись.
|
||||
|
||||
**Ограничение iloader:** проверен официальный исходник `nab138/isideload`, ветка
|
||||
`apple-codesign-quick`, commit `f6a4d5dba717d72fc2af63eaba26b27ba44116be`, который использует текущий iloader.
|
||||
`isideload/src/sideload/bundle.rs` обнаруживает расширения только в `PlugIns`,
|
||||
а NowPlaying размещается в `Extensions`. `sign.rs` берёт entitlements из profile,
|
||||
добавление shared Keychain реализовано для отдельных SpecialApp, не для MusicBridge.
|
||||
Поэтому обычную установку 0.7.0 через iloader нельзя считать поддержанной.
|
||||
Не переносить ExtensionKit в PlugIns наугад и не передавать токен через публичные атрибуты.
|
||||
Следующий этап установки: совместимый подписыватель или доработка iloader для
|
||||
ExtensionKit и общего Keychain с реальными provisioning profiles. Xcode signing
|
||||
всех трёх targets — вариант при наличии совместимого Mac и профилей; разрешение
|
||||
нового extension point бесплатной командой ещё не подтверждено на устройстве.
|
||||
|
||||
Без APNs расширение читает ПК при команде; открытое приложение обновляет сессию
|
||||
при смене трека/статуса и периодически. Смена трека на самом ПК при приостановленном
|
||||
приложении не гарантирует автоматического обновления карточки. Фиктивное аудио не используется.
|
||||
|
||||
## Инфраструктура — текущее состояние
|
||||
|
||||
- SSH к macOS проверен: `aresov@10.67.67.28`, macOS 26.6.2, x86_64,
|
||||
@@ -73,7 +102,7 @@ Windows-агент — соседний проект `C:/Users/areso/MusicBridge
|
||||
- [x] LiveActivityIntent: предыдущий/следующий трек, play/pause и обновление — 0.6.0.
|
||||
- [ ] Проверить команды из Live Activity/Island при блокировке на физическом iPhone.
|
||||
- [ ] Команды Siri.
|
||||
- [ ] NowPlaying RemoteMediaSession (iOS 27+): перейти на SDK 27, добавить remote-media extension и проверить системный Control Center.
|
||||
- [x] NowPlaying RemoteMediaSession (iOS 27+): SDK 27 и remote-media extension реализованы в 0.7.0; установка и физическая проверка — в текущем этапе выше.
|
||||
- [ ] APNs: отдельный проект обновлений при приостановленном приложении, если нужен постоянный актуальный трек в фоне.
|
||||
|
||||
Ограничения: хранится один доверенный ПК; адрес при смене IP восстанавливается через
|
||||
|
||||
@@ -23,6 +23,23 @@ Windows-агентом и подключение по QR. В 0.4.0 добавл
|
||||
|
||||
## Сборка на Mac
|
||||
|
||||
Версия **0.7.0 (8)** добавляет системный пульт iOS 27: в плеере нажать
|
||||
«Включить системный пульт iOS», затем открыть Пункт управления. Реализованы
|
||||
play/pause, следующий/предыдущий трек, перемотка и громкость ПК. Звук остаётся на ПК.
|
||||
Проверка этой функции на физическом iPhone пока не завершена.
|
||||
|
||||
Для этой версии нужно подписать **MusicBridge, MusicBridgeWidgets и MusicBridgeRemote**.
|
||||
У приложения и MusicBridgeRemote должны совпадать разрешённые Keychain Sharing
|
||||
groups: `<AppIdentifierPrefix>ru.yukinoki.musicbridge.remote-control`.
|
||||
Префикс определяется из подписи при запуске, без хранения Apple Account в проекте.
|
||||
MusicBridgeRemote — ExtensionKit в `Extensions`, а WidgetKit — в `PlugIns`.
|
||||
Текущий iloader не обрабатывает эту комбинацию полностью: обычную переподпись
|
||||
0.7.0 считать поддержанной нельзя. Для прежней установки оставлена IPA 0.6.0 ниже.
|
||||
Состояние работ и требования к подписывателю описаны в `PLAN.md`.
|
||||
|
||||
Без APNs трек обновляется из открытого приложения или после команды системного
|
||||
пульта. Изменения на ПК при приостановленном приложении могут отображаться с задержкой.
|
||||
|
||||
1. Открыть `MusicBridge.xcodeproj` в Xcode 27.
|
||||
2. Выбрать схему MusicBridge и симулятор iPhone. Run запускает приложение,
|
||||
Test выполняет `MusicBridgeTests`.
|
||||
|
||||
Reference in New Issue
Block a user