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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
04f8c1fccd
commit
8c752e3d1f
@@ -99,6 +99,48 @@ final class NFCDienst: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// Kurzfassung eines CoreNFC-Fehlers.
|
||||
///
|
||||
/// Apples ``localizedDescription`` ist hier unbrauchbar – sie liefert Dinge
|
||||
/// wie „Stack Error", die niemandem sagen, was zu tun ist. Fast alle Faelle
|
||||
/// sind voruebergehend: Tag zu frueh weg, Funkkontakt verloren, Stack belegt.
|
||||
///
|
||||
/// Ein unbekannter Code behaelt seine Nummer. Damit steht beim naechsten
|
||||
/// Auftreten sofort fest, welcher Fall hier noch fehlt.
|
||||
static func kurz(_ fehler: Error) -> String {
|
||||
guard let code = (fehler as? NFCReaderError)?.code else {
|
||||
return "Fehlgeschlagen."
|
||||
}
|
||||
switch code {
|
||||
case .readerTransceiveErrorTagConnectionLost:
|
||||
return "Tag zu früh entfernt."
|
||||
case .readerTransceiveErrorRetryExceeded:
|
||||
return "Keine Antwort vom Tag."
|
||||
case .readerTransceiveErrorTagResponseError:
|
||||
return "Tag antwortet fehlerhaft."
|
||||
case .readerTransceiveErrorTagNotConnected:
|
||||
return "Kein Tag verbunden."
|
||||
case .readerSessionInvalidationErrorSessionTimeout:
|
||||
return "Zeit abgelaufen."
|
||||
case .readerSessionInvalidationErrorSystemIsBusy:
|
||||
return "NFC gerade belegt."
|
||||
case .readerSessionInvalidationErrorSessionTerminatedUnexpectedly:
|
||||
return "Sitzung abgebrochen."
|
||||
case .ndefReaderSessionErrorTagUpdateFailure:
|
||||
return "Schreiben fehlgeschlagen."
|
||||
case .ndefReaderSessionErrorTagNotWritable:
|
||||
return "Tag nicht beschreibbar."
|
||||
case .readerErrorRadioDisabled:
|
||||
return "NFC ist aus."
|
||||
case .readerErrorSecurityViolation:
|
||||
return "NFC nicht erlaubt."
|
||||
case .readerErrorUnsupportedFeature:
|
||||
return "Tag-Typ nicht unterstützt."
|
||||
default:
|
||||
return "Fehlgeschlagen (Code \(code.rawValue))."
|
||||
}
|
||||
}
|
||||
|
||||
private enum Auftrag {
|
||||
case lesen
|
||||
case schreiben(nutzlast: String, sperren: Bool)
|
||||
@@ -155,8 +197,10 @@ final class NFCDienst: NSObject {
|
||||
if let meldung {
|
||||
session?.alertMessage = meldung
|
||||
session?.invalidate()
|
||||
} else if case .failure = ergebnis {
|
||||
session?.invalidate()
|
||||
} else if case let .failure(fehler) = ergebnis {
|
||||
// Mit Text: Ohne ihn schliesst sich Apples Blatt wortlos und die
|
||||
// Meldung steht erst danach in der App.
|
||||
session?.invalidate(errorMessage: fehler.localizedDescription)
|
||||
}
|
||||
session = nil
|
||||
fortsetzung.resume(with: ergebnis)
|
||||
@@ -166,7 +210,7 @@ final class NFCDienst: NSObject {
|
||||
private func lesen(_ tag: NFCNDEFTag) {
|
||||
tag.readNDEF { nachricht, fehler in
|
||||
if let fehler {
|
||||
self.fertig(.failure(Fehler.system(fehler.localizedDescription)))
|
||||
self.fertig(.failure(Fehler.system(Self.kurz(fehler))))
|
||||
return
|
||||
}
|
||||
guard let text = nachricht?.records.compactMap(Self.text(aus:)).first, !text.isEmpty else {
|
||||
@@ -187,7 +231,7 @@ final class NFCDienst: NSObject {
|
||||
|
||||
tag.queryNDEFStatus { status, kapazitaet, fehler in
|
||||
if let fehler {
|
||||
self.fertig(.failure(Fehler.system(fehler.localizedDescription)))
|
||||
self.fertig(.failure(Fehler.system(Self.kurz(fehler))))
|
||||
return
|
||||
}
|
||||
switch status {
|
||||
@@ -210,7 +254,7 @@ final class NFCDienst: NSObject {
|
||||
|
||||
tag.writeNDEF(nachricht) { fehler in
|
||||
if let fehler {
|
||||
self.fertig(.failure(Fehler.system(fehler.localizedDescription)))
|
||||
self.fertig(.failure(Fehler.system(Self.kurz(fehler))))
|
||||
return
|
||||
}
|
||||
guard sperren else {
|
||||
@@ -224,8 +268,8 @@ final class NFCDienst: NSObject {
|
||||
// Das gehört gesagt, statt den ganzen Vorgang als
|
||||
// gescheitert auszugeben.
|
||||
self.fertig(.failure(Fehler.system(
|
||||
"Tag beschrieben, aber das Sperren ist fehlgeschlagen: "
|
||||
+ fehler.localizedDescription)))
|
||||
"Tag beschrieben, Sperren fehlgeschlagen: "
|
||||
+ Self.kurz(fehler))))
|
||||
return
|
||||
}
|
||||
AudioServicesPlaySystemSound(1057)
|
||||
@@ -274,7 +318,7 @@ extension NFCDienst: NFCTagReaderSessionDelegate {
|
||||
}
|
||||
session.connect(to: ersteser) { fehler in
|
||||
if let fehler {
|
||||
self.fertig(.failure(Fehler.system(fehler.localizedDescription)))
|
||||
self.fertig(.failure(Fehler.system(Self.kurz(fehler))))
|
||||
return
|
||||
}
|
||||
switch self.auftrag {
|
||||
@@ -293,6 +337,6 @@ extension NFCDienst: NFCTagReaderSessionDelegate {
|
||||
let abbruch = (error as? NFCReaderError)?.code
|
||||
== .readerSessionInvalidationErrorUserCanceled
|
||||
self.session = nil
|
||||
fertig(.failure(abbruch ? Fehler.abgebrochen : Fehler.system(error.localizedDescription)))
|
||||
fertig(.failure(abbruch ? Fehler.abgebrochen : Fehler.system(Self.kurz(error))))
|
||||
}
|
||||
}
|
||||
|
||||
+26
-16
@@ -33,30 +33,40 @@ struct NFCSchreibenView: View {
|
||||
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.")
|
||||
// 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() }
|
||||
Section {
|
||||
Button(laeuft ? "Tag anhalten…" : "Tag beschreiben") {
|
||||
Task { await schreiben() }
|
||||
}
|
||||
.disabled(laeuft)
|
||||
}
|
||||
.disabled(laeuft)
|
||||
}
|
||||
|
||||
if erfolg {
|
||||
Section {
|
||||
Label("Tag beschrieben.", systemImage: "checkmark.circle")
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user