Playable nativ auf macOS und Linux

Playable
20. September 2026
PAP-024

Playable erhält zwei eigenständige Desktop-Apps mit gemeinsamem Swift-Kern. macOS verwendet SwiftUI und AppKit, Linux GTK4 und libadwaita. Beide teilen Fachlogik, API-Vertrag, lokale Datenregeln und Wiedergabezustände, aber keine Oberfläche und keine Annahme über den UI-Ereigniszyklus. Der bestehende Projektplan bleibt bis Epic 09 unverändert; die Linux-Arbeit folgt als Epic 10 bis 13.

Inhalt

  1. Die Entscheidung
  2. Der gemeinsame Kern
  3. Zwei native Apps
  4. Packages und Ausweichwege
  5. Die Epics in Reihenfolge
  6. Prüfpunkte und Fallstricke
  7. Wo Plan und Fortschritt stehen

Anhang

  1. Vorgesehene Modulstruktur
  2. Daten- und Ereignisfluss
  3. Quellennachweis

1. Die Entscheidung

Playable wird nicht als eine Oberfläche gebaut, die sich an zwei Systeme anpasst. Es wird als ein Produkt mit zwei nativen Oberflächen gebaut. Die macOS-App folgt den Konventionen von macOS, die Linux-App den Konventionen von GNOME und libadwaita. Gemeinsam bleibt, was für einen Nutzer auf beiden Systemen dasselbe bedeutet: Podcast, Episode, Favorit, Konto, Warteschlange, Wiedergabezustand, Suche und Synchronisation.

Der gemeinsame Teil ist ein Satz kleiner Swift-Module. Sie kennen weder Fenster noch Schaltflächen, weder AppKit noch GTK. Sie sprechen mit der Playable-Plattform über deren versionierten OpenAPI-Vertrag und erhalten Live-Änderungen über die dafür vorgesehene Echtzeitverbindung. Diese Verträge entstehen bereits in Epic 03 und sind damit die feste Grenze für beide Apps.12

Swift ist auf den vorgesehenen Linux-Systemen offiziell verfügbar.3 Damit liegt die sprachliche Gemeinsamkeit dort, wo sie Wert hat, ohne den nativen Charakter der Apps einzuschränken.

Tabelle 1 Welche Verantwortung an welcher Stelle liegt
TeilVerantwortungGeteilt
PlayableCoreDomänenmodelle, Anwendungsfälle, Sortierung, Suche, Wiedergabezustand und Fehlerformenja
PlayableAPIOpenAPI-Client, Transportmodelle, Authentifizierung, Echtzeitereignisse und Abbildung in die Domäneja
PlayableStoreLokaler Cache, Favoritenzustand, Wiedergabefortschritt und ausstehende Änderungenja
PlayableMacSwiftUI, AppKit, AVFoundation, Keychain, Now Playing und Apple-Benachrichtigungennein
PlayableLinuxGTK4, libadwaita, GStreamer, libsecret, MPRIS und Flatpaknein

2. Der gemeinsame Kern

Der Kern stellt Werte und Fähigkeiten bereit, keine globalen App-Zustände. Ein Podcast oder eine Episode ist ein unveränderlicher Swift-Wert. Ein Repository lädt und verändert Daten. Ein Wiedergabecontroller übersetzt Befehle und Ereignisse in einen nachvollziehbaren Zustand. Die Apps entscheiden selbst, wie sie diesen Zustand beobachten und darstellen.

Die wichtigsten Grenzen sind PodcastRepository, AccountRepository, FavouritesRepository, RealtimeFeed, AudioEngine, CredentialStore, NowPlayingService und NotificationService. Jede Plattform setzt nur die Grenzen um, für die sie eigene Systemdienste besitzt. Transportmodelle bleiben in PlayableAPI, Datenbankzeilen in PlayableStore und Framework-Objekte in der jeweiligen App.

Die API wird aus dem veröffentlichten OpenAPI-Vertrag erzeugt. Der erzeugte Client bleibt hinter PlayableAPI, damit eine Änderung des Generators nicht in die Oberfläche wandert. Auf Linux übernimmt ein AsyncHTTPClient-Transport die Verbindung.45 Die Echtzeitverbindung liefert Ereignisse mit denselben kanonischen Kennungen wie die REST-Antworten und kann nach einem Verbindungsabbruch einen konsistenten Stand wiederherstellen.2

Lokale Daten liegen in SQLite. SQLite.swift ist dafür die erste Wahl, weil es Swift und Linux ausdrücklich unterstützt.6 Geheimnisse gehören nicht in diese Datenbank. Sie liegen auf macOS im Keychain und auf Linux über libsecret im Secret Service.

Die wichtigste technische Grenze

