@@ -76,8 +76,12 @@ final class NFCReader: NSObject {
76
76
private var sendRetryCount = Constants . retryCount
77
77
private var startRetryCount = Constants . startRetryCount
78
78
private let pollingOption : NFCTagReaderSession . PollingOption
79
- private var sessionDidBecomeActiveTimestamp : Date = . init( )
80
-
79
+ private var sessionDidBecomeActiveTS : Date = . init( )
80
+ private var firstConnectionTS : Date ? = nil
81
+ private var tagConnectionTS : Date ? = nil
82
+ private var isBeingStopped = false
83
+ private var stoppedError : TangemSdkError ? = nil
84
+
81
85
/// Starting from iOS 17 is no longer possible to invoke restart polling after 20 seconds from first connection on some devices
82
86
private lazy var shouldReduceRestartPolling : Bool = {
83
87
if #available( iOS 17 , * ) , NFCUtils . isBrokenRestartPollingDevice {
@@ -87,8 +91,6 @@ final class NFCReader: NSObject {
87
91
return false
88
92
} ( )
89
93
90
- private var firstConnectionTimestamp : Date ? = nil
91
-
92
94
private lazy var nfcUtils : NFCUtils = . init( )
93
95
94
96
init ( pollingOption: NFCTagReaderSession . PollingOption = [ . iso14443] ) {
@@ -108,6 +110,11 @@ extension NFCReader: CardReader {
108
110
var alertMessage : String {
109
111
get { return _alertMessage ?? " " }
110
112
set {
113
+ if isBeingStopped {
114
+ Log . nfc ( " Session is being stopped. Skip alert message. " )
115
+ return
116
+ }
117
+
111
118
readerSession? . alertMessage = newValue
112
119
_alertMessage = newValue
113
120
}
@@ -122,6 +129,11 @@ extension NFCReader: CardReader {
122
129
invalidatedWithError = nil
123
130
cancelled = false
124
131
connectedTag = nil
132
+ isBeingStopped = false
133
+ stoppedError = nil
134
+ tagConnectionTS = nil
135
+ firstConnectionTS = nil
136
+ sessionDidBecomeActiveTS = Date ( )
125
137
126
138
let alertMessage = message ?? " view_delegate_scan_description " . localized
127
139
_alertMessage = alertMessage
@@ -140,7 +152,7 @@ extension NFCReader: CardReader {
140
152
. filter { [ weak self] _ in
141
153
guard let self else { return false }
142
154
143
- let distanceToSessionActive = self . sessionDidBecomeActiveTimestamp . distance ( to: Date ( ) )
155
+ let distanceToSessionActive = self . sessionDidBecomeActiveTS . distance ( to: Date ( ) )
144
156
if !self . isSessionReady || distanceToSessionActive < 1 {
145
157
Log . nfc ( " Filter out cancelled event " )
146
158
return false
@@ -207,16 +219,17 @@ extension NFCReader: CardReader {
207
219
. dropFirst ( )
208
220
. sink { [ weak self] isSilent in
209
221
guard let self, let session = self . readerSession,
210
- session. isReady else {
222
+ session. isReady,
223
+ !self . isBeingStopped else {
211
224
return
212
225
}
213
226
214
- if self . shouldReduceRestartPolling, let firstConnectionTimestamp = self . firstConnectionTimestamp {
215
- let interval = Date ( ) . timeIntervalSince ( firstConnectionTimestamp )
227
+ if self . shouldReduceRestartPolling, let firstConnectionTS = self . firstConnectionTS {
228
+ let interval = Date ( ) . timeIntervalSince ( firstConnectionTS )
216
229
Log . nfc ( " Restart polling interval is: \( interval) " )
217
230
218
231
// 20 is too much because of time inaccuracy
219
- if interval >= 18 {
232
+ if interval >= 16 {
220
233
Log . nfc ( " Ignore restart polling " )
221
234
return
222
235
}
@@ -247,6 +260,15 @@ extension NFCReader: CardReader {
247
260
}
248
261
249
262
func stopSession( with errorMessage: String ? = nil ) {
263
+ guard ( readerSession? . isReady == true ) else {
264
+ return
265
+ }
266
+
267
+ if isBeingStopped {
268
+ return
269
+ }
270
+
271
+ isBeingStopped = true
250
272
Log . nfc ( " Stop reader session invoked " )
251
273
stopTimers ( )
252
274
if let errorMessage = errorMessage {
@@ -256,6 +278,11 @@ extension NFCReader: CardReader {
256
278
}
257
279
}
258
280
281
+ func stopSession( with error: Error ) {
282
+ stoppedError = error. toTangemSdkError ( )
283
+ stopSession ( with: error. localizedDescription)
284
+ }
285
+
259
286
func restartPolling( silent: Bool ) {
260
287
restartPollingPublisher. send ( silent)
261
288
}
@@ -264,6 +291,13 @@ extension NFCReader: CardReader {
264
291
/// - Parameter apdu: serialized apdu
265
292
/// - Parameter completion: result with ResponseApdu or NFCError otherwise
266
293
func sendPublisher( apdu: CommandApdu ) -> AnyPublisher < ResponseApdu , TangemSdkError > {
294
+ if isBeingStopped {
295
+ Log . nfc ( " Session is being stopped. Skip sending. " )
296
+ return Empty ( completeImmediately: false )
297
+ . setFailureType ( to: TangemSdkError . self)
298
+ . eraseToAnyPublisher ( )
299
+ }
300
+
267
301
Log . nfc ( " Send publisher invoked " )
268
302
269
303
return Just ( ( ) )
@@ -289,7 +323,7 @@ extension NFCReader: CardReader {
289
323
return Fail ( error: TangemSdkError . unsupportedCommand) . eraseToAnyPublisher ( )
290
324
} //todo: handle tag lost
291
325
292
- let requestTimestamp = Date ( )
326
+ let requestTS = Date ( )
293
327
Log . apdu ( " SEND --> \( apdu) " )
294
328
return iso7816tag
295
329
. sendCommandPublisher ( cApdu: apdu)
@@ -307,8 +341,8 @@ extension NFCReader: CardReader {
307
341
. eraseToAnyPublisher ( )
308
342
}
309
343
310
- let distance = requestTimestamp . distance ( to: Date ( ) )
311
- let isDistanceTooLong = distance > Constants . timestampTolerance
344
+ let distance = requestTS . distance ( to: Date ( ) )
345
+ let isDistanceTooLong = distance > Constants . requestToleranceTS
312
346
if isDistanceTooLong || self . sendRetryCount <= 0 { //retry to fix old device issues
313
347
Log . nfc ( " Invoke restart polling on error " )
314
348
self . sendRetryCount = Constants . retryCount
@@ -337,7 +371,7 @@ extension NFCReader: CardReader {
337
371
}
338
372
339
373
private func start( ) {
340
- firstConnectionTimestamp = nil
374
+ firstConnectionTS = nil
341
375
readerSession? . invalidate ( ) //Important! We must keep invalidate/begin in balance after start retries
342
376
readerSession = NFCTagReaderSession ( pollingOption: self . pollingOption, delegate: self , queue: queue) !
343
377
readerSession!. alertMessage = _alertMessage!
@@ -371,12 +405,11 @@ extension NFCReader: CardReader {
371
405
. TimerPublisher ( interval: Constants . tagTimeout, tolerance: 0 , runLoop: RunLoop . main, mode: . common)
372
406
. autoconnect ( )
373
407
. receive ( on: queue)
374
- . filter { [ weak self] _ in self ? . idleTimerCancellable != nil }
375
408
. sink { [ weak self] _ in
376
409
guard let self else { return }
377
410
378
411
Log . nfc ( " Stop by tag timer " )
379
- self . stopSession ( with: TangemSdkError . nfcTimeout. localizedDescription )
412
+ self . stopSession ( with: TangemSdkError . nfcTimeout)
380
413
self . tagTimerCancellable = nil
381
414
}
382
415
}
@@ -390,7 +423,7 @@ extension NFCReader: CardReader {
390
423
guard let self else { return }
391
424
392
425
Log . nfc ( " Stop by session timer " )
393
- self . stopSession ( with: TangemSdkError . nfcTimeout. localizedDescription )
426
+ self . stopSession ( with: TangemSdkError . nfcTimeout)
394
427
self . sessionTimerCancellable = nil
395
428
}
396
429
}
@@ -400,6 +433,7 @@ extension NFCReader: CardReader {
400
433
. TimerPublisher ( interval: Constants . idleTimeout, runLoop: RunLoop . main, mode: . common)
401
434
. autoconnect ( )
402
435
. receive ( on: queue)
436
+ . filter { [ weak self] _ in !( self ? . isBeingStopped ?? true ) }
403
437
. sink { [ weak self] _ in
404
438
guard let self else { return }
405
439
@@ -470,36 +504,47 @@ extension NFCReader: CardReader {
470
504
@available ( iOS 13 . 0 , * )
471
505
extension NFCReader : NFCTagReaderSessionDelegate {
472
506
func tagReaderSessionDidBecomeActive( _ session: NFCTagReaderSession ) {
473
- sessionDidBecomeActiveTimestamp = Date ( )
507
+ sessionDidBecomeActiveTS = Date ( )
474
508
isSessionReady = true
475
509
}
476
510
477
511
func tagReaderSession( _ session: NFCTagReaderSession , didInvalidateWithError error: Error ) {
478
- Log . nfc ( " NFC Session did invalidate with: \( error. localizedDescription) " )
512
+ Log . nfc ( " NFC Session did invalidate with NFC error : \( error. localizedDescription) " )
479
513
if nfcStuckTimerCancellable == nil { //handle stuck retries ios14
480
- invalidatedWithError = TangemSdkError . parse ( error as! NFCReaderError )
514
+ invalidatedWithError = stoppedError ?? TangemSdkError . parse ( error as! NFCReaderError )
515
+ }
516
+
517
+ if let tagConnectionTS {
518
+ let currentTS = Date ( )
519
+ Log . nfc ( " Session time is: \( currentTS. timeIntervalSince ( sessionDidBecomeActiveTS) ) " )
520
+ Log . nfc ( " Tag time is: \( currentTS. timeIntervalSince ( tagConnectionTS) ) " )
481
521
}
482
522
}
483
523
484
524
func tagReaderSession( _ session: NFCTagReaderSession , didDetect tags: [ NFCTag ] ) {
485
525
Log . nfc ( " NFC tag detected: \( tags) " )
486
526
487
- if firstConnectionTimestamp == nil {
488
- firstConnectionTimestamp = Date ( )
489
- }
490
-
491
527
let nfcTag = tags. first!
492
528
493
529
sessionConnectCancellable = session. connectPublisher ( tag: nfcTag)
494
530
. receive ( on: queue)
495
531
. sink { [ weak self] completion in
532
+ guard let self else { return }
533
+
496
534
switch completion {
497
535
case . failure:
498
- self ? . restartPolling ( silent: false )
536
+ self . restartPolling ( silent: false )
499
537
case . finished:
500
538
break
501
539
}
502
- self ? . sessionConnectCancellable = nil
540
+ self . sessionConnectCancellable = nil
541
+
542
+ self . tagConnectionTS = Date ( )
543
+
544
+ if self . firstConnectionTS == nil {
545
+ self . firstConnectionTS = self . tagConnectionTS
546
+ }
547
+
503
548
} receiveValue: { [ weak self] _ in
504
549
self ? . tagDidConnect ( nfcTag)
505
550
}
@@ -516,7 +561,7 @@ extension NFCReader {
516
561
static let nfcStuckTimeout = 1.0
517
562
static let retryCount = 20
518
563
static let startRetryCount = 5
519
- static let timestampTolerance = 1.0
564
+ static let requestToleranceTS = 1.0
520
565
static let searchTagTimeout = 1.0
521
566
}
522
567
}
0 commit comments