Catalyst: ohne Netz, ohne Übersetzung, ohne Fenstergröße
Drei Fehler auf einmal, und alle drei meine. **Die App kam gar nicht ins Netz.** Ein Catalyst-Programm läuft in der Sandbox und braucht eine ausdrückliche Erlaubnis für ausgehende Verbindungen. Ohne sie gibt es keine Fehlermeldung — der Ladekreis dreht sich einfach für immer, und genau so sah es aus. **In der Seitenleiste standen Schlüssel statt Wörtern** — „sidebar.downloads" statt „Downloads". Ich hatte zwei Fassungen derselben Beschriftung gebaut, eine mit `LocalizedStringKey` und eine mit `String` für Titel, die vom Server kommen. Bei einem Literal darf Swift sich für beide entscheiden, und es entschied sich nicht überall gleich. Jetzt gibt es eine Fassung; wer einen fertigen Text hat, baut sein `Text` selbst. **Das Fenster öffnete wieder in Notizzettelgröße.** Meine Größenangabe hing an `os(macOS)` — unter Catalyst gilt aber `os(iOS)`, also griff sie nicht. Dort hängt die Größe ohnehin an der Szene, nicht am Fenster. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f296d7ae1a
commit
39d9138911
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<!-- Catalyst-Programme laufen in der Sandbox. Ohne die Erlaubnis für
|
||||||
|
ausgehende Verbindungen erreicht die App keinen Server — und zwar
|
||||||
|
ohne Fehlermeldung: der Ladekreis dreht sich einfach für immer. -->
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
+31
-1
@@ -70,9 +70,18 @@ struct FlixyApp: App {
|
|||||||
// Eine Untergrenze, unter der die Oberfläche nicht mehr
|
// Eine Untergrenze, unter der die Oberfläche nicht mehr
|
||||||
// aufgeht: schmaler als das hier klappt die Seitenleiste zu
|
// aufgeht: schmaler als das hier klappt die Seitenleiste zu
|
||||||
// und die Reihen zeigen anderthalb Kacheln.
|
// und die Reihen zeigen anderthalb Kacheln.
|
||||||
#if os(macOS)
|
//
|
||||||
|
// `os(macOS)` allein reichte nicht — unter Catalyst gilt
|
||||||
|
// `os(iOS)`, und genau deshalb öffnete das Fenster wieder in
|
||||||
|
// Notizzettelgröße.
|
||||||
|
#if os(macOS) || targetEnvironment(macCatalyst)
|
||||||
.frame(minWidth: 900, minHeight: 620)
|
.frame(minWidth: 900, minHeight: 620)
|
||||||
#endif
|
#endif
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
// Unter Catalyst gibt es `defaultSize` nicht; die Größe hängt
|
||||||
|
// an der Szene.
|
||||||
|
.task { Self.widenWindow() }
|
||||||
|
#endif
|
||||||
.environment(session)
|
.environment(session)
|
||||||
.environment(playback)
|
.environment(playback)
|
||||||
.environment(playbackSettings)
|
.environment(playbackSettings)
|
||||||
@@ -123,6 +132,27 @@ struct FlixyApp: App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
extension FlixyApp {
|
||||||
|
/// Gibt dem Fenster eine vernünftige Mindestgröße.
|
||||||
|
///
|
||||||
|
/// Unter Catalyst öffnet macOS sonst ein Fenster von der Größe eines
|
||||||
|
/// Notizzettels — und darin fällt der Aufmacher in sich zusammen, weil
|
||||||
|
/// seine Abstände dehnbar sind.
|
||||||
|
@MainActor
|
||||||
|
static func widenWindow() {
|
||||||
|
for scene in UIApplication.shared.connectedScenes {
|
||||||
|
guard let windowScene = scene as? UIWindowScene else { continue }
|
||||||
|
windowScene.sizeRestrictions?.minimumSize = CGSize(width: 900, height: 620)
|
||||||
|
windowScene.sizeRestrictions?.maximumSize = CGSize(
|
||||||
|
width: CGFloat.greatestFiniteMagnitude,
|
||||||
|
height: CGFloat.greatestFiniteMagnitude
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Der klassische App-Delegat, für das eine, was SwiftUI nicht abdeckt.
|
/// Der klassische App-Delegat, für das eine, was SwiftUI nicht abdeckt.
|
||||||
@MainActor
|
@MainActor
|
||||||
final class AppDelegate: NSObject, UIApplicationDelegate {
|
final class AppDelegate: NSObject, UIApplicationDelegate {
|
||||||
|
|||||||
@@ -40,6 +40,8 @@
|
|||||||
<key>NSAllowsLocalNetworking</key>
|
<key>NSAllowsLocalNetworking</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
|
<string>Flixy benötigt Zugriff auf das lokale Netzwerk, um deinen Medienserver zu finden und Filme direkt von ihm zu laden.</string>
|
||||||
<key>UIBackgroundModes</key>
|
<key>UIBackgroundModes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>audio</string>
|
<string>audio</string>
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
{
|
{
|
||||||
"sourceLanguage" : "en",
|
"sourceLanguage" : "en",
|
||||||
"strings" : {
|
"strings" : {
|
||||||
"%@ GB": {
|
|
||||||
"comment": "A picker option in the settings view that lets the user select the maximum amount of storage space that can be used for downloaded content. The value is given in gigabytes.",
|
|
||||||
"isCommentAutoGenerated": true
|
|
||||||
},
|
|
||||||
"%@ → %@" : {
|
"%@ → %@" : {
|
||||||
"comment" : "A line in a diagnostic trace that shows a network request path and its result. The first argument is the network request path. The second argument is the network response result.",
|
"comment" : "A line in a diagnostic trace that shows a network request path and its result. The first argument is the network request path. The second argument is the network response result.",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
@@ -17,8 +13,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"1080p": {
|
"%@ GB" : {
|
||||||
"comment": "A label for a 1080p video.",
|
"comment" : "A picker option in the settings view that lets the user select the maximum amount of storage space that can be used for downloaded content. The value is given in gigabytes.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"4K" : {
|
"4K" : {
|
||||||
@@ -29,8 +25,8 @@
|
|||||||
"comment" : "A label for a 720p video.",
|
"comment" : "A label for a 720p video.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"SD": {
|
"1080p" : {
|
||||||
"comment": "A label for a 720p movie.",
|
"comment" : "A label for a 1080p video.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"account.hint" : {
|
"account.hint" : {
|
||||||
@@ -3212,23 +3208,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"player.subtitleTracks": {
|
|
||||||
"extractionState": "stale",
|
|
||||||
"localizations": {
|
|
||||||
"de": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "Untertitel"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"en": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "Subtitles"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"player.subtitlesOff" : {
|
"player.subtitlesOff" : {
|
||||||
"comment" : "Eintrag, der die Untertitel abschaltet",
|
"comment" : "Eintrag, der die Untertitel abschaltet",
|
||||||
"extractionState" : "stale",
|
"extractionState" : "stale",
|
||||||
@@ -3247,7 +3226,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"player.subtitleTracks" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
|
"localizations" : {
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Untertitel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Subtitles"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"player.tracks" : {
|
"player.tracks" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -3329,22 +3326,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"quality.autoShort": {
|
|
||||||
"localizations": {
|
|
||||||
"de": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "Auto"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"en": {
|
|
||||||
"stringUnit": {
|
|
||||||
"state": "translated",
|
|
||||||
"value": "Auto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"quality.automatic" : {
|
"quality.automatic" : {
|
||||||
"extractionState" : "stale",
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -3362,6 +3343,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"quality.autoShort" : {
|
||||||
|
"localizations" : {
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Auto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Auto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"quality.current" : {
|
"quality.current" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
@@ -3685,6 +3682,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"SD" : {
|
||||||
|
"comment" : "A label for a 720p movie.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"search.hint" : {
|
"search.hint" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ struct RootView: View {
|
|||||||
) {
|
) {
|
||||||
mediaStack { LibraryView(library: library) }
|
mediaStack { LibraryView(library: library) }
|
||||||
} label: {
|
} label: {
|
||||||
tabLabel(library.title, symbol: symbol(for: library.kind))
|
sidebarLabel(Text(library.title), symbol: symbol(for: library.kind))
|
||||||
}
|
}
|
||||||
.customizationID("flixy.tab.library.\(library.id)")
|
.customizationID("flixy.tab.library.\(library.id)")
|
||||||
}
|
}
|
||||||
@@ -292,22 +292,19 @@ struct RootView: View {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Die Beschriftung eines Eintrags.
|
/// Die Beschriftung eines Eintrags.
|
||||||
|
///
|
||||||
|
/// **Ein** Aufruf, nicht zwei. Es gab hier eine zweite Fassung für Titel,
|
||||||
|
/// die vom Server kommen — mit `String` statt `LocalizedStringKey`. Swift
|
||||||
|
/// darf sich bei einem Literal für beide entscheiden, und es entschied sich
|
||||||
|
/// nicht überall gleich: in der Seitenleiste stand plötzlich
|
||||||
|
/// „sidebar.downloads" statt „Downloads". Wer einen fertigen Text hat,
|
||||||
|
/// baut sein `Text` jetzt selbst.
|
||||||
private func tabLabel(_ title: LocalizedStringKey, symbol: String) -> some View {
|
private func tabLabel(_ title: LocalizedStringKey, symbol: String) -> some View {
|
||||||
let label = Label(title, systemImage: symbol)
|
sidebarLabel(Text(title), symbol: symbol)
|
||||||
#if os(macOS)
|
|
||||||
return label.frame(
|
|
||||||
minWidth: Self.sidebarWidth.min - 40,
|
|
||||||
maxWidth: Self.sidebarWidth.max - 40,
|
|
||||||
alignment: .leading
|
|
||||||
)
|
|
||||||
#else
|
|
||||||
return label
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dieselbe Beschriftung, wenn der Titel erst vom Server kommt.
|
private func sidebarLabel(_ title: Text, symbol: String) -> some View {
|
||||||
private func tabLabel(_ title: String, symbol: String) -> some View {
|
let label = Label { title } icon: { Image(systemName: symbol) }
|
||||||
let label = Label(title, systemImage: symbol)
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
return label.frame(
|
return label.frame(
|
||||||
minWidth: Self.sidebarWidth.min - 40,
|
minWidth: Self.sidebarWidth.min - 40,
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ targets:
|
|||||||
CODE_SIGN_IDENTITY: "-"
|
CODE_SIGN_IDENTITY: "-"
|
||||||
DEVELOPMENT_TEAM: ""
|
DEVELOPMENT_TEAM: ""
|
||||||
ENABLE_HARDENED_RUNTIME: NO
|
ENABLE_HARDENED_RUNTIME: NO
|
||||||
|
CODE_SIGN_ENTITLEMENTS: Flixy/Flixy-macCatalyst.entitlements
|
||||||
info:
|
info:
|
||||||
path: Flixy/Info-macOS.plist
|
path: Flixy/Info-macOS.plist
|
||||||
properties:
|
properties:
|
||||||
@@ -222,6 +223,9 @@ targets:
|
|||||||
ITSAppUsesNonExemptEncryption: false
|
ITSAppUsesNonExemptEncryption: false
|
||||||
NSAppTransportSecurity:
|
NSAppTransportSecurity:
|
||||||
NSAllowsLocalNetworking: true
|
NSAllowsLocalNetworking: true
|
||||||
|
NSLocalNetworkUsageDescription: >-
|
||||||
|
Flixy benötigt Zugriff auf das lokale Netzwerk, um deinen
|
||||||
|
Medienserver zu finden und Filme direkt von ihm zu laden.
|
||||||
|
|
||||||
DownloadWidget:
|
DownloadWidget:
|
||||||
type: app-extension
|
type: app-extension
|
||||||
|
|||||||
Reference in New Issue
Block a user