Der gemeinsame Kern ist nicht an MainActor gebunden. macOS führt UI-Änderungen auf dem Swift Main Actor aus. Linux übergibt sie an den GLib Main Context. Ein gemeinsames UI-Beobachtungsobjekt würde diese beiden Modelle vermischen und wird deshalb nicht verwendet.

3. Zwei native Apps

Auf macOS entsteht die Oberfläche mit SwiftUI und gezielten AppKit-Bausteinen. Fenster, Seitenleiste, Toolbar, Menüs, Tastaturkürzel, Einstellungen, Wiederherstellung und Systemmediensteuerung verhalten sich wie Bestandteile einer macOS-App. AVFoundation übernimmt die Wiedergabe, Keychain die Sitzung und MediaPlayer die Systemanzeige.

Auf Linux entsteht die Oberfläche mit GTK4 und libadwaita. Navigation, Toolbar, Listen, Dialoge, adaptive Aufteilung und schmale Fenster folgen der GNOME Human Interface Guideline.7 Die Anwendung verwendet Standard-Widgets, damit Tastaturbedienung und AT-SPI-Semantik bereits in den Grundelementen vorhanden sind.8

swift-adwaita ist die bevorzugte Swift-Bindung. Das Package deckt GTK4 und libadwaita ab und führt eigene Hinweise für GLib-Nebenläufigkeit und GTK-Objektlebenszeiten.91011 Seine Typen bleiben vollständig in PlayableLinux, damit die App bei Bedarf zu einer anderen Bindung wechseln kann.

Tabelle 2 Native Dienste der beiden Apps
FähigkeitmacOSLinux
OberflächeSwiftUI und AppKitGTK4 und libadwaita
AudioAVFoundationGStreamer
ZugangsdatenKeychainlibsecret und Secret Service
SystemmedienMediaPlayer und Now PlayingMPRIS über GDBus
BenachrichtigungenApple Push und UserNotificationsDesktop-Benachrichtigungen während der Laufzeit; Hintergrundzustellung als eigener Board-Vorgang
DistributionMac App Store und signierte BuildsFlatpak und Flathub

Die Wiedergaberegeln sind gemeinsam, die Decoder nicht. Ein pausierter Live-Stream schließt seine Verbindung. Beim Fortsetzen beginnt er an der aktuellen Live-Position. Eine aufgezeichnete Episode behält ihre Position und kann suchen. Beide Audioadapter melden dieselben Zustände: Auflösen, Puffern, Abspielen, Pausieren, Stoppen und Fehler.

4. Packages und Ausweichwege

Jede externe Abhängigkeit bekommt eine enge Aufgabe und eine definierte Ausweichroute. Ein Package wird nur übernommen, wenn der Feasibility-Lauf seine benötigten Funktionen ohne Fork nachweist.

Tabelle 3 Bevorzugte Packages und ihre Ausweichwege
BereichBevorzugtAusweichwegEntscheidungskriterium
Linux-Oberflächeswift-adwaitaAdwaita for Swift, danach SwiftGtkNative adaptive Fenster, sichere GLib-Übergabe und beherrschbare Objektlebenszeit ohne Fork
APISwift OpenAPI Generatorkleiner typisierter AsyncHTTPClient-ClientDer erzeugte Vertrag bleibt vollständig hinter PlayableAPI
Lokale DatenSQLite.swiftschmaler eigener SQLite-C-AdapterMigrationen, Transaktionen und Linux-Tests laufen ohne eigene SQLite-Funktionen
Linux-Audioschmaler eigener Adapter um GStreamer playbingstreamer-swift, danach libmpvStreams, Pufferung, Tags, Fehler, Lautstärke und Spektrum funktionieren ohne Package-Fork
Linux-Geheimnisselibsecret-Systemmodulnur flüchtige SitzungGesperrter, fehlender und abgebrochener Secret Service werden ausdrücklich behandelt
Linux-SystemmedienGDBus und MPRISSwift-DBUS nach vollständigem Bus-Name-Managementplayerctl findet und steuert die App mit minimalen Sandbox-Rechten

GStreamer playbin übernimmt Formaterkennung, Decoderwahl und Netzwerkpufferung.12 Der Swift-Adapter bleibt klein und serialisiert alle Pipeline-Aufrufe. gstreamer-swift wird im Feasibility-Lauf geprüft, aber nur übernommen, wenn Bus-Nachrichten, Abbruch, Streamwechsel und Spektraldaten vollständig zugänglich sind.13

libsecret ist die direkte Anbindung an den Secret Service.14 MPRIS stellt Wiedergabestatus, Metadaten, Lautstärke und Befehle bereit.15 Für Live-Inhalte meldet die Linux-App ausdrücklich, dass Suchen nicht möglich ist.

5. Die Epics in Reihenfolge

