@iotize/device-com-nfc.cordova 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,103 +1,500 @@
1
- import CoreNFC
2
-
3
- @available(iOS 13.0, *)
4
- class NFCTagReader : NSObject, NFCTagReaderSessionDelegate {
5
-
6
- typealias Completion = (Error?) -> ()
7
- typealias CompletionWithJSONResponse = ([AnyHashable: Any]?, Error?) -> ()
8
-
9
- internal var comSession: NFCTagReaderSession?
10
- internal var tag: NFCTag?
11
-
12
- static var MB_CTRL_DYN : UInt8 = 0x0D
13
-
14
- internal var connectionCompleted : Completion?
15
- internal var onDiscoverCompletion : CompletionWithJSONResponse?
16
-
17
-
18
- static var DELAY : UInt32 = 1000; // timeout resolution in millionths of second
19
- static var NB_MAX_RETRY : Int = 50;
20
-
21
- override init() {
22
- super.init()
23
- }
24
-
25
- func initSession( pollingOption: NFCTagReaderSession.PollingOption, alertMessage: String, completed: @escaping (Error?)->() ) {
26
- return self.initSession(pollingOption: pollingOption, alertMessage: alertMessage, completed: completed, onDiscover: nil)
27
- }
28
-
29
- func initSession( pollingOption: NFCTagReaderSession.PollingOption, alertMessage: String, completed: @escaping (Error?)->(), onDiscover: CompletionWithJSONResponse? ) {
30
- connectionCompleted = completed
31
- onDiscoverCompletion = onDiscover
32
-
33
- if NFCNDEFReaderSession.readingAvailable {
34
- comSession = NFCTagReaderSession(pollingOption: pollingOption, delegate: self, queue: nil)
35
- comSession?.alertMessage = alertMessage
36
- comSession?.begin()
37
- } else {
38
- completed(NFCReaderError.readerTransceiveErrorSessionInvalidated as? Error)
39
- }
40
- }
41
-
42
- func invalidateSession( message :String) {
43
- comSession?.alertMessage = message
44
- comSession?.invalidate()
45
- tag = nil
46
- }
47
-
48
- func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
49
- // If necessary, you may perform additional operations on session start.
50
- // At this point RF polling is enabled.
51
- print( "tagReaderSessionDidBecomeActive" )
52
- }
53
-
54
- func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
55
- // If necessary, you may handle the error. Note session is no longer valid.
56
- // You must create a new session to restart RF polling.
57
- print( "tagReaderSession:didInvalidateWithError - \(error)" )
58
- connectionCompleted?(error)
59
- self.comSession = nil
60
- self.tag = nil
61
- }
62
-
63
- func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
64
- print( "tagReaderSession:didDectectTag" )
65
- guard let session = self.comSession else {
66
- return;
67
- }
68
- if tags.count > 1 {
69
- // Restart polling in 500 milliseconds.
70
- let retryInterval = DispatchTimeInterval.milliseconds(500)
71
- session.alertMessage = "More than 1 Tap is detected. Please remove all tags and try again."
72
- DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval, execute: {
73
- session.restartPolling()
74
- })
75
- return
76
- }
77
-
78
- guard let tag = tags.first else {
79
- return;
80
- }
81
-
82
- // Connect to tag
83
-
84
- session.connect(to: tag) { [weak self] (error: Error?) in
85
- guard let strongSelf = self else {
86
- return;
87
- }
88
-
89
- if error != nil {
90
- let error = NFCReaderError( NFCReaderError.readerTransceiveErrorTagNotConnected )
91
- strongSelf.invalidateSession( message: error.localizedDescription )
92
- strongSelf.connectionCompleted?(error)
93
- return
94
- }
95
- print( "connected to tag" )
96
- strongSelf.tag = tag
97
- strongSelf.connectionCompleted?(nil)
98
- }
99
- }
100
-
101
-
102
-
103
- }
1
+ import CoreNFC
2
+
3
+ @available(iOS 13.0, *)
4
+ class NFCTagReader : NSObject, NFCTagReaderSessionDelegate {
5
+
6
+ private var plugin: NfcPlugin
7
+
8
+ typealias Completion = (Error?) -> ()
9
+ typealias CompletionWithJSONResponse = ([AnyHashable: Any]?, Error?) -> ()
10
+
11
+ internal var comSession: NFCTagReaderSession?
12
+ internal var tag: NFCTag?
13
+
14
+ static var MB_CTRL_DYN : UInt8 = 0x0D
15
+
16
+ internal var connectionCompleted : Completion?
17
+ internal var initSessionCompletion: Completion?
18
+ internal var onDiscoverCompletion : CompletionWithJSONResponse?
19
+
20
+
21
+ static var DELAY : UInt32 = 1000; // timeout resolution in millionths of second
22
+ static var NB_MAX_RETRY : Int = 50;
23
+
24
+ init(plugin: NfcPlugin) {
25
+ self.plugin = plugin
26
+ super.init()
27
+ }
28
+
29
+ func initSession( pollingOption: NFCTagReaderSession.PollingOption, alertMessage: String, completed: Completion? ) {
30
+ return self.initSession(pollingOption: pollingOption, alertMessage: alertMessage, completed: completed, onDiscover: nil)
31
+ }
32
+
33
+ func initSession( pollingOption: NFCTagReaderSession.PollingOption, alertMessage: String, onDiscover: CompletionWithJSONResponse?) {
34
+ return self.initSession(pollingOption: pollingOption, alertMessage: alertMessage, completed: nil, onDiscover: onDiscover)
35
+ }
36
+
37
+ func initSession( pollingOption: NFCTagReaderSession.PollingOption, alertMessage: String, completed: Completion?, onDiscover: CompletionWithJSONResponse? ) {
38
+ connectionCompleted = completed
39
+ onDiscoverCompletion = onDiscover
40
+
41
+ if NFCNDEFReaderSession.readingAvailable {
42
+ comSession = NFCTagReaderSession(pollingOption: pollingOption, delegate: self, queue: nil)
43
+ comSession?.alertMessage = alertMessage
44
+ comSession?.begin()
45
+ } else {
46
+ completed?(NFCReaderError.readerTransceiveErrorSessionInvalidated as? Error)
47
+ }
48
+ }
49
+
50
+ func initSession( pollingOption: NFCTagReaderSession.PollingOption, alertMessage: String, initSessionCompletion: Completion?, onDiscover: CompletionWithJSONResponse? ) {
51
+ self.initSessionCompletion = initSessionCompletion
52
+ return self.initSession(pollingOption: pollingOption, alertMessage: alertMessage, completed: nil, onDiscover: onDiscover)
53
+ }
54
+
55
+ func invalidateSession( message :String) {
56
+ comSession?.alertMessage = message
57
+ comSession?.invalidate()
58
+ tag = nil
59
+ }
60
+
61
+ func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
62
+ // If necessary, you may perform additional operations on session start.
63
+ // At this point RF polling is enabled.
64
+ if let actualInitSessionCompletion = initSessionCompletion {
65
+ actualInitSessionCompletion(nil)
66
+ initSessionCompletion = nil //do not keep session completion
67
+ }
68
+ printNFC( "tagReaderSessionDidBecomeActive" )
69
+ }
70
+
71
+ func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
72
+ // If necessary, you may handle the error. Note session is no longer valid.
73
+ // You must create a new session to restart RF polling.
74
+ printNFC( "tagReaderSession:didInvalidateWithError - \(error)" )
75
+ if let actualInitSessionCompletion = initSessionCompletion {
76
+ actualInitSessionCompletion(error)
77
+ initSessionCompletion = nil //do not keep session completion
78
+ }
79
+ connectionCompleted?(error)
80
+ clear()
81
+ self.comSession = nil
82
+ }
83
+
84
+ func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
85
+ printNFC( "tagReaderSession:didDectectTag" )
86
+ guard let session = self.comSession else {
87
+ return;
88
+ }
89
+ if tags.count > 1 {
90
+ // Restart polling in 500 milliseconds.
91
+ let retryInterval = DispatchTimeInterval.milliseconds(500)
92
+ session.alertMessage = "More than 1 Tap is detected. Please remove all tags and try again."
93
+ DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval, execute: {
94
+ session.restartPolling()
95
+ })
96
+ return
97
+
98
+ }
99
+
100
+ guard let tag = tags.first else {
101
+ return;
102
+ }
103
+
104
+ self.tag = tag
105
+
106
+ if let onDiscover = self.onDiscoverCompletion {
107
+
108
+ Task {
109
+ self.onDiscoverCompletion?(await self.createJSON(tag: tag), nil)
110
+ }
111
+
112
+ }
113
+
114
+ if let connectionCompletion = self.connectionCompleted {
115
+ connect(tech: nil, connectionCompletion: connectionCompletion)
116
+ }
117
+
118
+ }
119
+
120
+ //full transparent mode
121
+
122
+ @available(iOS 14.0, *)
123
+ func transceiveRaw(request: Data, completed: @escaping (Data?, Error?) -> (), nbTry: Int = NB_MAX_RETRY) {
124
+ guard (request.count >= 2) else {
125
+ completed(nil, NFCReaderError(NFCReaderError.readerErrorInvalidParameterLength))
126
+ return
127
+ }
128
+
129
+ switch self.tag {
130
+ case .iso15693(let iso15693Tag):
131
+
132
+ let flags = Int(request[0])
133
+ let commandCode = Int(request[1])
134
+ let dataToSend = request.dropFirst(2)
135
+
136
+ printNFC("TRANSCEIVE RAW SEND \(request.hexEncodedString())")
137
+ printNFC("TRANSCEIVE RAW SEND ON ISO15TAG TRY \(nbTry), SESSION READY \(String(describing: comSession?.isReady))")
138
+
139
+ iso15693Tag.sendRequest(requestFlags: flags, commandCode: commandCode, data: dataToSend, resultHandler: {(result: Result<(NFCISO15693ResponseFlag, Data?), any Error>) in
140
+
141
+ printNFC("SEND_REQUEST_CALLBACK")
142
+ switch result {
143
+ case .success((let flag, let data)):
144
+ let firstByteBuffer = withUnsafeBytes(of: flag.rawValue) { Data($0)}
145
+ var resultData = Data(firstByteBuffer)
146
+ if let nonNilData = data {
147
+ resultData.append(nonNilData)
148
+ }
149
+
150
+ //Last delay to let ST25DV "breathe"
151
+ //usleep(1000*NFCTagReader.DELAY)
152
+
153
+ printNFC("TRANSCEIVE RAW RETURN \(resultData.hexEncodedString())")
154
+ completed(resultData, nil)
155
+ return
156
+ case .failure(let error):
157
+ printNFC("TRANSCEIVE RAW ERROR \(error.localizedDescription), TRY \(nbTry)")
158
+ if (nbTry >= 0) {
159
+ usleep(NFCTagReader.DELAY)
160
+ return self.transceiveRaw(request: request, completed: completed, nbTry: nbTry - 1)
161
+ }
162
+
163
+ completed(nil, error)
164
+ return
165
+ }
166
+ })
167
+ break
168
+ case .iso7816(let iso7816Tag):
169
+ printNFC("TRANSCEIVE RAW SEND ON iso7816Tag TRY \(nbTry), SESSION READY \(String(describing: comSession?.isReady))")
170
+
171
+ guard let requestAPDU = NFCISO7816APDU(data: request) else {
172
+ completed(nil, NFCReaderError(NFCReaderError.readerErrorInvalidParameterLength) )
173
+ return
174
+ }
175
+
176
+ iso7816Tag.sendCommand(apdu: requestAPDU, resultHandler: {(result: Result<NFCISO7816ResponseAPDU, any Error>) in
177
+
178
+ printNFC("SEND_REQUEST_CALLBACK")
179
+ switch result {
180
+ case .success(let response):
181
+ var resultData = Data()
182
+
183
+ if let nonNilData = response.payload {
184
+ resultData.append(nonNilData)
185
+ }
186
+
187
+ resultData.append(response.statusWord1)
188
+ resultData.append(response.statusWord2)
189
+
190
+
191
+ printNFC("TRANSCEIVE RAW RETURN \(resultData.hexEncodedString())")
192
+ completed(resultData, nil)
193
+ return
194
+ case .failure(let error):
195
+ printNFC("TRANSCEIVE RAW ERROR \(error.localizedDescription), TRY \(nbTry)")
196
+ if (nbTry >= 0) {
197
+ usleep(NFCTagReader.DELAY)
198
+ return self.transceiveRaw(request: request, completed: completed, nbTry: nbTry - 1)
199
+ }
200
+ completed(nil, error)
201
+ return
202
+ }
203
+ })
204
+ default:
205
+ completed(nil, NFCReaderError(NFCReaderError.readerTransceiveErrorTagConnectionLost))
206
+ return
207
+ }
208
+
209
+
210
+ }
211
+
212
+ func connect(tech: NfcTech?, connectionCompletion: Completion?) {
213
+
214
+ // Check if a tag has been detected
215
+ guard let tag = self.tag else {
216
+ connectionCompletion?(NFCReaderError( NFCReaderError.readerTransceiveErrorTagNotConnected ))
217
+ return
218
+ }
219
+ // Connect to tag
220
+ if let actualTech = tech {
221
+ switch actualTech {
222
+ case NfcTech.NfcV:
223
+ guard case .iso15693(_) = self.tag else {
224
+ connectionCompletion?(NFCReaderError( NFCReaderError.readerErrorInvalidParameter ))
225
+ return
226
+ }
227
+ break
228
+ case NfcTech.IsoDep:
229
+ fallthrough
230
+ case NfcTech.NfcA:
231
+ fallthrough
232
+ case NfcTech.NfcB:
233
+ guard case .iso7816(_) = self.tag else {
234
+ connectionCompletion?(NFCReaderError( NFCReaderError.readerErrorInvalidParameter ))
235
+ return
236
+ }
237
+ break
238
+ default:
239
+ connectionCompletion?(NFCReaderError( NFCReaderError.readerErrorInvalidParameter ))
240
+ return
241
+ }
242
+ }
243
+ self.connectionCompleted = connectionCompletion
244
+
245
+ self.comSession?.connect(to: tag) { [weak self] (error: Error?) in
246
+ guard let strongSelf = self else {
247
+ return;
248
+ }
249
+
250
+ if error != nil {
251
+ let error = NFCReaderError( NFCReaderError.readerTransceiveErrorTagNotConnected )
252
+ strongSelf.invalidateSession( message: error.localizedDescription )
253
+ strongSelf.connectionCompleted?(error)
254
+ strongSelf.connectionCompleted = nil
255
+ return
256
+ }
257
+ printNFC( "connected to tag" )
258
+ strongSelf.tag = tag
259
+ strongSelf.connectionCompleted?(nil)
260
+ }
261
+ }
262
+
263
+ func clear() {
264
+ self.tag = nil
265
+ self.connectionCompleted = nil
266
+ }
267
+ }
268
+
269
+ @available(iOS 13.0, *)
270
+ extension NFCTagReader {
271
+
272
+
273
+
274
+ func transceiveTap(request: Data, completed: @escaping (Data?, Error?)->()){
275
+
276
+
277
+ checkMBEnabled( completed: { ( error: Error?) in
278
+ if nil != error {
279
+ self.invalidateSession( message: error?.localizedDescription ?? "Error" )
280
+ completed( nil, error )
281
+ return
282
+ }
283
+ printNFC( "Com enabled" )
284
+ self.sendRequest( request: request,
285
+ nbTry: NFCTagReader.NB_MAX_RETRY,
286
+ completed: { ( response: Data?, error: Error?) in
287
+ if nil != error {
288
+ self.invalidateSession( message: error?.localizedDescription ?? "Error" )
289
+ completed( nil, error )
290
+ return
291
+ }
292
+ completed(response, nil)
293
+ })
294
+ })
295
+
296
+ }
297
+
298
+ func sendRequest(request: Data, nbTry: Int, completed: @escaping (Data?, Error?)->() ) {
299
+ guard case let .iso15693(iso15693tag) = self.tag else {
300
+ let error = NFCReaderError( NFCReaderError.readerTransceiveErrorTagNotConnected )
301
+ invalidateSession( message: error.localizedDescription )
302
+ completed(nil, error )
303
+ return;
304
+ }
305
+
306
+ if (nbTry <= 0){
307
+ let error = NFCReaderError( NFCReaderError.readerTransceiveErrorRetryExceeded )
308
+ invalidateSession( message: error.localizedDescription )
309
+ completed(nil, error )
310
+ return
311
+ }
312
+
313
+ var parameters = Data( bytes:[request.count - 1], count: 1 )
314
+ parameters.append(request)
315
+ printNFC( "Send - \(parameters.hexEncodedString())" )
316
+
317
+ iso15693tag.customCommand(requestFlags: [.highDataRate],
318
+ customCommandCode: 0xAA,
319
+ customRequestParameters: parameters,
320
+ completionHandler: { (response: Data?, error: Error?) in
321
+ if nil != error {
322
+ usleep(NFCTagReader.DELAY)
323
+ self.sendRequest( request: request, nbTry: nbTry - 1, completed: completed )
324
+ return
325
+ }
326
+ usleep(NFCTagReader.DELAY * 10) // free ST25DV for SPI
327
+ self.readResponse( nbTry: nbTry , completed: completed)
328
+ })
329
+
330
+ }
331
+
332
+ func readResponse( nbTry: Int, completed: @escaping (Data?, Error?)->() ) {
333
+
334
+ guard case let .iso15693(iso15693tag) = self.tag else {
335
+ let error = NFCReaderError( NFCReaderError.readerTransceiveErrorTagNotConnected )
336
+ invalidateSession( message: error.localizedDescription )
337
+ completed( nil, error )
338
+ return;
339
+ }
340
+
341
+ //We have tried enough timeout and return
342
+ if (nbTry <= 0){
343
+ printNFC( "Read Abandonned" )
344
+ let error = NFCReaderError( NFCReaderError.readerTransceiveErrorRetryExceeded )
345
+ invalidateSession( message: error.localizedDescription )
346
+ completed( nil, error )
347
+ return;
348
+ }
349
+
350
+ printNFC( "Read \(nbTry)" )
351
+
352
+ //check Mailbox
353
+ iso15693tag.customCommand(requestFlags: [.highDataRate],
354
+ customCommandCode: 0xAD,
355
+ customRequestParameters: Data(bytes: [UInt8(0x0D)], count: 1),
356
+ completionHandler: { (response: Data, error: Error?) in
357
+ if nil != error {
358
+ usleep(NFCTagReader.DELAY)
359
+ self.readResponse( nbTry: nbTry - 1, completed: completed )
360
+ return
361
+ }
362
+
363
+ printNFC( "Read response" )
364
+
365
+ if ( (response.count >= 1) && ( (response[0]&0x1) != 0 ) && ( (response[0]&0x2) != 0 )){
366
+
367
+ printNFC( "Read Value - \(Data(response).hexEncodedString())" )
368
+ iso15693tag.customCommand(requestFlags: [.highDataRate],
369
+ customCommandCode: 0xAC,
370
+ customRequestParameters: Data(bytes: [UInt8(0), UInt8(0)], count: 2),
371
+ completionHandler: { (response: Data, error: Error?) in
372
+ if nil != error {
373
+ self.invalidateSession( message: error?.localizedDescription ?? "Error" )
374
+ completed( nil, error )
375
+ return
376
+ }
377
+ printNFC( "got Value - \(Data(response).hexEncodedString())" )
378
+ completed(response,nil)
379
+ return
380
+ })
381
+
382
+ }
383
+ else {
384
+ usleep(NFCTagReader.DELAY)
385
+ self.readResponse( nbTry: nbTry - 1, completed: completed )
386
+ }
387
+
388
+ })
389
+
390
+
391
+ }
392
+
393
+ func checkMBEnabled(completed: @escaping (Error?)->()) {
394
+ guard case let .iso15693(iso15693tag) = self.tag else {
395
+ let error = NFCReaderError( NFCReaderError.readerTransceiveErrorTagNotConnected )
396
+ invalidateSession( message: error.localizedDescription )
397
+ completed( error )
398
+ return;
399
+ }
400
+
401
+ //Read Config
402
+ iso15693tag.customCommand(requestFlags: [.highDataRate],
403
+ customCommandCode: 0xAD,
404
+ customRequestParameters: Data(bytes: [UInt8(0x0D)], count: 1),
405
+ completionHandler: { (response: Data, error: Error?) in
406
+ if nil != error {
407
+ self.invalidateSession( message: error?.localizedDescription ?? "Error" )
408
+ completed(error)
409
+ return
410
+ }
411
+
412
+ if ( response.count == 0) {
413
+ let error = NFCReaderError( NFCReaderError.readerTransceiveErrorTagResponseError )
414
+ self.invalidateSession( message: error.localizedDescription )
415
+ completed( error )
416
+ return
417
+ }
418
+
419
+
420
+ let current = response[0];
421
+
422
+ //We should reset mailbox
423
+ if ( (current != 0x41) && (current != 0x81) ) {
424
+
425
+ //disable
426
+ iso15693tag.customCommand(requestFlags: [.highDataRate],
427
+ customCommandCode: 0xAE,
428
+ customRequestParameters: Data(bytes: [UInt8(0x0D), UInt8(0x00)], count: 2),
429
+ completionHandler: { (response: Data, error: Error?) in
430
+ if nil != error {
431
+ self.invalidateSession( message: error?.localizedDescription ?? "Error" )
432
+ completed( error )
433
+ return
434
+ }
435
+
436
+ //enable
437
+ iso15693tag.customCommand(requestFlags: [.highDataRate],
438
+ customCommandCode: 0xAE,
439
+ customRequestParameters: Data(bytes: [UInt8(0x0D), UInt8(0x01)], count: 2),
440
+ completionHandler: { (response: Data, error: Error?) in
441
+ if nil != error {
442
+ self.invalidateSession( message: error?.localizedDescription ?? "Error" )
443
+ completed( error )
444
+ return
445
+ }
446
+
447
+ completed(nil)
448
+ return
449
+
450
+ })
451
+ })
452
+ }
453
+ //We are ok to go
454
+ else
455
+ {
456
+ completed(nil)
457
+ return
458
+ }
459
+
460
+ })
461
+ }
462
+
463
+ func sendTap( request: String, completed: @escaping (Data?,Error?)->() ) {
464
+
465
+ guard NFCNDEFReaderSession.readingAvailable else {
466
+ let error = NFCReaderError( NFCReaderError.readerErrorUnsupportedFeature )
467
+ invalidateSession( message: error.localizedDescription )
468
+ completed( nil, error )
469
+ return
470
+ }
471
+
472
+ guard comSession != nil && comSession!.isReady else {
473
+ let error = NFCReaderError( NFCReaderError.readerTransceiveErrorTagNotConnected )
474
+ invalidateSession( message: error.localizedDescription )
475
+ completed( nil, error )
476
+ return
477
+ }
478
+
479
+ let requestData : Data = request.dataFromHexString()
480
+ printNFC( "Transceive - \(requestData.hexEncodedString())" )
481
+ transceiveTap(request: requestData,
482
+ completed: { ( response: Data?, error: Error?) in
483
+ if nil != error {
484
+ self.invalidateSession( message: error?.localizedDescription ?? "Error" )
485
+ completed( nil, error )
486
+ return
487
+ }
488
+ else {
489
+ completed( response, nil)
490
+ return
491
+ }
492
+ })
493
+ }
494
+
495
+ func createJSON(tag: NFCTag) async -> [AnyHashable: Any] {
496
+ try? await self.comSession?.connect(to: tag)
497
+ return await tag.toJSON(isTapDiscoveryEnabled: self.plugin.isTapDiscoveryEnabled())
498
+ }
499
+ }
500
+