Skip to content

Commit 7ddce67

Browse files
authored
Merge pull request #338 from tangem/IOS-5674_fix_reader
IOS-5674 Fix reader
2 parents 5d860b5 + b6fadf9 commit 7ddce67

File tree

3 files changed

+73
-28
lines changed

3 files changed

+73
-28
lines changed

TangemSdk/TangemSdk/Common/Core/CardSession.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ public class CardSession {
246246
/// - Parameter error: The error to show
247247
public func stop(error: Error, completion: (() -> Void)?) {
248248
Log.session("Stop session")
249-
reader.stopSession(with: error.localizedDescription)
249+
reader.stopSession(with: error)
250250
sessionDidStop(completion: completion)
251251
}
252252

@@ -321,7 +321,6 @@ public class CardSession {
321321
.sink(receiveCompletion: {[weak self] readerCompletion in
322322
self?.sendSubscription = []
323323
if case let .failure(error) = readerCompletion {
324-
Log.error(error)
325324
completion(.failure(error))
326325
}
327326
}, receiveValue: {[weak self] responseApdu in

TangemSdk/TangemSdk/Common/NFC/CardReader.swift

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public protocol CardReader: AnyObject {
4040
func resumeSession()
4141
func stopSession(with errorMessage: String?)
4242
func pauseSession(with errorMessage: String?)
43+
func stopSession(with error: Error)
4344
func sendPublisher(apdu: CommandApdu) -> AnyPublisher<ResponseApdu, TangemSdkError>
4445
func restartPolling(silent: Bool)
4546
}

TangemSdk/TangemSdk/Common/NFC/NFCReader.swift

+71-26
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,12 @@ final class NFCReader: NSObject {
7676
private var sendRetryCount = Constants.retryCount
7777
private var startRetryCount = Constants.startRetryCount
7878
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+
8185
/// Starting from iOS 17 is no longer possible to invoke restart polling after 20 seconds from first connection on some devices
8286
private lazy var shouldReduceRestartPolling: Bool = {
8387
if #available(iOS 17, *), NFCUtils.isBrokenRestartPollingDevice {
@@ -87,8 +91,6 @@ final class NFCReader: NSObject {
8791
return false
8892
}()
8993

90-
private var firstConnectionTimestamp: Date? = nil
91-
9294
private lazy var nfcUtils: NFCUtils = .init()
9395

9496
init(pollingOption: NFCTagReaderSession.PollingOption = [.iso14443]) {
@@ -108,6 +110,11 @@ extension NFCReader: CardReader {
108110
var alertMessage: String {
109111
get { return _alertMessage ?? "" }
110112
set {
113+
if isBeingStopped {
114+
Log.nfc("Session is being stopped. Skip alert message.")
115+
return
116+
}
117+
111118
readerSession?.alertMessage = newValue
112119
_alertMessage = newValue
113120
}
@@ -122,6 +129,11 @@ extension NFCReader: CardReader {
122129
invalidatedWithError = nil
123130
cancelled = false
124131
connectedTag = nil
132+
isBeingStopped = false
133+
stoppedError = nil
134+
tagConnectionTS = nil
135+
firstConnectionTS = nil
136+
sessionDidBecomeActiveTS = Date()
125137

126138
let alertMessage = message ?? "view_delegate_scan_description".localized
127139
_alertMessage = alertMessage
@@ -140,7 +152,7 @@ extension NFCReader: CardReader {
140152
.filter{[weak self] _ in
141153
guard let self else { return false }
142154

143-
let distanceToSessionActive = self.sessionDidBecomeActiveTimestamp.distance(to: Date())
155+
let distanceToSessionActive = self.sessionDidBecomeActiveTS.distance(to: Date())
144156
if !self.isSessionReady || distanceToSessionActive < 1 {
145157
Log.nfc("Filter out cancelled event")
146158
return false
@@ -207,16 +219,17 @@ extension NFCReader: CardReader {
207219
.dropFirst()
208220
.sink {[weak self] isSilent in
209221
guard let self, let session = self.readerSession,
210-
session.isReady else {
222+
session.isReady,
223+
!self.isBeingStopped else {
211224
return
212225
}
213226

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)
216229
Log.nfc("Restart polling interval is: \(interval)")
217230

218231
// 20 is too much because of time inaccuracy
219-
if interval >= 18 {
232+
if interval >= 16 {
220233
Log.nfc("Ignore restart polling")
221234
return
222235
}
@@ -247,6 +260,15 @@ extension NFCReader: CardReader {
247260
}
248261

249262
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
250272
Log.nfc("Stop reader session invoked")
251273
stopTimers()
252274
if let errorMessage = errorMessage {
@@ -256,6 +278,11 @@ extension NFCReader: CardReader {
256278
}
257279
}
258280

281+
func stopSession(with error: Error) {
282+
stoppedError = error.toTangemSdkError()
283+
stopSession(with: error.localizedDescription)
284+
}
285+
259286
func restartPolling(silent: Bool) {
260287
restartPollingPublisher.send(silent)
261288
}
@@ -264,6 +291,13 @@ extension NFCReader: CardReader {
264291
/// - Parameter apdu: serialized apdu
265292
/// - Parameter completion: result with ResponseApdu or NFCError otherwise
266293
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+
267301
Log.nfc("Send publisher invoked")
268302

269303
return Just(())
@@ -289,7 +323,7 @@ extension NFCReader: CardReader {
289323
return Fail(error: TangemSdkError.unsupportedCommand).eraseToAnyPublisher()
290324
} //todo: handle tag lost
291325

292-
let requestTimestamp = Date()
326+
let requestTS = Date()
293327
Log.apdu("SEND --> \(apdu)")
294328
return iso7816tag
295329
.sendCommandPublisher(cApdu: apdu)
@@ -307,8 +341,8 @@ extension NFCReader: CardReader {
307341
.eraseToAnyPublisher()
308342
}
309343

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
312346
if isDistanceTooLong || self.sendRetryCount <= 0 { //retry to fix old device issues
313347
Log.nfc("Invoke restart polling on error")
314348
self.sendRetryCount = Constants.retryCount
@@ -337,7 +371,7 @@ extension NFCReader: CardReader {
337371
}
338372

339373
private func start() {
340-
firstConnectionTimestamp = nil
374+
firstConnectionTS = nil
341375
readerSession?.invalidate() //Important! We must keep invalidate/begin in balance after start retries
342376
readerSession = NFCTagReaderSession(pollingOption: self.pollingOption, delegate: self, queue: queue)!
343377
readerSession!.alertMessage = _alertMessage!
@@ -371,12 +405,11 @@ extension NFCReader: CardReader {
371405
.TimerPublisher(interval: Constants.tagTimeout, tolerance: 0, runLoop: RunLoop.main, mode: .common)
372406
.autoconnect()
373407
.receive(on: queue)
374-
.filter {[weak self] _ in self?.idleTimerCancellable != nil }
375408
.sink {[weak self] _ in
376409
guard let self else { return }
377410

378411
Log.nfc("Stop by tag timer")
379-
self.stopSession(with: TangemSdkError.nfcTimeout.localizedDescription)
412+
self.stopSession(with: TangemSdkError.nfcTimeout)
380413
self.tagTimerCancellable = nil
381414
}
382415
}
@@ -390,7 +423,7 @@ extension NFCReader: CardReader {
390423
guard let self else { return }
391424

392425
Log.nfc("Stop by session timer")
393-
self.stopSession(with: TangemSdkError.nfcTimeout.localizedDescription)
426+
self.stopSession(with: TangemSdkError.nfcTimeout)
394427
self.sessionTimerCancellable = nil
395428
}
396429
}
@@ -400,6 +433,7 @@ extension NFCReader: CardReader {
400433
.TimerPublisher(interval: Constants.idleTimeout, runLoop: RunLoop.main, mode: .common)
401434
.autoconnect()
402435
.receive(on: queue)
436+
.filter { [weak self] _ in !(self?.isBeingStopped ?? true) }
403437
.sink {[weak self] _ in
404438
guard let self else { return }
405439

@@ -470,36 +504,47 @@ extension NFCReader: CardReader {
470504
@available(iOS 13.0, *)
471505
extension NFCReader: NFCTagReaderSessionDelegate {
472506
func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
473-
sessionDidBecomeActiveTimestamp = Date()
507+
sessionDidBecomeActiveTS = Date()
474508
isSessionReady = true
475509
}
476510

477511
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)")
479513
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))")
481521
}
482522
}
483523

484524
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
485525
Log.nfc("NFC tag detected: \(tags)")
486526

487-
if firstConnectionTimestamp == nil {
488-
firstConnectionTimestamp = Date()
489-
}
490-
491527
let nfcTag = tags.first!
492528

493529
sessionConnectCancellable = session.connectPublisher(tag: nfcTag)
494530
.receive(on: queue)
495531
.sink {[weak self] completion in
532+
guard let self else { return }
533+
496534
switch completion {
497535
case .failure:
498-
self?.restartPolling(silent: false)
536+
self.restartPolling(silent: false)
499537
case .finished:
500538
break
501539
}
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+
503548
} receiveValue: {[weak self] _ in
504549
self?.tagDidConnect(nfcTag)
505550
}
@@ -516,7 +561,7 @@ extension NFCReader {
516561
static let nfcStuckTimeout = 1.0
517562
static let retryCount = 20
518563
static let startRetryCount = 5
519-
static let timestampTolerance = 1.0
564+
static let requestToleranceTS = 1.0
520565
static let searchTagTimeout = 1.0
521566
}
522567
}

0 commit comments

Comments
 (0)