Die bestehenden Epics 01 bis 07 bauen Plattform, Daten, API, Benachrichtigungen, Website, Dashboard und Dokumentation. Epic 08 und 09 liefern die macOS-App und das vollständige Podcast-Erlebnis. Ihre gemeinsamen Swift-Teile werden von Beginn an frei von Apple-UI-Typen gehalten. Dadurch beginnt Linux nicht mit einer nachträglichen Portierung, sondern mit einem bereits getesteten Kern.

Tabelle 4 Die Desktop-Epics in ihrer Ausführungsreihenfolge
EpicZielErgebnis
E08macOS Live-Erlebnis und portable Desktop-GrenzenGreenfield-App, OpenAPI-Client, Echtzeitdaten, Live-Wiedergabe, Favoriten und macOS-Integration. PlayableCore, PlayableAPI und PlayableStore enthalten keine Apple-UI-Typen.
E09Vollständiges On-Demand-ErlebnisSuche, Mediathek, Episoden, Warteschlange, Downloads, Fortschritt und Synchronisation erweitern dieselben gemeinsamen Module.
E10Linux-Feasibility und AnwendungsskelettSwift-Toolchain, libadwaita-Fenster, GLib-Übergabe, API-Aufruf, GStreamer, libsecret, MPRIS und Offline-Flatpak-Build sind im Ziel-Runtime nachgewiesen.
E11Native Linux-Live-AppAdaptive GTK-Oberfläche, Live-Katalog, Suche, Favoriten, Echtzeitstatus, GStreamer-Wiedergabe und native Desktop-Steuerung.
E12Native Linux-Podcast-AppMediathek, Episoden, Wiedergabefortschritt, Warteschlange, Downloads und plattformübergreifend synchronisierte Zustände.
E13Linux-Distribution und PlattformparitätFlathub-Metadaten, Sandbox-Rechte, Codec-Matrix, GNOME- und KDE-Prüfung, Orca, Installationsprüfung und dokumentierte Plattformunterschiede.

E10 wird erst als GitHub-Epic angelegt, wenn E08 die gemeinsamen Grenzen tatsächlich liefert. Sein erster Vorgang ist ein Wegwerf-Prototyp, der die riskanten Linux-Anbindungen gemeinsam im Flatpak-Runtime prüft. Die Entscheidung über Wrapper fällt aus diesem Lauf, nicht aus der API-Oberfläche eines Repositories.

6. Prüfpunkte und Fallstricke

Flatpak gehört in den ersten Linux-Lauf. Ein Flathub-Build darf seine SwiftPM-Abhängigkeiten nicht während des Baus aus dem Netz laden. Die Quellen werden deshalb mit dem offiziellen SwiftPM-Generator vorab in das Manifest aufgenommen.1617 GTK, libadwaita und GStreamer kommen dynamisch aus dem GNOME-Runtime. Ein vollständig statisches Swift-Linux-SDK ersetzt diese Bibliotheken nicht.

Tabelle 5 Die Prüfungen, die E10 bestehen muss
PrüfungBestanden, wenn
GTK und Swift ConcurrencyEin Hintergrundereignis aktualisiert reproduzierbar ein Widget über den GLib Main Context und wird beim Schließen vollständig abgebrochen.
ObjektlebenszeitFenster, Dialoge und Signal-Closures schließen ohne veralteten Callback, Zyklus oder Absturz.
Playable-APIDer generierte Client lädt einen kanonischen Live-Stand und stellt einen strukturierten Fehler samt errorId dar.
EchtzeitEine getrennte Verbindung synchronisiert sich wieder und liefert danach einen konsistenten Stand.
AudioMP3, AAC im vorhandenen Codec-Runtime, Ogg/Opus, Redirects, .m3u und .pls laufen oder scheitern mit eindeutigem Grund.
GeheimnisseSpeichern, Lesen, Löschen, gesperrter Keyring, fehlender Dienst und Nutzerabbruch sind sichtbar und sicher behandelt.
MPRISplayerctl findet Playable und steuert Wiedergabe sowie Lautstärke; Live-Inhalte melden CanSeek als falsch.
FlatpakDer Build löst alle Quellen offline auf, installiert sauber und läuft auf x86_64 und aarch64.
Tabelle 6 Fallstricke und ihre feste Gegenmaßnahme
FallstrickGegenmaßnahme
Swift Main Actor und GLib Main Context werden gleichgesetztGetrennte Plattform-Presenter und ein ausdrücklicher GLib-Übergang
GTK-Signale halten Fenster oder Controller festBesitzverhältnisse pro Signal dokumentieren und beim Schließen trennen
Ein Codec fehlt nur im ausgelieferten FlatpakDie Stream-Matrix läuft im exakten Release-Runtime
Ein junger Wrapper wird zur unersetzbaren GrundlageWrapper-Typen bleiben im Adapter; jeder Bereich besitzt einen benannten Ausweichweg
Ein optimistischer Favorit wird bei Wiederholung zurückgedrehtDie Warteschlange speichert den gewünschten Zustand, niemals einen Toggle
Der Secret Service ist nicht verfügbarFlüchtige Sitzung oder klarer Fähigkeitsfehler, niemals Klartextablage
Die Linux-App kopiert das macOS-LayoutGemeinsame Informationsarchitektur, getrennte native Komponenten und Plattformprüfung
Hintergrundbenachrichtigungen werden als Desktop-Detail behandeltEin eigener Board-Vorgang entscheidet über Dienst, Zustellung und Lebenszyklus

