From e24e0e68258e394dec40d99b86bcbc7a98bd91fb Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 13 May 2026 14:39:09 +0200 Subject: [PATCH] fix(editor): PhotosPicker Sendable-Warning durch Sub-View struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ImagePickerLabel als private View-Struct extrahiert. SwiftUIs PhotosPicker(label:)-Closure ist @Sendable, aber View-Konstruktor- Calls werden zur Build-Zeit MainActor-isoliert evaluiert — im Gegensatz zu direktem @State-Zugriff im Closure-Body. Vorher: pickerLabel als computed property → Warning blieb. Jetzt: ImagePickerLabel(hasImage: occlusionImage != nil) → Warning weg, Swift-Build clean. Archive grün, Build grün, keine Swift-Warnings mehr (nur eine AppIntents-Framework-Hinweis-Note ohne Auswirkung). Co-Authored-By: Claude Opus 4.7 (1M context) --- Sources/Features/Editor/CardEditorView.swift | 33 ++++---- Sources/Resources/Localizable.xcstrings | 89 +++++++++++++++++++- 2 files changed, 105 insertions(+), 17 deletions(-) diff --git a/Sources/Features/Editor/CardEditorView.swift b/Sources/Features/Editor/CardEditorView.swift index 3854fef..2f2249b 100644 --- a/Sources/Features/Editor/CardEditorView.swift +++ b/Sources/Features/Editor/CardEditorView.swift @@ -153,25 +153,11 @@ struct CardEditorView: View { } } - /// Extrahiert in eine eigene Property, weil PhotosPickers Label-Closure - /// unter Swift-6-Strict-Concurrency den direkten Zugriff auf - /// `@State`-Properties als Sendable-Verletzung markiert. Indirektion - /// über eine MainActor-isolierte computed Property löst das. - private var pickerLabel: some View { - Group { - if occlusionImage == nil { - Label("Bild auswählen", systemImage: "photo") - } else { - Label("Bild ersetzen", systemImage: "arrow.triangle.2.circlepath") - } - } - } - @ViewBuilder private var imageOcclusionFields: some View { Section("Bild") { PhotosPicker(selection: $imagePickerItem, matching: .images) { - pickerLabel + ImagePickerLabel(hasImage: occlusionImage != nil) } .onChange(of: imagePickerItem) { _, newItem in Task { await loadPickedImage(newItem) } @@ -364,3 +350,20 @@ private extension String { trimmingCharacters(in: .whitespacesAndNewlines) } } + +/// Wird als Sub-View aus dem PhotosPicker-Label-Closure aufgerufen. +/// Eigene `View`-Struct vermeidet die Swift-6-Strict-Concurrency- +/// Warning: SwiftUIs `PhotosPicker(label:)`-Closure ist `@Sendable`, +/// aber View-Konstruktor-Calls werden zur Build-Zeit MainActor-isoliert +/// evaluiert (im Gegensatz zu direktem @State-Zugriff im Closure-Body). +private struct ImagePickerLabel: View { + let hasImage: Bool + + var body: some View { + if hasImage { + Label("Bild ersetzen", systemImage: "arrow.triangle.2.circlepath") + } else { + Label("Bild auswählen", systemImage: "photo") + } + } +} diff --git a/Sources/Resources/Localizable.xcstrings b/Sources/Resources/Localizable.xcstrings index 9b38231..00f53d6 100644 --- a/Sources/Resources/Localizable.xcstrings +++ b/Sources/Resources/Localizable.xcstrings @@ -1,5 +1,90 @@ { "sourceLanguage" : "de", - "strings" : { }, + "strings" : { + "%@" : { + + }, + "%@ fällige Karten aus abonnierten Decks" : { + + }, + "%@ Karten gelernt" : { + + }, + "Abmelden" : { + + }, + "Alle Karten und Reviews dieses Decks werden ebenfalls gelöscht. Diese Aktion kann nicht rückgängig gemacht werden." : { + + }, + "Anmelden" : { + + }, + "Antwort anzeigen" : { + + }, + "Aus Teilen-Menü" : { + + }, + "Beide Richtungen werden gelernt — front→back und back→front." : { + + }, + "Card-Type »%@« kommt in einer späteren Phase" : { + + }, + "Cards" : { + + }, + "Changelog" : { + + }, + "Distractor-Optionen werden zur Lernzeit automatisch aus anderen Karten desselben Decks gezogen." : { + + }, + "Erst ein Deck erstellen." : { + + }, + "Erstelle dein erstes Deck auf cardecky.mana.how oder ziehe nach unten zum Aktualisieren." : { + + }, + "Inbox" : { + + }, + "Karteikarten des Vereins mana e.V." : { + + }, + "Karten konnten nicht geladen werden" : { + + }, + "Keine" : { + + }, + "Lade Decks …" : { + + }, + "Mit Hint: `{{c1::Berlin::Hauptstadt von DE}}`" : { + + }, + "Noch keine Maske" : { + + }, + "Öffentlich" : { + + }, + "Privat" : { + + }, + "Space" : { + + }, + "Tippe und ziehe auf das Bild, um eine Maske zu erstellen." : { + + }, + "Versuche eine andere Suche oder Sortierung." : { + + }, + "Wählen …" : { + + } + }, "version" : "1.0" -} +} \ No newline at end of file