Files
ScarriffleandClaude Opus 5 8c752e3d1f App: NFC-Fehler kurz statt "Stack Error", Knopf weg nach Erfolg
Apples localizedDescription kam unveraendert durch und lieferte beim
Beschreiben von Tags wiederholt "Stack Error" - ein Text, der niemandem
sagt, was zu tun ist. Jetzt uebersetzt NFCDienst.kurz die CoreNFC-Codes
in kurze Meldungen ("Tag zu frueh entfernt.", "NFC gerade belegt."); ein
unbekannter Code behaelt seine Nummer, damit der fehlende Fall beim
naechsten Auftreten feststeht.

Die Meldung steht jetzt auch in Apples Systemblatt: im Fehlerfall wurde
die Sitzung wortlos beendet, deshalb las man sie erst danach in der App.

Nach einem erfolgreichen Schreibvorgang verschwinden Knopf und
Schreibschutz-Schalter - sie standen vorher weiter da und waren aktiv,
ein weiterer Tipp hat das Tag ein zweites Mal beschrieben. Stattdessen
steht dort jetzt "Fertig".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-20 21:41:35 +02:00

144 lines
5.2 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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/<UID>`.
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)")
}
// Nach dem Erfolg hat das Tag seinen Inhalt. Knopf und Schalter
// stehenzulassen hiesse, es noch einmal beschreiben zu wollen.
if !erfolg {
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(sperren ? "Tag beschrieben und gesperrt." : "Tag beschrieben.",
systemImage: "checkmark.circle")
.foregroundStyle(.green)
// Hier ist nichts mehr zu tun; „Fertig" gehoert deshalb in
// den Inhalt und nicht nur oben in die Leiste. Ohne das
// stuende man im Anlege-Ablauf vor einem fast leeren Blatt.
Button("Fertig") { if let onFertig { onFertig() } else { dismiss() } }
.buttonStyle(.borderedProminent)
}
}
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)
}
}
}