7. Wo Plan und Fortschritt stehen

Dieses Paper hält die Architektur, die Paketentscheidungen und die Reihenfolge der Epics fest. Es ändert sich, wenn eine Modulgrenze, ein Package, ein Ausweichweg oder die Reihenfolge geändert wird. Das GitHub-Projekt bleibt die einzige Sicht auf den Fortschritt. Ein Epic trägt den Zuschnitt, seine Issues tragen die Arbeit und ihre Checklisten.18

Die Epics E10 bis E13 werden als Board-Vorgänge angelegt, sobald die Linux-Umsetzung freigegeben wird. Bis dahin ist dieses Paper die technische Entscheidung, nicht der Beginn einer Implementierung.

Anhang

Vorgesehene Dateien, Ereigniswege und geprüfte Quellen. Für die Entscheidung und Reihenfolge oben braucht man das hier nicht.

A. Vorgesehene Modulstruktur

Package.swift
Sources/
  PlayableCore/
    Domain/
    Errors/
    Ports/
    State/
    UseCases/
  PlayableAPI/
    Generated/
    Client/
    Mapping/
    Realtime/
  PlayableStore/
    Database/
    Migrations/
    Repositories/
    Sync/
  PlayableLinux/
    Application/
    Presentation/
    UI/
    Platform/
Apps/
  macOS/
    PlayableMac/
      Application/
      Presentation/
      UI/
      Platform/
Packaging/
  Flatpak/

PlayableCore kennt nur Swift und Foundation-Werte. PlayableAPI kennt den erzeugten Vertrag. PlayableStore kennt SQLite. Die beiden Apps setzen die Ports zusammen und halten ihre Frameworks in Platform und UI.

B. Daten- und Ereignisfluss

Native UI
  -> platform presenter
  -> shared use case
  -> repository or platform port
  -> API, store or audio adapter
  -> normalized domain event
  -> shared state transition
  -> platform presenter
  -> native UI event loop

Der letzte Schritt ist absichtlich plattformspezifisch. macOS wechselt auf den Main Actor, Linux auf den GLib Main Context. Wiedergabe und lokale Daten laufen jeweils serialisiert, damit C-Frameworks, Datenbank und Oberfläche nicht gleichzeitig denselben veränderlichen Zustand besitzen.

C. Quellennachweis

  1. Playable: Define versioned API boundaries and the OpenAPI contract. Issue 28. Quelle↩
  2. Playable: Publish realtime live-state updates. Issue 34. Quelle↩↩
  3. Swift.org: Platform Support. Volltext gelesen Quelle↩
  4. Apple: Swift OpenAPI Generator. Repository und Dokumentation gelesen Quelle↩
  5. Swift Server Work Group: Swift OpenAPI AsyncHTTPClient Transport. Repository und Dokumentation gelesen Quelle↩
  6. SQLite.swift: Linux documentation. Volltext gelesen Quelle↩
  7. GNOME: Human Interface Guidelines. Relevante Abschnitte gelesen Quelle↩
  8. GNOME: Accessibility guidelines. Volltext gelesen Quelle↩
  9. Makoni: swift-adwaita. Repository und Dokumentation gelesen Quelle↩
  10. Makoni: Concurrency on GTK. Volltext gelesen Quelle↩
  11. Makoni: GTK lifetime and ownership. Volltext gelesen Quelle↩
  12. GStreamer: playbin documentation. Volltext gelesen Quelle↩
  13. Wendy Labs: gstreamer-swift. Öffentliche API und Dokumentation gelesen Quelle↩
  14. GNOME: libsecret documentation. Relevante Abschnitte gelesen Quelle↩
  15. freedesktop.org: MPRIS Player interface. Volltext gelesen Quelle↩
  16. Flathub: Requirements for applications. Volltext gelesen Quelle↩
  17. Flatpak: SwiftPM source generator. Volltext gelesen Quelle↩
  18. Playable: Product README and Project board. Aktuellen Stand gelesen Quelle↩