Die Pakete wissen jetzt, was ein Fernseher ist
Schritt eins zur Apple-TV-Fassung: nichts davon ist sichtbar, aber ohne das lässt sich für tvOS nicht einmal übersetzen. **Der stille Fehler zuerst.** `FlixyPlatform.isMac` hatte einen `#else`-Zweig, der `true` zurückgab — eine Antwort auf eine Frage, die niemand gestellt hatte. Auf tvOS fiel er dort hindurch: kein Übersetzungsfehler, keine Warnung, nur ein Fernseher, der sich für einen Mac hielt. Jetzt steht dort `#error`. Die nächste Plattform bricht den Bau ab und verlangt eine Entscheidung, statt sich eine auszusuchen. Ein Test hält fest, dass nicht beides zugleich gelten darf; er hätte genau diesen Fehler gefangen. **Ein echter Übersetzungsfehler**, und er lag an der Reihenfolge: `UIPasteboard` steht hinter `canImport(UIKit)`, und das ist auf tvOS wahr. Der tvOS-Zweig steht deshalb jetzt davor — ein Fernseher hat ohnehin nichts, wohin man etwas einfügen könnte. **Drei Vermutungen waren falsch**, nachgeschlagen im tvOS-SDK statt geraten: `presentationDetents` gibt es dort seit tvOS 16 (übersetzt, bewirkt nur nichts — jetzt steht es ausdrücklich da), `.topBarTrailing` seit 14.0, und `isMac` hatte acht Fundstellen in fünf Dateien, nicht fünfzig in siebenundzwanzig. **Schrift und Abstände** bekommen einen eigenen Zweig statt durchzufallen. Nicht feste Grössen: Dynamic Type gibt es auf tvOS auch, und dass `body` dort grösser ausfällt, ist der Sinn der Sache. Falsch war nur die Zuordnung der Rollen — die Skala ist anders gespreizt, und eine Beschriftung in `subheadline` unter einem Plakat ist aus drei Metern Zierrat. Der Aufmacher geht auf `.title`, weil UIKit `largeTitle` auf tvOS gar nicht kennt und nirgends steht, was SwiftUI daraus macht. `screenMargin` steht auf 48 und nicht auf 90: die neunzig Punkte des Überscan-Bereichs liefert das System als Sicherheitsrand, und wer sie hier aufschlägt, hat hundertachtzig. **Zwei Dinge, die sich erst beim Bauen zeigten.** `FlixyDownloads` übersetzte nicht — `volumeAvailableCapacityForImportantUsage` gibt es auf tvOS nicht. Das Paket wird dort trotzdem mitverlinkt, damit `PlayerModel` den Koordinator nennen darf, ohne dass zweitausend Zeilen Wiedergabelogik von `#if` durchzogen werden; angelegt wird nie einer. Und `FlixyProbe` liess das ganze Paket scheitern: ein Kommandozeilenwerkzeug mit `getpass` und `Host`, das auf einem Fernseher nie laufen sollte, hielt den Bau für alle anderen auf. Es ist jetzt ausdrücklich macOS-only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4576eac67c
commit
6010229b80
@@ -8,8 +8,9 @@ let package = Package(
|
||||
defaultLocalization: "en",
|
||||
// macOS steht hier nur, damit `swift test` die Logik-Tests ohne
|
||||
// Simulator laufen lassen kann — das spart bei jedem Durchlauf Minuten.
|
||||
// Die App selbst ist iOS-only; das legt project.yml fest.
|
||||
platforms: [.iOS(.v26), .macOS(.v26)],
|
||||
// Welche Plattformen die Apps bedienen, legt project.yml fest: iOS für
|
||||
// Flixy, tvOS für FlixyTV.
|
||||
platforms: [.iOS(.v26), .macOS(.v26), .tvOS(.v26)],
|
||||
products: [
|
||||
.library(name: "MediaKit", targets: ["MediaKit"]),
|
||||
.library(name: "PlexKit", targets: ["PlexKit"]),
|
||||
|
||||
@@ -14,6 +14,13 @@ import SwiftUI
|
||||
/// Das kostet auf dem Mac Dynamic Type. Das ist vertretbar: macOS hat keine
|
||||
/// systemweite Textgrößen-Einstellung, auf die Apps reagieren müssten, und ein
|
||||
/// Fenster lässt sich stattdessen größer ziehen.
|
||||
///
|
||||
/// **Auf dem Fernseher wieder Systemstile, aber andere Rollen.** Dynamic Type
|
||||
/// gibt es dort auch, und dass `body` grösser ausfällt als am Telefon, ist
|
||||
/// der Sinn der Sache — drei Meter Abstand. Falsch wäre nur, die iOS-Zuordnung
|
||||
/// einfach durchfallen zu lassen: die Skala ist dort anders gespreizt, und
|
||||
/// eine Beschriftung in `subheadline` unter einem Plakat ist aus drei Metern
|
||||
/// Zierrat.
|
||||
extension Font {
|
||||
#if os(macOS)
|
||||
/// Der Titel im Aufmacher-Bild.
|
||||
@@ -28,6 +35,29 @@ extension Font {
|
||||
public static let flixyBody = Font.system(size: 17)
|
||||
/// Jahreszahl, Laufzeit, Auflösung — alles, was neben dem Titel steht.
|
||||
public static let flixyCaption = Font.system(size: 12)
|
||||
#elseif os(tvOS)
|
||||
/// Der Titel im Aufmacher-Bild.
|
||||
///
|
||||
/// **`.title` und nicht `.largeTitle`.** In SwiftUI gibt es den Stil auf
|
||||
/// tvOS, in UIKit nicht — `UIFontTextStyleLargeTitle` ist dort als nicht
|
||||
/// verfügbar markiert. Was SwiftUI daraus macht, steht nirgends, und eine
|
||||
/// Schrift, deren Grösse niemand nennen kann, gehört nicht in die grösste
|
||||
/// Zeile der App.
|
||||
public static let flixyHero = Font.system(.title, design: .default, weight: .bold)
|
||||
/// Titel auf Detailseiten.
|
||||
public static let flixyTitle = Font.system(.title2, design: .default, weight: .semibold)
|
||||
/// Überschrift einer Reihe auf der Startseite.
|
||||
public static let flixyRowHeader = Font.system(.headline, design: .default, weight: .semibold)
|
||||
/// Titel unter einem Poster — auf tvOS nur bei Fokus sichtbar.
|
||||
///
|
||||
/// Eine Stufe tiefer als auf iOS, und das ist gewollt: auf tvOS ist
|
||||
/// `caption` schon gross, und ein Raster aus sechs Spalten mit
|
||||
/// `subheadline`-Titeln hätte mehr Schrift als Bild.
|
||||
public static let flixyCardTitle = Font.system(.caption, design: .default, weight: .medium)
|
||||
/// Fließtext, Beschreibungen.
|
||||
public static let flixyBody = Font.system(.body)
|
||||
/// Jahreszahl, Laufzeit, Auflösung — alles, was neben dem Titel steht.
|
||||
public static let flixyCaption = Font.system(.caption2)
|
||||
#else
|
||||
/// Der Titel im Aufmacher-Bild.
|
||||
public static let flixyHero = Font.system(.largeTitle, design: .default, weight: .bold)
|
||||
@@ -49,6 +79,22 @@ extension Font {
|
||||
/// Eine Skala statt frei gewählter Zahlen: dadurch stimmen die Ränder zwischen
|
||||
/// Bildschirmen überein, ohne dass man sie einzeln nachmisst.
|
||||
public enum FlixySpacing {
|
||||
#if os(tvOS)
|
||||
// Durchweg doppelt: ein Fernseher ist 1920 Punkte breit und wird aus drei
|
||||
// Metern gelesen. Acht Punkte Abstand sind dort keine Trennung mehr.
|
||||
public static let tight: CGFloat = 8
|
||||
public static let snug: CGFloat = 16
|
||||
public static let regular: CGFloat = 24
|
||||
public static let loose: CGFloat = 32
|
||||
public static let section: CGFloat = 48
|
||||
/// Der seitliche Rand des Inhalts — **zusätzlich** zum Sicherheitsrand.
|
||||
///
|
||||
/// **Nicht 90.** Die neunzig Punkte des Überscan-Bereichs liefert das
|
||||
/// System als Sicherheitsrand; wer sie hier noch einmal aufschlägt, hat
|
||||
/// hundertachtzig und ein Raster, das in der Bildmitte klebt. Hier steht
|
||||
/// nur die Rinne innerhalb des sicheren Bereichs.
|
||||
public static let screenMargin: CGFloat = 48
|
||||
#else
|
||||
public static let tight: CGFloat = 4
|
||||
public static let snug: CGFloat = 8
|
||||
public static let regular: CGFloat = 12
|
||||
@@ -56,10 +102,34 @@ public enum FlixySpacing {
|
||||
public static let section: CGFloat = 24
|
||||
/// Der seitliche Rand des Inhalts zum Bildschirmrand.
|
||||
public static let screenMargin: CGFloat = 16
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Der Überscan-Bereich eines Fernsehers.
|
||||
///
|
||||
/// **Was das ist.** Ein Fernseher zeigt nicht zwingend das ganze Bild: je nach
|
||||
/// Gerät und Einstellung schneidet er am Rand ab, und was dort steht, sieht
|
||||
/// niemand. Apple nennt für 1920×1080 einen sicheren Bereich von neunzig
|
||||
/// Punkten seitlich und sechzig oben wie unten.
|
||||
///
|
||||
/// Ein **Bild** darf darüber hinausgehen — ein Aufmacher soll das sogar.
|
||||
/// Schrift und Knöpfe nicht. Die Zahl steht hier für die Fälle, in denen der
|
||||
/// Sicherheitsrand absichtlich übergangen wird und der Text trotzdem drinnen
|
||||
/// bleiben muss.
|
||||
public enum FlixySafeArea {
|
||||
public static let tv = EdgeInsets(top: 60, leading: 90, bottom: 60, trailing: 90)
|
||||
}
|
||||
|
||||
public enum FlixyRadius {
|
||||
/// Die Ecke einer Kachel.
|
||||
///
|
||||
/// Auf dem Fernseher grösser: eine Kachel, die sich beim Fokus anhebt,
|
||||
/// wirkt mit acht Punkten bei 1920 Punkten Breite kantig.
|
||||
#if os(tvOS)
|
||||
public static let card: CGFloat = 12
|
||||
#else
|
||||
public static let card: CGFloat = 8
|
||||
#endif
|
||||
public static let sheet: CGFloat = 16
|
||||
public static let control: CGFloat = 10
|
||||
/// Eine schwebende Fläche — die Seitenleiste. So rund wie das System sie
|
||||
|
||||
@@ -5,7 +5,7 @@ import UIKit
|
||||
import AppKit
|
||||
#endif
|
||||
|
||||
/// Was auf iPhone und Mac verschieden heißt — an einer Stelle.
|
||||
/// Was auf iPhone, Mac und Fernseher verschieden heißt — an einer Stelle.
|
||||
///
|
||||
/// Nicht jede Ansicht soll `#if os(iOS)` tragen. Ein Formular, das auf beiden
|
||||
/// Plattformen dasselbe meint, soll auf beiden gleich aussehen; wo die
|
||||
@@ -25,19 +25,49 @@ public enum FlixyPlatform {
|
||||
/// Wer nur die erste Hälfte prüft, schreibt Code für den Mac, der auf dem
|
||||
/// Mac nicht läuft: übersetzt sauber, ist aber genau dort abgeschaltet,
|
||||
/// wo er gebraucht wird. Deshalb hier beide Wege, an einer Stelle.
|
||||
///
|
||||
/// **Und warum unten `#error` steht.** Der `#else`-Zweig lautete `true` —
|
||||
/// eine Antwort auf eine Frage, die niemand gestellt hatte. Auf tvOS fiel
|
||||
/// er deshalb hindurch und meldete einen Mac: kein Übersetzungsfehler,
|
||||
/// keine Warnung, nur ein Fernseher, der sich für einen Mac hielt. Eine
|
||||
/// Vorgabe ist an dieser Stelle immer falsch, weil die nächste Plattform
|
||||
/// nicht danach fragt, ob sie gemeint war. Jetzt bricht der Bau ab und
|
||||
/// verlangt eine Entscheidung.
|
||||
public static let isMac: Bool = {
|
||||
#if targetEnvironment(macCatalyst)
|
||||
#if os(tvOS)
|
||||
false
|
||||
#elseif targetEnvironment(macCatalyst)
|
||||
true
|
||||
#elseif os(iOS)
|
||||
ProcessInfo.processInfo.isiOSAppOnMac
|
||||
#else
|
||||
#elseif os(macOS)
|
||||
true
|
||||
#else
|
||||
#error("FlixyPlatform kennt diese Plattform nicht. Hier eintragen — nicht raten.")
|
||||
#endif
|
||||
}()
|
||||
|
||||
/// Läuft die App auf einem Fernseher?
|
||||
///
|
||||
/// Daneben und nicht in einem Aufzählungstyp: über die Frage wird
|
||||
/// nirgends verzweigt, sie steht überall in einem Fragezeichen-Ausdruck.
|
||||
/// Ein Aufzählungstyp wäre Zeremonie — und „iPhone oder iPad" liesse sich
|
||||
/// darin ohnehin nicht beantworten, weil das `UIDevice.current` verlangt
|
||||
/// und damit den Hauptakteur.
|
||||
public static let isTV: Bool = {
|
||||
#if os(tvOS)
|
||||
true
|
||||
#elseif os(iOS) || os(macOS)
|
||||
false
|
||||
#else
|
||||
#error("FlixyPlatform kennt diese Plattform nicht. Hier eintragen — nicht raten.")
|
||||
#endif
|
||||
}()
|
||||
}
|
||||
|
||||
extension View {
|
||||
/// Eine schmale Titelzeile — auf dem Mac gibt es sie nicht.
|
||||
/// Eine schmale Titelzeile — auf dem Mac und auf dem Fernseher gibt es
|
||||
/// sie nicht. Beide fallen in denselben `#else`-Zweig, und beide zu Recht.
|
||||
public func flixyInlineTitle() -> some View {
|
||||
#if os(iOS)
|
||||
navigationBarTitleDisplayMode(.inline)
|
||||
@@ -51,22 +81,37 @@ extension View {
|
||||
/// Auf dem Mac ist ein Sheet ein Fenster: dort zählt keine Rastung,
|
||||
/// sondern eine vernünftige Mindestgröße.
|
||||
public func flixyMediumSheet() -> some View {
|
||||
#if os(tvOS)
|
||||
// Auf dem Fernseher ist ein Sheet immer bildfüllend. `presentationDetents`
|
||||
// übersetzt dort zwar — es gibt sie seit tvOS 16 —, bewirkt aber
|
||||
// nichts. Und eine Zeile, die nichts bewirkt, liest sich beim nächsten
|
||||
// Mal wie eine, die etwas bewirkt.
|
||||
self
|
||||
#else
|
||||
// **Zur Laufzeit, nicht beim Übersetzen.** Der `#else`-Zweig für
|
||||
// macOS griff nie: auf dem Mac läuft der iOS-Bau, und damit bekam ein
|
||||
// grosses Fenster eine Sheet-Kante in iPhone-Höhe — ein schmaler
|
||||
// Streifen, der unten im Nichts klebt.
|
||||
presentationDetents(FlixyPlatform.isMac ? [.large] : [.medium])
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Dasselbe, aber auf Wunsch bis zur vollen Höhe aufziehbar.
|
||||
public func flixyResizableSheet() -> some View {
|
||||
#if os(tvOS)
|
||||
self
|
||||
#else
|
||||
// Siehe oben. Auf dem Mac gibt es kein „halb aufgezogen": ein Fenster
|
||||
// hat die Höhe, die es hat, und eine Liste soll sie nutzen.
|
||||
presentationDetents(FlixyPlatform.isMac ? [.large] : [.medium, .large])
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Ein Formular, das auf beiden Seiten gleich aussieht.
|
||||
///
|
||||
/// Auf tvOS gibt es `formStyle` nicht und es ist auch nicht nötig — dort
|
||||
/// fällt es in denselben Zweig wie iOS.
|
||||
///
|
||||
/// Auf dem Mac setzt SwiftUI sonst den Spalten-Stil: Beschriftungen rechts
|
||||
/// ausgerichtet, keine abgesetzten Abschnitte, alles eng. Der gruppierte
|
||||
/// Stil ist derselbe, den iOS ohnehin nimmt — und der Grund, warum die
|
||||
@@ -82,6 +127,9 @@ extension View {
|
||||
|
||||
/// Eingabefelder für Adressen und Namen: keine Großschreibung, keine
|
||||
/// Korrektur — auf dem Mac gibt es beides ohnehin nicht als Tastatur.
|
||||
///
|
||||
/// Auf tvOS ebenfalls der `#else`-Zweig: die Bildschirmtastatur dort
|
||||
/// kennt weder `keyboardType` noch Autokorrektur in dieser Form.
|
||||
public func flixyPlainEntry(isAddress: Bool = false) -> some View {
|
||||
#if os(iOS)
|
||||
self
|
||||
@@ -98,8 +146,9 @@ extension View {
|
||||
/// Keine eigene Fläche hinter der Titelzeile.
|
||||
///
|
||||
/// Auf der Startseite liegt darunter der Aufmacher — eine weichgezeichnete
|
||||
/// Leiste verschleierte die Hälfte davon. Auf dem Mac gibt es die Leiste in
|
||||
/// dieser Form nicht.
|
||||
/// Leiste verschleierte die Hälfte davon. Auf dem Mac und auf dem
|
||||
/// Fernseher gibt es die Leiste in dieser Form nicht — beide fallen in
|
||||
/// den `#else`-Zweig.
|
||||
public func flixyClearToolbarBackground() -> some View {
|
||||
#if os(iOS)
|
||||
self
|
||||
@@ -117,7 +166,16 @@ extension View {
|
||||
/// deswegen nicht mit `#if` beginnen.
|
||||
public enum FlixyClipboard {
|
||||
public static func copy(_ text: String) {
|
||||
#if canImport(UIKit)
|
||||
#if os(tvOS)
|
||||
// Es gibt keine. `UIPasteboard` ist auf tvOS als nicht verfügbar
|
||||
// markiert — und ein Fernseher hat auch nichts, wohin man etwas
|
||||
// einfügen könnte.
|
||||
//
|
||||
// **Die Reihenfolge ist der ganze Punkt.** `canImport(UIKit)` ist auf
|
||||
// tvOS wahr; stünde dieser Zweig darunter, wäre es kein stiller
|
||||
// Fehler, sondern ein lauter.
|
||||
_ = text
|
||||
#elseif canImport(UIKit)
|
||||
UIPasteboard.general.string = text
|
||||
#else
|
||||
NSPasteboard.general.clearContents()
|
||||
@@ -129,7 +187,7 @@ public enum FlixyClipboard {
|
||||
extension ToolbarItemPlacement {
|
||||
/// Rechts in der Titelzeile.
|
||||
public static var flixyTrailing: ToolbarItemPlacement {
|
||||
#if os(iOS)
|
||||
#if os(iOS) || os(tvOS)
|
||||
.topBarTrailing
|
||||
#else
|
||||
.primaryAction
|
||||
@@ -138,7 +196,7 @@ extension ToolbarItemPlacement {
|
||||
|
||||
/// Links in der Titelzeile.
|
||||
public static var flixyLeading: ToolbarItemPlacement {
|
||||
#if os(iOS)
|
||||
#if os(iOS) || os(tvOS)
|
||||
.topBarLeading
|
||||
#else
|
||||
.navigation
|
||||
|
||||
@@ -106,9 +106,22 @@ public struct DownloadFileStore: Sendable {
|
||||
///
|
||||
/// Vor jedem Download geprüft: ein Download, der das Gerät vollschreibt,
|
||||
/// legt auch alles andere lahm.
|
||||
///
|
||||
/// **Auf tvOS gibt es die Auskunft nicht** —
|
||||
/// `volumeAvailableCapacityForImportantUsage` ist dort als nicht
|
||||
/// verfügbar markiert, und ein Apple TV hat auch keinen Speicher, auf den
|
||||
/// man etwas legt. Das Paket wird dort trotzdem mitverlinkt, damit
|
||||
/// `PlayerModel` den Koordinator **nennen** darf, ohne dass zweitausend
|
||||
/// Zeilen Wiedergabelogik von `#if` durchzogen werden. Angelegt wird nie
|
||||
/// einer; diese Zeile läuft also nie. Null heisst „kein Platz", und das
|
||||
/// ist die richtige Antwort für ein Gerät, auf dem nichts liegen soll.
|
||||
public func availableCapacity() -> Int64 {
|
||||
#if os(tvOS)
|
||||
0
|
||||
#else
|
||||
let values = try? root.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
|
||||
return values?.volumeAvailableCapacityForImportantUsage ?? 0
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Entfernt Dateien, zu denen es keinen Eintrag mehr gibt.
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
// **Nur für den Rechner, auf dem entwickelt wird.**
|
||||
//
|
||||
// Das hier ist ein Kommandozeilenwerkzeug: es fragt nach einem Passwort über
|
||||
// `getpass`, löst Rechnernamen über `Host` auf und druckt in ein Terminal.
|
||||
// Nichts davon gibt es auf iOS, und auf tvOS erst recht nicht — die App
|
||||
// hängt auch nicht daran. Ohne diese Bedingung scheitert aber jeder Versuch,
|
||||
// **das ganze Paket** für eine andere Plattform zu übersetzen, an einem
|
||||
// Werkzeug, das dort nie laufen sollte. Das kostete eine halbe Stunde
|
||||
// Fehlersuche an der falschen Stelle.
|
||||
#if os(macOS)
|
||||
import Foundation
|
||||
import JellyfinKit
|
||||
import MediaKit
|
||||
@@ -523,3 +533,4 @@ func zeigeSpuren(host: String, suche: String) async {
|
||||
print("Zu lesen ist das so: stimmt das Kürzel nicht mit dem Namen überein,")
|
||||
print("lügt die Datei — und dann hilft nur der Name oder das Merken.")
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
// **Nur für den Rechner, auf dem entwickelt wird.**
|
||||
//
|
||||
// Das hier ist ein Kommandozeilenwerkzeug: es fragt nach einem Passwort über
|
||||
// `getpass`, löst Rechnernamen über `Host` auf und druckt in ein Terminal.
|
||||
// Nichts davon gibt es auf iOS, und auf tvOS erst recht nicht — die App
|
||||
// hängt auch nicht daran. Ohne diese Bedingung scheitert aber jeder Versuch,
|
||||
// **das ganze Paket** für eine andere Plattform zu übersetzen, an einem
|
||||
// Werkzeug, das dort nie laufen sollte. Das kostete eine halbe Stunde
|
||||
// Fehlersuche an der falschen Stelle.
|
||||
#if os(macOS)
|
||||
import Foundation
|
||||
import MediaKit
|
||||
import PlexKit
|
||||
@@ -380,3 +390,4 @@ func herausziehen(_ name: String, aus text: String) -> String? {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import Testing
|
||||
@testable import FlixyDesign
|
||||
|
||||
@Suite("Auf welchem Gerät wir laufen")
|
||||
struct FlixyPlatformTests {
|
||||
|
||||
/// **Der Test, den es vorher nicht gab — und der gefehlt hat.**
|
||||
///
|
||||
/// `isMac` hatte einen `#else`-Zweig, der `true` zurückgab. Auf tvOS fiel
|
||||
/// er dort hindurch: kein Übersetzungsfehler, keine Warnung, nur ein
|
||||
/// Fernseher, der sich für einen Mac hielt. Zwei Wahrheitswerte, von denen
|
||||
/// beide gelten, sind immer ein Fehler — und dieser eine Ausdruck hätte
|
||||
/// ihn gefangen.
|
||||
///
|
||||
/// Er läuft hier auf macOS und beweist damit nur die Hälfte. Die andere
|
||||
/// Hälfte beweist er, sobald er auf einem Fernseher läuft — und bis dahin
|
||||
/// hält er wenigstens fest, dass die Frage gestellt gehört.
|
||||
@Test("Ein Gerät ist nicht zugleich Mac und Fernseher")
|
||||
func neverBoth() {
|
||||
#expect(!(FlixyPlatform.isMac && FlixyPlatform.isTV))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user