Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add custom labels text support to scan nfc process #7

Merged
merged 3 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading