import SwiftUI /// Ein NFC-Tag mit dem Link auf ein Einzelstück beschreiben. /// /// Gedacht für Kleidung: Das Tag wird eingenäht und ersetzt den QR, der die /// Wäsche nicht überstehen würde. Auf das Tag kommt genau das, was auch im QR /// steht – `vorrania://i/`. struct NFCSchreibenView: View { let item: Item /// Was „Fertig" tun soll. Ohne Angabe schliesst die Ansicht sich selbst – /// das ist der Fall, wenn sie aus der Stueck-Bearbeitung heraus aufgerufen /// wird. Haengt sie als Schritt an einem Ablauf (gleich nach dem Anlegen), /// muss stattdessen der ganze Ablauf enden. var onFertig: (() -> Void)? = nil @Environment(\.dismiss) private var dismiss @State private var sperren = false @State private var laeuft = false @State private var erfolg = false @State private var fehler: String? private var nutzlast: String { einzelstueckLink(fuer: item.uid) } var body: some View { Form { Section { if let name = item.productName { LabeledContent("Artikel", value: name) } LabeledContent("UID", value: item.uid) } footer: { Text("Auf das Tag wird geschrieben: \(nutzlast)") } Section { Toggle("Tag dauerhaft schreibschützen", isOn: $sperren) } footer: { if sperren { Text("Das lässt sich nicht rückgängig machen. Das Tag gehört danach " + "für immer zu diesem Einzelstück und kann nicht neu beschrieben werden.") .foregroundStyle(.orange) } else { Text("Ohne Schreibschutz kannst du das Tag später einem anderen " + "Einzelstück zuweisen.") } } Section { Button(laeuft ? "Tag anhalten…" : "Tag beschreiben") { Task { await schreiben() } } .disabled(laeuft) } if erfolg { Section { Label("Tag beschrieben.", systemImage: "checkmark.circle") .foregroundStyle(.green) } } if let fehler { Section { Text(fehler).foregroundStyle(.red).font(.callout) } } } .navigationTitle("NFC-Tag") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button("Fertig") { if let onFertig { onFertig() } else { dismiss() } } } } } private func schreiben() async { laeuft = true fehler = nil erfolg = false defer { laeuft = false } do { try await NFCDienst.schreiben(nutzlast, sperren: sperren) erfolg = true } catch NFCDienst.Fehler.abgebrochen { // Kein Fehler, sondern eine Entscheidung – nichts anzeigen. } catch { fehler = error.localizedDescription } } } /// Knopf, der ein NFC-Tag liest und die gefundene Einzelstück-UID zurückgibt. /// /// Blendet sich auf Geräten ohne NFC (iPad) selbst aus, damit die umgebenden /// Ansichten das nicht jedes Mal selbst prüfen müssen. struct NFCLesenButton: View { var titel: String = "NFC-Tag lesen" var onUid: (String) -> Void var onFehler: (String) -> Void @State private var laeuft = false var body: some View { if NFCDienst.verfuegbar { Button { Task { await lesen() } } label: { Label(laeuft ? "Tag anhalten…" : titel, systemImage: "wave.3.right") } .disabled(laeuft) } } private func lesen() async { laeuft = true defer { laeuft = false } do { let roh = try await NFCDienst.lesen() // Ein selbst beschriebenes Tag darf auch die nackte UID tragen – anders // als bei der Kamera droht hier keine Verwechslung mit einem Barcode. guard let uid = einzelstueckCode(aus: roh, nackteUidErlauben: true) else { onFehler("Auf dem Tag steht kein Einzelstück: \(roh)") return } onUid(uid) } catch NFCDienst.Fehler.abgebrochen { // Nutzer hat abgebrochen – keine Meldung. } catch { onFehler(error.localizedDescription) } } }