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>
144 lines
5.2 KiB
Swift
144 lines
5.2 KiB
Swift
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)
|
||
}
|
||
}
|
||
}
|