SwiftNFC.swift (7159B)
1 // MIT License 2 // Copyright © Ming 3 // https://github.com/1998code/SwiftNFC 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 // and associated documentation files (the "Software"), to deal in the Software without restriction, 7 // including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 // sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in all copies or 12 // substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 15 // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 // 20 /** 21 * @author Marc Stibane 22 */ 23 import SwiftUI 24 import CoreNFC 25 26 @available(iOS 15.0, *) 27 public class NFCReader: NSObject, ObservableObject, NFCNDEFReaderSessionDelegate { 28 29 public var startAlert = String(localized: "Hold your iPhone near the tag.") 30 public var endAlert = "" 31 public var msg = String(localized: "Scan to read or Edit here to write...") 32 public var raw = String(localized: "Raw Data available after scan.") 33 34 public var session: NFCNDEFReaderSession? 35 36 public func read() { 37 guard NFCNDEFReaderSession.readingAvailable else { 38 print("Error") 39 return 40 } 41 session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true) 42 session?.alertMessage = self.startAlert 43 session?.begin() 44 } 45 46 public func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) { 47 DispatchQueue.main.async { 48 self.msg = messages.map { 49 $0.records.map { 50 String(decoding: $0.payload, as: UTF8.self) 51 }.joined(separator: "\n") 52 }.joined(separator: " ") 53 54 self.raw = messages.map { 55 $0.records.map { 56 "\($0.typeNameFormat) \(String(decoding:$0.type, as: UTF8.self)) \(String(decoding:$0.identifier, as: UTF8.self)) \(String(decoding: $0.payload, as: UTF8.self))" 57 }.joined(separator: "\n") 58 }.joined(separator: " ") 59 60 61 session.alertMessage = self.endAlert != "" ? self.endAlert : "Read \(messages.count) NDEF Messages, and \(messages[0].records.count) Records." 62 } 63 } 64 65 public func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) { 66 } 67 68 public func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) { 69 print("Session did invalidate with error: \(error)") 70 self.session = nil 71 } 72 } 73 74 public class NFCWriter: NSObject, ObservableObject, NFCNDEFReaderSessionDelegate { 75 76 public var startAlert = String(localized: "Hold your iPhone near the tag.") 77 public var endAlert = "" 78 public var msg = "" 79 public var type = "T" // T=Text - U=URL 80 public var data: Data? = nil 81 82 public var session: NFCNDEFReaderSession? 83 84 public func write(_ data: Data? = nil) { 85 guard NFCNDEFReaderSession.readingAvailable else { 86 print("Error readingAvailable") 87 return 88 } 89 self.data = data 90 session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: false) 91 if let session { 92 session.alertMessage = self.startAlert 93 session.begin() 94 } else { 95 print("Error NFCNDEFReaderSession") 96 } 97 } 98 99 public func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) { 100 print("didDetectNDEFs", messages) 101 } 102 103 public func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) { 104 if tags.count > 1 { 105 let retryInterval = DispatchTimeInterval.milliseconds(500) 106 session.alertMessage = "Detected more than 1 tag. Please try again." 107 DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval, execute: { 108 session.restartPolling() 109 }) 110 return 111 } 112 113 let tag = tags.first! 114 session.connect(to: tag, completionHandler: { (error: Error?) in 115 if nil != error { 116 session.alertMessage = "Unable to connect to tag." 117 session.invalidate() 118 return 119 } 120 121 tag.queryNDEFStatus(completionHandler: { (ndefStatus: NFCNDEFStatus, capacity: Int, error: Error?) in 122 guard error == nil else { 123 session.alertMessage = "Unable to query the status of the tag." 124 session.invalidate() 125 return 126 } 127 128 switch ndefStatus { 129 case .notSupported: 130 session.alertMessage = "Tag is not NDEF compliant." 131 session.invalidate() 132 case .readOnly: 133 session.alertMessage = "Read only tag detected." 134 session.invalidate() 135 case .readWrite: 136 let payload: NFCNDEFPayload? 137 if self.type == "T" { 138 payload = NFCNDEFPayload.init( 139 format: .nfcWellKnown, 140 type: Data("\(self.type)".utf8), 141 identifier: Data(), 142 payload: self.data ?? Data("\(self.msg)".utf8) 143 ) 144 } else { 145 payload = NFCNDEFPayload.wellKnownTypeURIPayload(string: "\(self.msg)") 146 } 147 let message = NFCNDEFMessage(records: [payload].compactMap({ $0 })) 148 tag.writeNDEF(message, completionHandler: { (error: Error?) in 149 if nil != error { 150 session.alertMessage = "Write to tag failed: \(error!)" 151 } else { 152 session.alertMessage = self.endAlert != "" ? self.endAlert : "Write \(self.msg) to tag successful." 153 } 154 session.invalidate() 155 }) 156 @unknown default: 157 session.alertMessage = "Unknown tag status." 158 session.invalidate() 159 } 160 }) 161 }) 162 } 163 164 public func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) { 165 } 166 167 public func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) { 168 print("Session did invalidate with error: \(error)") 169 self.session = nil 170 } 171 }