// // BelegZwischenablage.swift // DataMetric360 // // Holt ein PDF aus der System-Zwischenablage — der Weg, den die Web-API // nicht anbietet. // // WARUM ES DAS BRAUCHT // -------------------- // `navigator.clipboard.read()` gibt aus Datenschutzgründen nur Text, HTML // und Bilder heraus; ein kopiertes PDF taucht dort NIE auf. Der Knopf „Aus // Zwischenablage einfügen" endete deshalb auf dem Telefon zwangsläufig mit // „In der Zwischenablage liegt kein PDF" (gemeldet vom Eigentümer, // 02.09.2026). Das ist keine Lücke in unserem Code, sondern die Grenze der // Schnittstelle. // // `UIPasteboard` kennt diese Grenze nicht: es gibt beliebige Typen heraus, // auch `com.adobe.pdf`. Der Preis ist derselbe wie im Web — iOS zeigt beim // Zugriff seine eigene Einfügen-Rückfrage, und das ist richtig so. // // DREI FORMEN, IN DENEN EIN PDF IN DER ZWISCHENABLAGE LIEGT // --------------------------------------------------------- // 1. Als Daten unter `com.adobe.pdf` — so legen es die meisten Apps ab. // 2. Als Datei-URL — Mail kopiert einen Anhang oft nur als Verweis auf eine // temporäre Kopie. Ohne den zweiten Weg bliebe genau der Fall übrig, für // den der Knopf gedacht ist. // 3. Als Zusage (`NSItemProvider`) — die Datei existiert dann noch gar nicht, // die kopierende App liefert sie erst auf Anforderung. `data(forPasteboardType:)` // gibt dabei nil zurück und `urls` bleibt leer; nur der Anbieter kennt sie. // Das ist Apples eigener, empfohlener Weg und der einzige, der auch eine // Datei aus einer Sandbox einer anderen App zuverlässig herausgibt — Weg 2 // scheitert dort am Lesezugriff. Er ist asynchron, deshalb steht er hier // zuletzt: die beiden schnellen Wege sollen nicht auf ihn warten. // // DIESE DATEI IST VERSIONIERT, ios/ NICHT. // `npx cap add ios` erzeugt den nativen Ordner neu und würde alles darin // verlieren. scripts/ios-teilen-einrichten.mjs kopiert sie bei jedem Bau // wieder hinein und hängt sie an die Sources-Phase des App-Ziels. // import Capacitor import Foundation import UIKit import UniformTypeIdentifiers @objc(BelegZwischenablagePlugin) public class BelegZwischenablagePlugin: CAPPlugin, CAPBridgedPlugin { public let identifier = "BelegZwischenablagePlugin" public let jsName = "BelegZwischenablage" public let pluginMethods: [CAPPluginMethod] = [ CAPPluginMethod(name: "pdfHolen", returnType: CAPPluginReturnPromise) ] /// Liefert `{ base64, name }`, wenn ein PDF in der Zwischenablage liegt — /// sonst ein leeres Ergebnis. Bewusst kein Fehler: „nichts kopiert" ist /// kein Fehlschlag, sondern eine Antwort, und die Oberfläche sagt dazu /// ohnehin das Passende (zwischenablage.ts). @objc func pdfHolen(_ call: CAPPluginCall) { let ablage = UIPasteboard.general if let daten = ablage.data(forPasteboardType: UTType.pdf.identifier) { return call.resolve(["base64": daten.base64EncodedString(), "name": "beleg.pdf"]) } // Mail legt den Anhang oft nur als Verweis ab. for url in ablage.urls ?? [] where url.pathExtension.lowercased() == "pdf" { // Sicherheitsbereich anfordern: die URL zeigt auf eine Datei // außerhalb des eigenen Sandkastens, sonst schlägt das Lesen fehl. let erlaubt = url.startAccessingSecurityScopedResource() defer { if erlaubt { url.stopAccessingSecurityScopedResource() } } if let daten = try? Data(contentsOf: url) { return call.resolve([ "base64": daten.base64EncodedString(), "name": url.lastPathComponent, ]) } } // Dritter Weg: die Datei liegt nur als Zusage vor. let anbieter = ablage.itemProviders.first { $0.hasItemConformingToTypeIdentifier(UTType.pdf.identifier) } guard let anbieter else { return call.resolve([:]) } anbieter.loadDataRepresentation(forTypeIdentifier: UTType.pdf.identifier) { daten, _ in guard let daten, !daten.isEmpty else { // Kein Fehler: die Oberfläche sagt zu einem leeren Ergebnis // ohnehin das Passende (zwischenablage.ts). return call.resolve([:]) } let name = anbieter.suggestedName.map { $0.hasSuffix(".pdf") ? $0 : $0 + ".pdf" } call.resolve([ "base64": daten.base64EncodedString(), "name": name ?? "beleg.pdf", ]) } } }