Absturz beim Abspielen, und die Annahme dahinter

Der Crashlog vom Gerät:

    _dispatch_assert_queue_fail
    _swift_task_checkIsolatedSwift
    MainActor.assumeIsolated
    VLCPlayerEngine.mediaPlayerStateChanged(_:)
    VLCKit input_thread_Events

Im Code stand als Begründung für assumeIsolated, VLCKit stelle seine
Rückrufe über die Hauptwarteschlange zu. Das ist falsch — es ruft den
Delegaten von seinem eigenen Input-Thread. Die App brach deshalb hart
ab, sobald VLC den ersten Zustandswechsel meldete, also unmittelbar
beim Drücken auf Play.

Alle drei Delegat-Methoden springen jetzt mit Task { @MainActor in }
sauber hinüber. Die Reihenfolge bleibt erhalten: die Aufgaben werden aus
demselben Thread nacheinander eingereiht.

Dieselbe Annahme steckte im AVPlayer-Motor in den KVO-Beobachtern —
KVO meldet auf dem Thread, der die Änderung ausgelöst hat, und AVPlayer
ändert seinen Zustand auch aus internen Threads. Ebenfalls umgestellt.
Die beiden Rückrufe mit ausdrücklichem queue: .main behalten
assumeIsolated: dort ist die Annahme belegt statt geraten, und der
Zeitbeobachter feuert viermal je Sekunde.

Dazu die Anmeldung: Plex führt den Rücksprung über forwardUrl nicht
zuverlässig aus. Auf dem iPad landet der Browser nach dem Anmelden auf
watch.plex.tv und bleibt stehen — der Token liegt bereit, aber die App
wartete auf einen Rückruf, der nie kam. Das Fenster wird jetzt geöffnet,
ohne darauf zu warten; parallel wird im Sekundentakt beim Server
nachgefragt. Der Rücksprung ist damit eine Annehmlichkeit, keine
Voraussetzung.

132 Tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-09-11 21:25:17 +02:00
co-authored by Claude Opus 5
parent d9ae4b8a1f
commit a2b3f901ed
6 changed files with 410 additions and 290 deletions
@@ -45,6 +45,23 @@ public actor PlexAuthenticator {
)
}
/// Fragt einmal nach, ob der Code inzwischen bestätigt wurde.
///
/// Gibt `nil` zurück, solange er es nicht ist. Für Aufrufer, die ihre
/// eigene Schleife führen — etwa weil sie nebenher noch auf das Schließen
/// eines Anmeldefensters achten müssen.
public func token(for pin: PlexPin) async throws(PlexError) -> String? {
let current = try await client.send(
PlexRequest(path: "/api/v2/pins/\(pin.id)"),
as: PlexPin.self
)
guard let token = current.authToken, !token.isEmpty else { return nil }
try? tokenStore.store(token, for: Self.accountTokenAccount)
await client.setToken(token)
return token
}
/// Wartet darauf, dass der Nutzer den Code im Browser bestätigt.
///
/// Plex bietet keinen Rückkanal — es bleibt nur regelmäßiges Nachfragen.