NFC: Entitlement TAG statt NDEF, Tag-Session statt NDEF-Session
Der Upload nach App Store Connect scheiterte an der Entitlement-Pruefung: "NDEF is disallowed" und "TAG is missing in the entitlement". Apple hat den Wert NDEF zugunsten von TAG abgeloest; mit dem SDK von iOS 26/27 wird er abgelehnt. TAG ist der Wert fuer NFCTagReaderSession, deshalb laeuft der Lese- und Schreibweg jetzt darueber statt ueber NFCNDEFReaderSession. Die NDEF-Befehle bleiben dabei vollstaendig erhalten: alle vier Tag-Protokolle (MiFare, ISO7816, ISO15693, FeliCa) erben laut SDK-Header von NFCNDEFTag, also gibt es weiterhin queryNDEFStatus, readNDEF, writeNDEF und writeLock. Gepollt wird iso14443 (die ueblichen NTAG-Aufkleber) und iso15693 (Waeschetags mancher Hersteller). Die eigene Klasse hiess NFCTag und kollidierte damit mit dem gleichnamigen CoreNFC-Protokoll, das der neue Weg braucht - sie heisst jetzt NFCDienst. Info.plist enthaelt ausserdem die Neuformatierung durch Xcode (Schluessel sortiert, Kommentare entfernt). Inhaltlich unveraendert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
09a763a823
commit
140b81798c
+61
-93
@@ -2,113 +2,30 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<!-- Von Xcode aus den Build-Einstellungen gefuellt -->
|
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||||
<!-- Deutsch als Sprache der App: Sonst zeichnet iOS DatePicker und
|
<array>
|
||||||
Monatsnamen auf Englisch ("10. Dec 2027"). -->
|
<string>com.scarriffle.vorrania.refresh</string>
|
||||||
|
</array>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>de</string>
|
<string>de</string>
|
||||||
<key>CFBundleLocalizations</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<array>
|
<string>Vorrania</string>
|
||||||
<string>de</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
|
<key>CFBundleLocalizations</key>
|
||||||
|
<array>
|
||||||
|
<string>de</string>
|
||||||
|
</array>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>$(PRODUCT_NAME)</string>
|
<string>$(PRODUCT_NAME)</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>Vorrania</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1</string>
|
|
||||||
|
|
||||||
<!-- App-Store-Kategorie. Muss hier stehen: Das Projekt nutzt ein eigenes
|
|
||||||
Info.plist (GENERATE_INFOPLIST_FILE = NO), deshalb wird die
|
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType-Build-Einstellung NICHT in das
|
|
||||||
gebaute Info.plist uebernommen. -->
|
|
||||||
<key>LSApplicationCategoryType</key>
|
|
||||||
<string>public.app-category.utilities</string>
|
|
||||||
|
|
||||||
<!-- Export-Compliance: Die App nutzt nur die Standard-Verschluesselung des
|
|
||||||
Systems (HTTPS ueber Apples Betriebssystem) und faellt damit unter die
|
|
||||||
Ausnahme. Mit diesem Schluessel entfaellt die "App Encryption
|
|
||||||
Documentation"-Rueckfrage bei jedem Upload nach App Store Connect. -->
|
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
|
||||||
<false/>
|
|
||||||
|
|
||||||
<key>UILaunchScreen</key>
|
|
||||||
<dict/>
|
|
||||||
|
|
||||||
<!-- Am iPhone bewusst nur Hochkant - damit sich beim Drehen nichts
|
|
||||||
verstellt. Am iPad verlangt das System alle Ausrichtungen, solange die
|
|
||||||
App nicht auf Vollbild besteht. -->
|
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
</array>
|
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
||||||
</array>
|
|
||||||
|
|
||||||
<!-- Kamera für den Barcode-Scan -->
|
|
||||||
<key>NSCameraUsageDescription</key>
|
|
||||||
<string>Die Kamera wird zum Scannen von Barcodes beim Ein- und Auslagern verwendet.</string>
|
|
||||||
|
|
||||||
<!-- NFC für die eingenähten Tags an Einzelstücken -->
|
|
||||||
<key>NFCReaderUsageDescription</key>
|
|
||||||
<string>NFC wird zum Lesen und Beschreiben der Tags an Einzelstücken verwendet.</string>
|
|
||||||
|
|
||||||
<!-- Lokaler Server ohne HTTPS (Heimnetz) -->
|
|
||||||
<key>NSAppTransportSecurity</key>
|
|
||||||
<dict>
|
|
||||||
<key>NSAllowsLocalNetworking</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
|
|
||||||
<!-- Hintergrund-Aktualisierung: die App frischt gelegentlich den Plan der
|
|
||||||
Ablauf-Benachrichtigungen auf. Zeitpunkt bestimmt iOS. -->
|
|
||||||
<key>UIBackgroundModes</key>
|
|
||||||
<array>
|
|
||||||
<string>fetch</string>
|
|
||||||
</array>
|
|
||||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
|
||||||
<array>
|
|
||||||
<string>com.scarriffle.vorrania.refresh</string>
|
|
||||||
</array>
|
|
||||||
|
|
||||||
<!-- Schnellaktionen beim langen Druck auf das App-Symbol -->
|
|
||||||
<key>UIApplicationShortcutItems</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>UIApplicationShortcutItemType</key>
|
|
||||||
<string>com.scarriffle.vorrania.checkin</string>
|
|
||||||
<key>UIApplicationShortcutItemTitle</key>
|
|
||||||
<string>Einlagern</string>
|
|
||||||
<key>UIApplicationShortcutItemIconType</key>
|
|
||||||
<string>UIApplicationShortcutIconTypeAdd</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>UIApplicationShortcutItemType</key>
|
|
||||||
<string>com.scarriffle.vorrania.checkout</string>
|
|
||||||
<key>UIApplicationShortcutItemTitle</key>
|
|
||||||
<string>Auslagern</string>
|
|
||||||
<key>UIApplicationShortcutItemIconType</key>
|
|
||||||
<string>UIApplicationShortcutIconTypeRemove</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
|
|
||||||
<!-- vorrania://checkin bzw. vorrania://checkout -->
|
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
@@ -120,5 +37,56 @@
|
|||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
<false/>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.utilities</string>
|
||||||
|
<key>NFCReaderUsageDescription</key>
|
||||||
|
<string>NFC wird zum Lesen und Beschreiben der Tags an Einzelstücken verwendet.</string>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsLocalNetworking</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Die Kamera wird zum Scannen von Barcodes beim Ein- und Auslagern verwendet.</string>
|
||||||
|
<key>UIApplicationShortcutItems</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>UIApplicationShortcutItemIconType</key>
|
||||||
|
<string>UIApplicationShortcutIconTypeAdd</string>
|
||||||
|
<key>UIApplicationShortcutItemTitle</key>
|
||||||
|
<string>Einlagern</string>
|
||||||
|
<key>UIApplicationShortcutItemType</key>
|
||||||
|
<string>com.scarriffle.vorrania.checkin</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UIApplicationShortcutItemIconType</key>
|
||||||
|
<string>UIApplicationShortcutIconTypeRemove</string>
|
||||||
|
<key>UIApplicationShortcutItemTitle</key>
|
||||||
|
<string>Auslagern</string>
|
||||||
|
<key>UIApplicationShortcutItemType</key>
|
||||||
|
<string>com.scarriffle.vorrania.checkout</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>fetch</string>
|
||||||
|
</array>
|
||||||
|
<key>UILaunchScreen</key>
|
||||||
|
<dict/>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ struct ItemEditView: View {
|
|||||||
LabeledContent("UID", value: item.uid)
|
LabeledContent("UID", value: item.uid)
|
||||||
// Für Kleidung: ein eingenähtes NFC-Tag hält, was ein aufgeklebter
|
// Für Kleidung: ein eingenähtes NFC-Tag hält, was ein aufgeklebter
|
||||||
// QR nicht übersteht. Auf dem iPad ausgeblendet – dort gibt es kein NFC.
|
// QR nicht übersteht. Auf dem iPad ausgeblendet – dort gibt es kein NFC.
|
||||||
if NFCTag.verfuegbar {
|
if NFCDienst.verfuegbar {
|
||||||
Button { nfcShown = true } label: {
|
Button { nfcShown = true } label: {
|
||||||
Label("NFC-Tag beschreiben", systemImage: "wave.3.right")
|
Label("NFC-Tag beschreiben", systemImage: "wave.3.right")
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-24
@@ -50,17 +50,25 @@ func einzelstueckCode(aus roh: String, nackteUidErlauben: Bool = false) -> Strin
|
|||||||
/// aussen sieht das hier wie ein normales `await` aus, damit die Views genauso
|
/// aussen sieht das hier wie ein normales `await` aus, damit die Views genauso
|
||||||
/// aussehen wie beim Kamera-Scan.
|
/// aussehen wie beim Kamera-Scan.
|
||||||
///
|
///
|
||||||
|
/// **Warum `NFCTagReaderSession` und nicht `NFCNDEFReaderSession`:** Seit dem
|
||||||
|
/// SDK von iOS 26/27 lehnt die App-Store-Prüfung den Entitlement-Wert `NDEF` ab
|
||||||
|
/// („NDEF is disallowed") und verlangt `TAG`. `TAG` ist der Wert für diese
|
||||||
|
/// Sitzungsart. Die NDEF-Befehle bleiben trotzdem verfügbar: alle vier
|
||||||
|
/// Tag-Protokolle (MiFare, ISO7816, ISO15693, FeliCa) erben von `NFCNDEFTag`,
|
||||||
|
/// also gibt es weiterhin `queryNDEFStatus`, `readNDEF`, `writeNDEF` und
|
||||||
|
/// `writeLock`.
|
||||||
|
///
|
||||||
/// Gelesen wird nur mit geöffneter App. iOS startet Apps beim Antippen im
|
/// Gelesen wird nur mit geöffneter App. iOS startet Apps beim Antippen im
|
||||||
/// Hintergrund ausschliesslich über https-Universal-Links auf einer beanspruchten
|
/// Hintergrund ausschliesslich über https-Universal-Links auf einer beanspruchten
|
||||||
/// Domain – eigene Schemes ignoriert es dabei stillschweigend. Eine feste Domain
|
/// Domain – eigene Schemes ignoriert es dabei stillschweigend. Eine feste Domain
|
||||||
/// wäre für einen selbst gehosteten Dienst aber gerade der falsche Weg (siehe
|
/// wäre für einen selbst gehosteten Dienst aber gerade der falsche Weg (siehe
|
||||||
/// Kommentar an `Router.handle(url:)`).
|
/// Kommentar an `Router.handle(url:)`).
|
||||||
final class NFCTag: NSObject {
|
final class NFCDienst: NSObject {
|
||||||
|
|
||||||
/// Ob das Gerät NFC lesen kann. Auf dem iPad immer `false` – die App läuft
|
/// Ob das Gerät NFC lesen kann. Auf dem iPad immer `false` – die App läuft
|
||||||
/// dort ebenfalls (`TARGETED_DEVICE_FAMILY "1,2"`), deshalb muss jede
|
/// dort ebenfalls (`TARGETED_DEVICE_FAMILY "1,2"`), deshalb muss jede
|
||||||
/// NFC-Bedienung daran hängen.
|
/// NFC-Bedienung daran hängen.
|
||||||
static var verfuegbar: Bool { NFCNDEFReaderSession.readingAvailable }
|
static var verfuegbar: Bool { NFCTagReaderSession.readingAvailable }
|
||||||
|
|
||||||
enum Fehler: LocalizedError {
|
enum Fehler: LocalizedError {
|
||||||
case nichtVerfuegbar
|
case nichtVerfuegbar
|
||||||
@@ -97,38 +105,42 @@ final class NFCTag: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let queue = DispatchQueue(label: "com.scarriffle.vorrania.nfc")
|
private let queue = DispatchQueue(label: "com.scarriffle.vorrania.nfc")
|
||||||
private var session: NFCNDEFReaderSession?
|
private var session: NFCTagReaderSession?
|
||||||
private var auftrag: Auftrag = .lesen
|
private var auftrag: Auftrag = .lesen
|
||||||
private var weiter: CheckedContinuation<String, Error>?
|
private var weiter: CheckedContinuation<String, Error>?
|
||||||
/// Die Sitzung hält sich selbst am Leben, solange sie läuft – sonst gibt der
|
/// Die Sitzung hält sich selbst am Leben, solange sie läuft – sonst gibt der
|
||||||
/// Aufrufer die Instanz frei und das Delegate meldet sich nie.
|
/// Aufrufer die Instanz frei und das Delegate meldet sich nie.
|
||||||
private var festhalten: NFCTag?
|
private var festhalten: NFCDienst?
|
||||||
|
|
||||||
// MARK: Öffentlich
|
// MARK: Öffentlich
|
||||||
|
|
||||||
/// Liest ein Tag und gibt seinen Inhalt roh zurück.
|
/// Liest ein Tag und gibt seinen Inhalt roh zurück.
|
||||||
static func lesen() async throws -> String {
|
static func lesen() async throws -> String {
|
||||||
try await NFCTag().starten(.lesen, hinweis: "Halte das Tag an die Oberseite des iPhones.")
|
try await NFCDienst().starten(.lesen)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Beschreibt ein Tag. `sperren` macht es **unwiderruflich** schreibgeschützt.
|
/// Beschreibt ein Tag. `sperren` macht es **unwiderruflich** schreibgeschützt.
|
||||||
static func schreiben(_ nutzlast: String, sperren: Bool = false) async throws {
|
static func schreiben(_ nutzlast: String, sperren: Bool = false) async throws {
|
||||||
_ = try await NFCTag().starten(
|
_ = try await NFCDienst().starten(.schreiben(nutzlast: nutzlast, sperren: sperren))
|
||||||
.schreiben(nutzlast: nutzlast, sperren: sperren),
|
|
||||||
hinweis: "Halte das Tag an die Oberseite des iPhones."
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Ablauf
|
// MARK: Ablauf
|
||||||
|
|
||||||
private func starten(_ auftrag: Auftrag, hinweis: String) async throws -> String {
|
private func starten(_ auftrag: Auftrag) async throws -> String {
|
||||||
guard Self.verfuegbar else { throw Fehler.nichtVerfuegbar }
|
guard Self.verfuegbar else { throw Fehler.nichtVerfuegbar }
|
||||||
self.auftrag = auftrag
|
self.auftrag = auftrag
|
||||||
return try await withCheckedThrowingContinuation { fortsetzung in
|
return try await withCheckedThrowingContinuation { fortsetzung in
|
||||||
|
// iso14443 deckt die üblichen NTAG-/MIFARE-Aufkleber ab, iso15693 die
|
||||||
|
// Wäschetags, die manche Hersteller stattdessen verbauen.
|
||||||
|
guard let s = NFCTagReaderSession(
|
||||||
|
pollingOption: [.iso14443, .iso15693], delegate: self, queue: queue
|
||||||
|
) else {
|
||||||
|
fortsetzung.resume(throwing: Fehler.nichtVerfuegbar)
|
||||||
|
return
|
||||||
|
}
|
||||||
weiter = fortsetzung
|
weiter = fortsetzung
|
||||||
festhalten = self
|
festhalten = self
|
||||||
let s = NFCNDEFReaderSession(delegate: self, queue: queue, invalidateAfterFirstRead: false)
|
s.alertMessage = "Halte das Tag an die Oberseite des iPhones."
|
||||||
s.alertMessage = hinweis
|
|
||||||
session = s
|
session = s
|
||||||
s.begin()
|
s.begin()
|
||||||
}
|
}
|
||||||
@@ -230,38 +242,51 @@ final class NFCTag: NSObject {
|
|||||||
if let text = record.wellKnownTypeTextPayload().0 { return text }
|
if let text = record.wellKnownTypeTextPayload().0 { return text }
|
||||||
return String(data: record.payload, encoding: .utf8)
|
return String(data: record.payload, encoding: .utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Alle vier Tag-Arten erben von `NFCNDEFTag` – die NDEF-Befehle gibt es
|
||||||
|
/// also unabhängig davon, welche Technik das Tag verwendet.
|
||||||
|
private static func ndef(aus tag: NFCTag) -> NFCNDEFTag? {
|
||||||
|
switch tag {
|
||||||
|
case let .miFare(t): return t
|
||||||
|
case let .iso7816(t): return t
|
||||||
|
case let .iso15693(t): return t
|
||||||
|
case let .feliCa(t): return t
|
||||||
|
@unknown default: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - NFCNDEFReaderSessionDelegate
|
// MARK: - NFCTagReaderSessionDelegate
|
||||||
|
|
||||||
extension NFCTag: NFCNDEFReaderSessionDelegate {
|
extension NFCDienst: NFCTagReaderSessionDelegate {
|
||||||
|
|
||||||
/// Wird nicht aufgerufen, solange `didDetect tags:` vorhanden ist – Core NFC
|
func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {}
|
||||||
/// nimmt dann den Weg über die Tags. Das Protokoll verlangt die Methode
|
|
||||||
/// trotzdem.
|
|
||||||
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {}
|
|
||||||
|
|
||||||
func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
|
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
|
||||||
guard tags.count == 1, let tag = tags.first else {
|
guard tags.count == 1, let ersteser = tags.first else {
|
||||||
session.alertMessage = "Mehrere Tags erkannt. Bitte nur eines anhalten."
|
session.alertMessage = "Mehrere Tags erkannt. Bitte nur eines anhalten."
|
||||||
session.restartPolling()
|
session.restartPolling()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
session.connect(to: tag) { fehler in
|
guard let ndef = Self.ndef(aus: ersteser) else {
|
||||||
|
fertig(.failure(Fehler.nichtBeschreibbar))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
session.connect(to: ersteser) { fehler in
|
||||||
if let fehler {
|
if let fehler {
|
||||||
self.fertig(.failure(Fehler.system(fehler.localizedDescription)))
|
self.fertig(.failure(Fehler.system(fehler.localizedDescription)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch self.auftrag {
|
switch self.auftrag {
|
||||||
case .lesen:
|
case .lesen:
|
||||||
self.lesen(tag)
|
self.lesen(ndef)
|
||||||
case let .schreiben(nutzlast, sperren):
|
case let .schreiben(nutzlast, sperren):
|
||||||
self.schreiben(tag, nutzlast: nutzlast, sperren: sperren)
|
self.schreiben(ndef, nutzlast: nutzlast, sperren: sperren)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
|
func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
|
||||||
// Hat der Vorgang schon ein Ergebnis geliefert, ist das hier nur noch das
|
// Hat der Vorgang schon ein Ergebnis geliefert, ist das hier nur noch das
|
||||||
// Aufräumen danach.
|
// Aufräumen danach.
|
||||||
guard weiter != nil else { return }
|
guard weiter != nil else { return }
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ struct NFCSchreibenView: View {
|
|||||||
erfolg = false
|
erfolg = false
|
||||||
defer { laeuft = false }
|
defer { laeuft = false }
|
||||||
do {
|
do {
|
||||||
try await NFCTag.schreiben(nutzlast, sperren: sperren)
|
try await NFCDienst.schreiben(nutzlast, sperren: sperren)
|
||||||
erfolg = true
|
erfolg = true
|
||||||
} catch NFCTag.Fehler.abgebrochen {
|
} catch NFCDienst.Fehler.abgebrochen {
|
||||||
// Kein Fehler, sondern eine Entscheidung – nichts anzeigen.
|
// Kein Fehler, sondern eine Entscheidung – nichts anzeigen.
|
||||||
} catch {
|
} catch {
|
||||||
fehler = error.localizedDescription
|
fehler = error.localizedDescription
|
||||||
@@ -93,7 +93,7 @@ struct NFCLesenButton: View {
|
|||||||
@State private var laeuft = false
|
@State private var laeuft = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if NFCTag.verfuegbar {
|
if NFCDienst.verfuegbar {
|
||||||
Button {
|
Button {
|
||||||
Task { await lesen() }
|
Task { await lesen() }
|
||||||
} label: {
|
} label: {
|
||||||
@@ -107,7 +107,7 @@ struct NFCLesenButton: View {
|
|||||||
laeuft = true
|
laeuft = true
|
||||||
defer { laeuft = false }
|
defer { laeuft = false }
|
||||||
do {
|
do {
|
||||||
let roh = try await NFCTag.lesen()
|
let roh = try await NFCDienst.lesen()
|
||||||
// Ein selbst beschriebenes Tag darf auch die nackte UID tragen – anders
|
// Ein selbst beschriebenes Tag darf auch die nackte UID tragen – anders
|
||||||
// als bei der Kamera droht hier keine Verwechslung mit einem Barcode.
|
// als bei der Kamera droht hier keine Verwechslung mit einem Barcode.
|
||||||
guard let uid = einzelstueckCode(aus: roh, nackteUidErlauben: true) else {
|
guard let uid = einzelstueckCode(aus: roh, nackteUidErlauben: true) else {
|
||||||
@@ -115,7 +115,7 @@ struct NFCLesenButton: View {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
onUid(uid)
|
onUid(uid)
|
||||||
} catch NFCTag.Fehler.abgebrochen {
|
} catch NFCDienst.Fehler.abgebrochen {
|
||||||
// Nutzer hat abgebrochen – keine Meldung.
|
// Nutzer hat abgebrochen – keine Meldung.
|
||||||
} catch {
|
} catch {
|
||||||
onFehler(error.localizedDescription)
|
onFehler(error.localizedDescription)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.developer.nfc.readersession.formats</key>
|
<key>com.apple.developer.nfc.readersession.formats</key>
|
||||||
<array>
|
<array>
|
||||||
<string>NDEF</string>
|
<string>TAG</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
+5
-1
@@ -63,5 +63,9 @@ targets:
|
|||||||
entitlements:
|
entitlements:
|
||||||
path: Sources/Vorrania.entitlements
|
path: Sources/Vorrania.entitlements
|
||||||
properties:
|
properties:
|
||||||
|
# "TAG", nicht "NDEF": Die App-Store-Pruefung lehnt NDEF seit dem
|
||||||
|
# SDK von iOS 26/27 ab ("NDEF is disallowed") und verlangt TAG. Deshalb
|
||||||
|
# laeuft der Code ueber NFCTagReaderSession; die NDEF-Befehle gibt es
|
||||||
|
# dort weiterhin, weil alle Tag-Protokolle von NFCNDEFTag erben.
|
||||||
com.apple.developer.nfc.readersession.formats:
|
com.apple.developer.nfc.readersession.formats:
|
||||||
- NDEF
|
- TAG
|
||||||
|
|||||||
Reference in New Issue
Block a user