Skip to content

Commit

Permalink
feat: add custom labels text support to scan nfc process (#7)
Browse files Browse the repository at this point in the history
* feat: add custom labels text support to scan nfc process

* feat: add custom labels text support to scan nfc process

* feat: add custom labels text support to scan nfc process
  • Loading branch information
DanielFRico authored Dec 6, 2024
1 parent 8eaada9 commit a0c899a
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class EIdReaderModule(reactContext: ReactApplicationContext) :
private val jsonToReactMap = JsonToReactMap()
private var _promise: Promise? = null
private var _dialog: AlertDialog? = null
private var labels: ReadableMap? = null

init {
reactApplicationContext.addLifecycleEventListener(this)
Expand Down Expand Up @@ -171,7 +172,8 @@ class EIdReaderModule(reactContext: ReactApplicationContext) :
)

currentActivity?.runOnUiThread(Runnable {
_dialog?.setMessage("Reading. Hold your document...")
val message = labels?.getString("reading") ?: "Reading. Hold your document..."
_dialog?.setMessage(message)
})

val result = nfcPassportReader.readPassport(IsoDep.get(tag), bacKey, includeImages, includeRawData)
Expand Down Expand Up @@ -199,7 +201,8 @@ class EIdReaderModule(reactContext: ReactApplicationContext) :

private fun reject(e: Exception) {
currentActivity?.runOnUiThread(Runnable {
_dialog?.setMessage("Sorry, there was a problem reading the passport. Please try again")
val message = labels?.getString("error") ?: "Sorry, there was a problem reading the passport. Please try again"
_dialog?.setMessage(message)
})
stopReading(false)
_promise?.reject(e)
Expand All @@ -210,6 +213,7 @@ class EIdReaderModule(reactContext: ReactApplicationContext) :
readableMap?.let {
try {
_promise = promise
labels = readableMap?.getMap("labels")
val mrzMap = readableMap?.getMap("mrzInfo")
val mrzExpirationDate = mrzMap?.getString("expirationDate")
val mrzBirthDate = mrzMap?.getString("birthDate")
Expand All @@ -230,11 +234,14 @@ class EIdReaderModule(reactContext: ReactApplicationContext) :
val currentActivity = currentActivity
if (currentActivity != null) {
currentActivity?.runOnUiThread(Runnable {
val title = labels?.getString("title") ?: "Ready to Scan"
val message = labels?.getString("requestPresentPassport") ?: "Hold your phone near an NFC enabled passport"
val cancelButton = labels?.getString("cancelButton") ?: "Cancel"
val builder = AlertDialog.Builder(currentActivity)
builder.setTitle("Ready to Scan")
.setMessage("Hold your phone near an NFC enabled passport")
builder.setTitle(title)
.setMessage(message)
.setCancelable(true)
.setNegativeButton("Cancel") { dialog, _ ->
.setNegativeButton(cancelButton) { dialog, _ ->
stopReading()
val reactMap = jsonToReactMap.convertJsonToMap(JSONObject("{status: 'Canceled' }"))

Expand Down
12 changes: 2 additions & 10 deletions ios/EidReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class EIdReader: RCTEventEmitter {
// TODO
isReading = true

let labels = params["labels"] as? NSDictionary
let mrzInfo = params["mrzInfo"] as! NSDictionary
let expirationDate = mrzInfo["expirationDate"] as! String
let birthDate = mrzInfo["birthDate"] as! String
Expand All @@ -44,16 +45,7 @@ class EIdReader: RCTEventEmitter {
Task {
var eidReadResult: [String: Any] = [:]
do {
let customMessageHandler : (NFCViewDisplayMessage)->String? = { (displayMessage) in
switch displayMessage {
case .requestPresentPassport:
return "Hold your iPhone near an NFC enabled passport."
default:
// Return nil for all other messages so we use the provided default
return nil
}
}
let passport = try await passportReader.readPassport( mrzKey: mrzKey, useExtendedMode: false, customDisplayMessage:customMessageHandler)
let passport = try await passportReader.readPassport( mrzKey: mrzKey, useExtendedMode: false, labels: labels)

var data: [String: Any] = [:]

Expand Down
2 changes: 1 addition & 1 deletion ios/NFCPassportReader/BACHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public class BACHandler {
let maResponse = try await tagReader.doMutualAuthentication(cmdData: Data(cmd_data))
Logger.bac.debug( "DATA - \(maResponse.data)" )
guard maResponse.data.count > 0 else {
throw NFCPassportReaderError.InvalidMRZKey
throw NFCPassportReaderError.InvalidMRZKey()
}

let (KSenc, KSmac, ssc) = try self.sessionKeys(data: [UInt8](maResponse.data))
Expand Down
2 changes: 1 addition & 1 deletion ios/NFCPassportReader/DataGroups/DataGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class DataGroup {

// Fix for some passports that may have invalid data - ensure that we do have data!
guard data.count > pos else {
throw NFCPassportReaderError.TagNotValid
throw NFCPassportReaderError.TagNotValid()
}

if binToHex(data[pos]) & 0x0F == 0x0F {
Expand Down
8 changes: 4 additions & 4 deletions ios/NFCPassportReader/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ public enum NFCPassportReaderError: Error {
case UnknownTag
case UnknownImageFormat
case NotImplemented
case TagNotValid
case ConnectionError
case TagNotValid(String? = nil)
case ConnectionError(String? = nil)
case UserCanceled
case InvalidMRZKey
case MoreThanOneTagFound
case InvalidMRZKey(String? = nil)
case MoreThanOneTagFound(String? = nil)
case InvalidHashAlgorithmSpecified
case UnsupportedCipherAlgorithm
case UnsupportedMappingType
Expand Down
53 changes: 27 additions & 26 deletions ios/NFCPassportReader/NFCViewDisplayMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,46 @@ import Foundation

@available(iOS 13, macOS 10.15, *)
public enum NFCViewDisplayMessage {
case requestPresentPassport
case authenticatingWithPassport(Int)
case readingDataGroupProgress(DataGroupId, Int)
case error(NFCPassportReaderError)
case activeAuthentication
case successfulRead
case requestPresentPassport(String? = nil)
case authenticatingWithPassport(Int, String? = nil)
case readingDataGroupProgress(DataGroupId, Int, String? = nil)
case error(NFCPassportReaderError, String? = nil)
case activeAuthentication(String? = nil)
case successfulRead(String? = nil)
}

@available(iOS 13, macOS 10.15, *)
extension NFCViewDisplayMessage {
public var description: String {
switch self {
case .requestPresentPassport:
return "Hold your iPhone near an NFC enabled passport."
case .authenticatingWithPassport(let progress):
case .requestPresentPassport(let label):
return label ?? "Hold your iPhone near an NFC enabled passport."
case .authenticatingWithPassport(let progress, let label):
let progressString = handleProgress(percentualProgress: progress)
return "Authenticating with passport.....\n\n\(progressString)"
case .readingDataGroupProgress(let dataGroup, let progress):
let message = "\(label ?? "Authenticating with passport...")\n\n\(progressString)"
return message
case .readingDataGroupProgress(let dataGroup, let progress, let label):
let progressString = handleProgress(percentualProgress: progress)
return "Reading \(dataGroup).....\n\n\(progressString)"
case .error(let tagError):
return "\(label ?? "Reading...") \(dataGroup)n\n\(progressString)"
case .error(let tagError, let label):
switch tagError {
case NFCPassportReaderError.TagNotValid:
return "Tag not valid."
case NFCPassportReaderError.MoreThanOneTagFound:
return "More than 1 tags was found. Please present only 1 tag."
case NFCPassportReaderError.ConnectionError:
return "Connection error. Please try again."
case NFCPassportReaderError.InvalidMRZKey:
case NFCPassportReaderError.TagNotValid(let label):
return label ?? "Tag not valid."
case NFCPassportReaderError.MoreThanOneTagFound(let label):
return label ?? "More than 1 tags was found. Please present only 1 tag."
case NFCPassportReaderError.ConnectionError(let label):
return label ?? "Connection error. Please try again."
case NFCPassportReaderError.InvalidMRZKey(let label):
return "MRZ Key not valid for this document."
case NFCPassportReaderError.ResponseError(let description, let sw1, let sw2):
return "Sorry, there was a problem reading the passport. \(description) - (0x\(sw1), 0x\(sw2)"
return "\(label ?? "Sorry, there was a problem reading the passport. Please try again.") \(description) - (0x\(sw1), 0x\(sw2)"
default:
return "Sorry, there was a problem reading the passport. Please try again"
return label ?? "Sorry, there was a problem reading the passport. Please try again"
}
case .activeAuthentication:
return "Authenticating....."
case .successfulRead:
return "Passport read successfully"
case .activeAuthentication(let label):
return label ?? "Authenticating..."
case .successfulRead(let label):
return label ?? "Passport read successfully"
}
}

Expand Down
36 changes: 18 additions & 18 deletions ios/NFCPassportReader/PassportReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import CoreNFC

@available(iOS 15, *)
public class PassportReader : NSObject {
private var labels: NSDictionary? = nil
private var pendingPassportModel: NFCPassportModel? = nil
private typealias NFCCheckedContinuation = CheckedContinuation<NFCPassportModel, Error>
private var nfcContinuation: NFCCheckedContinuation?
Expand Down Expand Up @@ -64,8 +65,8 @@ public class PassportReader : NSObject {
dataAmountToReadOverride = amount
}

public func readPassport( mrzKey : String, tags : [DataGroupId] = [], skipSecureElements : Bool = true, skipCA : Bool = false, skipPACE : Bool = false, useExtendedMode : Bool = false, customDisplayMessage : ((NFCViewDisplayMessage) -> String?)? = nil) async throws -> NFCPassportModel {

public func readPassport( mrzKey : String, tags : [DataGroupId] = [], skipSecureElements : Bool = true, skipCA : Bool = false, skipPACE : Bool = false, useExtendedMode : Bool = false, customDisplayMessage : ((NFCViewDisplayMessage) -> String?)? = nil, labels: NSDictionary?) async throws -> NFCPassportModel {
self.labels = labels
self.passport = NFCPassportModel()
self.mrzKey = mrzKey
self.skipCA = skipCA
Expand Down Expand Up @@ -98,7 +99,7 @@ public class PassportReader : NSObject {
if NFCTagReaderSession.readingAvailable {
readerSession = NFCTagReaderSession(pollingOption: [.iso14443], delegate: self, queue: nil)

self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.requestPresentPassport )
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.requestPresentPassport(labels?["requestPresentPassport"] as? String))
readerSession?.begin()
}

Expand Down Expand Up @@ -171,8 +172,8 @@ extension PassportReader : NFCTagReaderSessionDelegate {
if tags.count > 1 {
Logger.passportReader.debug( "tagReaderSession:more than 1 tag detected! - \(tags)" )

let errorMessage = NFCViewDisplayMessage.error(.MoreThanOneTagFound)
self.invalidateSession(errorMessage: errorMessage, error: NFCPassportReaderError.MoreThanOneTagFound)
let errorMessage = NFCViewDisplayMessage.error(NFCPassportReaderError.MoreThanOneTagFound(self.labels?["moreThanOneTagFound"]as? String))
self.invalidateSession(errorMessage: errorMessage, error: NFCPassportReaderError.MoreThanOneTagFound(self.labels?["moreThanOneTagFound"] as? String))
return
}

Expand All @@ -183,9 +184,8 @@ extension PassportReader : NFCTagReaderSessionDelegate {
passportTag = tag
default:
Logger.passportReader.debug( "tagReaderSession:invalid tag detected!!!" )

let errorMessage = NFCViewDisplayMessage.error(NFCPassportReaderError.TagNotValid)
self.invalidateSession(errorMessage:errorMessage, error: NFCPassportReaderError.TagNotValid)
let errorMessage = NFCViewDisplayMessage.error(NFCPassportReaderError.TagNotValid(self.labels?["tagNotValid"] as? String))
self.invalidateSession(errorMessage:errorMessage, error: NFCPassportReaderError.TagNotValid(self.labels?["tagNotValid"] as? String))
return
}

Expand All @@ -194,7 +194,7 @@ extension PassportReader : NFCTagReaderSessionDelegate {
try await session.connect(to: tag)

Logger.passportReader.debug( "tagReaderSession:connected to tag - starting authentication" )
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.authenticatingWithPassport(0) )
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.authenticatingWithPassport(0, self.labels?["authenticatingWithPassport"] as? String) )

let tagReader = TagReader(tag:passportTag)

Expand All @@ -204,20 +204,20 @@ extension PassportReader : NFCTagReaderSessionDelegate {

tagReader.progress = { [unowned self] (progress) in
if let dgId = self.currentlyReadingDataGroup {
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(dgId, progress) )
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(dgId, progress, self.labels?["reading"] as? String) )
} else {
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.authenticatingWithPassport(progress) )
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.authenticatingWithPassport(progress, self.labels?["reading"] as? String) )
}
}

try await self.startReading( tagReader : tagReader)

} catch let error as NFCPassportReaderError {
let errorMessage = NFCViewDisplayMessage.error(error)
let errorMessage = NFCViewDisplayMessage.error(error, self.labels?["error"] as? String)
self.invalidateSession(errorMessage: errorMessage, error: error)
} catch let error {
Logger.passportReader.debug( "tagReaderSession:failed to connect to tag - \(error.localizedDescription)" )
let errorMessage = NFCViewDisplayMessage.error(NFCPassportReaderError.ConnectionError)
let errorMessage = NFCViewDisplayMessage.error(NFCPassportReaderError.ConnectionError(self.labels?["error"] as? String))
self.invalidateSession(errorMessage: errorMessage, error: NFCPassportReaderError.Unknown(error))
}
}
Expand Down Expand Up @@ -264,7 +264,7 @@ extension PassportReader {

try await doActiveAuthenticationIfNeccessary(tagReader : tagReader)

self.updateReaderSessionMessage(alertMessage: NFCViewDisplayMessage.successfulRead)
self.updateReaderSessionMessage(alertMessage: NFCViewDisplayMessage.successfulRead(self.labels?["successfulRead"] as? String))
self.shouldNotReportNextReaderSessionInvalidationErrorUserCanceled = true
self.readerSession?.invalidate()

Expand All @@ -279,7 +279,7 @@ extension PassportReader {
guard self.passport.activeAuthenticationSupported else {
return
}
self.updateReaderSessionMessage(alertMessage: NFCViewDisplayMessage.activeAuthentication)
self.updateReaderSessionMessage(alertMessage: NFCViewDisplayMessage.activeAuthentication(self.labels?["activeAuthentication"] as? String))

Logger.passportReader.info( "Performing Active Authentication" )

Expand Down Expand Up @@ -309,7 +309,7 @@ extension PassportReader {
// Read COM
var DGsToRead = [DataGroupId]()

self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(.COM, 0) )
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(.COM, 0, self.labels?["reading"] as? String) )
if let com = try await readDataGroup(tagReader:tagReader, dgId:.COM) as? COM {
self.passport.addDataGroup( .COM, dataGroup:com )

Expand Down Expand Up @@ -353,7 +353,7 @@ extension PassportReader {
DGsToRead = DGsToRead.filter { dataGroupsToRead.contains($0) }
}
for dgId in DGsToRead {
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(dgId, 0) )
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(dgId, 0, self.labels?["reading"] as? String) )
if let dg = try await readDataGroup(tagReader:tagReader, dgId:dgId) {
self.passport.addDataGroup( dgId, dataGroup:dg )
}
Expand All @@ -367,7 +367,7 @@ extension PassportReader {
var readAttempts = 0
var nfcPassportReaderError: NFCPassportReaderError

self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(dgId, 0) )
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(dgId, 0, self.labels?["reading"] as? String) )

repeat {
do {
Expand Down
2 changes: 1 addition & 1 deletion ios/NFCPassportReader/TagReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ public class TagReader {
Logger.tagReader.error( "Error reading tag: sw1 - 0x\(binToHexRep(sw1)), sw2 - 0x\(binToHexRep(sw2))" )
let tagError: NFCPassportReaderError
if (rep.sw1 == 0x63 && rep.sw2 == 0x00) {
tagError = NFCPassportReaderError.InvalidMRZKey
tagError = NFCPassportReaderError.InvalidMRZKey()
} else {
let errorMsg = self.decodeError(sw1: rep.sw1, sw2: rep.sw2)
Logger.tagReader.error( "reason: \(errorMsg)" )
Expand Down
13 changes: 13 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ export type StartReadingParams = {
};
includeImages?: boolean; // default: false
includeRawData?: boolean; // default: false
labels?: {
title?: string;
cancelButton?: string;
requestPresentPassport?: string;
authenticatingWithPassport?: string;
reading?: string;
activeAuthentication?: string;
successfulRead?: string;
tagNotValid?: string;
moreThanOneTagFound?: string;
invalidMRZKey?: string;
error?: string;
};
};

export type EidReadStatus = 'OK' | 'Error' | 'Canceled';
Expand Down

0 comments on commit a0c899a

Please sign in to comment.