Files
self/app/ios/Examples/Example_SPM/NFCPassportReaderApp/Views/MainView.swift
2023-10-14 14:46:20 +02:00

231 lines
8.3 KiB
Swift

//
// MainView.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 04/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import SwiftUI
import Combine
import NFCPassportReader
import UniformTypeIdentifiers
import MRZParser
struct MainView : View {
@EnvironmentObject var settings: SettingsStore
@Environment(\.colorScheme) var colorScheme
@State private var showingAlert = false
@State private var showingSheet = false
@State private var showDetails = false
@State private var alertTitle : String = ""
@State private var alertMessage : String = ""
@State private var showSettings : Bool = false
@State private var showScanMRZ : Bool = false
@State private var showSavedPassports : Bool = false
@State var page = 0
@State var bgColor = Color( UIColor.systemBackground )
private let passportReader = PassportReader()
var body: some View {
NavigationView {
ZStack {
NavigationLink( destination: SettingsView(), isActive: $showSettings) { Text("") }
NavigationLink( destination: PassportView(), isActive: $showDetails) { Text("") }
NavigationLink( destination: StoredPassportView(), isActive: $showSavedPassports) { Text("") }
NavigationLink( destination: MRZScanner(completionHandler: { mrz in
if let (docNr, dob, doe) = parse( mrz:mrz ) {
settings.passportNumber = docNr
settings.dateOfBirth = dob
settings.dateOfExpiry = doe
}
showScanMRZ = false
}).navigationTitle("Scan MRZ"), isActive: $showScanMRZ){ Text("") }
VStack {
HStack {
Spacer()
Button(action: {self.showScanMRZ.toggle()}) {
Label("Scan MRZ", systemImage:"camera")
}.padding([.top, .trailing])
}
MRZEntryView()
Button(action: {
self.scanPassport()
}) {
Text("Scan Passport")
.font(.largeTitle)
.foregroundColor(isValid ? .secondary : Color.secondary.opacity(0.25))
}
.disabled( !isValid )
Spacer()
HStack(alignment:.firstTextBaseline) {
Text( "Version - \(UIApplication.version)" )
.font(.footnote)
.padding(.leading)
Spacer()
Button(action: {
shareLogs()
}) {
Text("Share logs")
.foregroundColor(.secondary)
}.padding(.trailing)
.disabled( !isValid )
}
}
}
.navigationBarTitle("Passport details", displayMode: .automatic)
.toolbar {
ToolbarItem(placement: .primaryAction) {
Menu {
Button(action: {showSettings.toggle()}) {
Label("Settings", systemImage: "gear")
}
Button(action: {self.showSavedPassports.toggle()}) {
Label("Show saved passports", systemImage: "doc")
}
} label: {
Image(systemName: "gear")
.foregroundColor(Color.secondary)
}
}
}
.alert(isPresented: $showingAlert) {
Alert(title: Text(alertTitle), message:
Text(alertMessage), dismissButton: .default(Text("Got it!")))
}
.background(colorScheme == .dark ? Color.black : Color.white)
}
}
}
// MARK: View functions - functions that affect the view
extension MainView {
var isValid : Bool {
return settings.passportNumber.count >= 8
}
func parse( mrz:String ) -> (String, Date, Date)? {
print( "mrz = \(mrz)")
let parser = MRZParser(isOCRCorrectionEnabled: true)
if let result = parser.parse(mrzString: mrz),
let docNr = result.documentNumber,
let dob = result.birthdate,
let doe = result.expiryDate {
return (docNr, dob, doe)
}
return nil
}
}
// MARK: Action Functions
extension MainView {
func shareLogs() {
hideKeyboard()
PassportUtils.shareLogs()
}
func scanPassport( ) {
hideKeyboard()
self.showDetails = false
let df = DateFormatter()
df.timeZone = TimeZone(secondsFromGMT: 0)
df.dateFormat = "YYMMdd"
let pptNr = settings.passportNumber
let dob = df.string(from:settings.dateOfBirth)
let doe = df.string(from:settings.dateOfExpiry)
let passportUtils = PassportUtils()
let mrzKey = passportUtils.getMRZKey( passportNumber: pptNr, dateOfBirth: dob, dateOfExpiry: doe)
// Set the masterListURL on the Passport Reader to allow auto passport verification
let masterListURL = Bundle.main.url(forResource: "masterList", withExtension: ".pem")!
passportReader.setMasterListURL( masterListURL )
// Set whether to use the new Passive Authentication verification method (default true) or the old OpenSSL CMS verifiction
passportReader.passiveAuthenticationUsesOpenSSL = !settings.useNewVerificationMethod
// If we want to read only specific data groups we can using:
// let dataGroups : [DataGroupId] = [.COM, .SOD, .DG1, .DG2, .DG7, .DG11, .DG12, .DG14, .DG15]
// passportReader.readPassport(mrzKey: mrzKey, tags:dataGroups, completed: { (passport, error) in
Log.logLevel = settings.logLevel
Log.storeLogs = settings.shouldCaptureLogs
Log.clearStoredLogs()
Log.error( "Using version \(UIApplication.version)" )
Task {
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
}
}
do {
let passport = try await passportReader.readPassport( mrzKey: mrzKey, customDisplayMessage:customMessageHandler)
if settings.savePassportOnScan {
// Save passport
let dict = passport.dumpPassportData(selectedDataGroups: DataGroupId.allCases, includeActiveAuthenticationData: true)
if let data = try? JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) {
let savedPath = FileManager.cachesFolder.appendingPathComponent("\(passport.documentNumber).json")
try? data.write(to: savedPath, options: .completeFileProtection)
}
}
DispatchQueue.main.async {
self.settings.passport = passport
self.showDetails = true
}
} catch {
self.alertTitle = "Oops"
self.alertTitle = error.localizedDescription
self.showingAlert = true
}
}
}
}
//MARK: PreviewProvider
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
let settings = SettingsStore()
return Group {
MainView()
.environmentObject(settings)
.environment( \.colorScheme, .light)
MainView()
.environmentObject(settings)
.environment( \.colorScheme, .dark)
}
}
}
#endif