fix XCode config, add icons

This commit is contained in:
0xturboblitz
2023-10-14 14:46:20 +02:00
parent 815fd9434b
commit cc5ed77dae
133 changed files with 95298 additions and 0 deletions

View File

@@ -0,0 +1,695 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
28BAEE044BE68A5986B97866 /* Pods_NFCPassportReaderApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7BD003B33886ED0F3F03325 /* Pods_NFCPassportReaderApp.framework */; };
8C4EEF9CDD6DAFEF26F8909A /* Pods_NFCPassportReaderAppTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA39DA603B1A7EBD8FC38112 /* Pods_NFCPassportReaderAppTests.framework */; };
A1298C9F25D53A5C00F5713E /* ViewExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1298C9E25D53A5C00F5713E /* ViewExt.swift */; };
A1614B9225D5856600191749 /* ExportPassportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1614B9125D5856600191749 /* ExportPassportView.swift */; };
A16EB23122A9E9E10008F53F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A16EB23022A9E9E10008F53F /* AppDelegate.swift */; };
A16EB23322A9E9E10008F53F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A16EB23222A9E9E10008F53F /* SceneDelegate.swift */; };
A16EB23A22A9E9E20008F53F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A16EB23922A9E9E20008F53F /* Assets.xcassets */; };
A16EB23D22A9E9E20008F53F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A16EB23B22A9E9E20008F53F /* LaunchScreen.storyboard */; };
A17134BE22C8EF7200C457C3 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A17134BD22C8EF7100C457C3 /* MainView.swift */; };
A17134C022C8F0A600C457C3 /* PassportUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A17134BF22C8F0A600C457C3 /* PassportUtils.swift */; };
A17375F525F78B0D005995DA /* PACETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A17375F425F78B0C005995DA /* PACETests.swift */; };
A1769DF725D3E318006002D1 /* SettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1769DF625D3E318006002D1 /* SettingsStore.swift */; };
A1816E9C22C9059F00F546A0 /* PassportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1816E9B22C9059F00F546A0 /* PassportView.swift */; };
A18248C22369D17300581384 /* DetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A18248C12369D17300581384 /* DetailsView.swift */; };
A182DE6125DD6F1300341204 /* StoredPassportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A182DE6025DD6F1300341204 /* StoredPassportView.swift */; };
A182DE6325DD730D00341204 /* FileManagerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = A182DE6225DD730D00341204 /* FileManagerExt.swift */; };
A182DE6825DD7EA600341204 /* MRZScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A182DE6725DD7EA600341204 /* MRZScannerViewController.swift */; };
A1A13B9525B8A29E0026074C /* UIApplicationExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1A13B9425B8A29E0026074C /* UIApplicationExt.swift */; };
A1A4C0F725B5E4D70070908B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A4C0F625B5E4D70070908B /* Foundation.framework */; };
A1C234C225DD19DE003FFD79 /* PassportSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1C234C125DD19DE003FFD79 /* PassportSummaryView.swift */; };
A1CE83AC22C91D1300E3EACF /* StringExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1CE83AB22C91D1300E3EACF /* StringExt.swift */; };
A1DD3F3F22C535F10067255C /* masterList.pem in Resources */ = {isa = PBXBuildFile; fileRef = A1DD3F3E22C535F00067255C /* masterList.pem */; };
A1EBC5242370BAB2004DD19E /* DataGroupParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EBC5212370BAB2004DD19E /* DataGroupParsingTests.swift */; };
A1EBC5252370BAB2004DD19E /* NFCPassportReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EBC5222370BAB2004DD19E /* NFCPassportReaderTests.swift */; };
A1FDC52E25D3F15D00D22FF4 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1FDC52D25D3F15D00D22FF4 /* SettingsView.swift */; };
A1FDC53225D3F19E00D22FF4 /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1FDC53125D3F19E00D22FF4 /* ViewModifiers.swift */; };
A1FDC53425D3F1DE00D22FF4 /* CheckBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1FDC53325D3F1DE00D22FF4 /* CheckBoxView.swift */; };
A1FDC53625D43AB400D22FF4 /* MRZEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1FDC53525D43AB400D22FF4 /* MRZEntryView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
A1EBC5122370B88D004DD19E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = A16EB22522A9E9E10008F53F /* Project object */;
proxyType = 1;
remoteGlobalIDString = A16EB22C22A9E9E10008F53F;
remoteInfo = NFCPassportReaderApp;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
29684A8780D423313153D8B6 /* Pods-NFCPassportReaderAppTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NFCPassportReaderAppTests.release.xcconfig"; path = "Target Support Files/Pods-NFCPassportReaderAppTests/Pods-NFCPassportReaderAppTests.release.xcconfig"; sourceTree = "<group>"; };
3B684038F7C81C32BA310074 /* Pods-NFCPassportReaderAppTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NFCPassportReaderAppTests.debug.xcconfig"; path = "Target Support Files/Pods-NFCPassportReaderAppTests/Pods-NFCPassportReaderAppTests.debug.xcconfig"; sourceTree = "<group>"; };
574315DEF74ED0B9CC6A8DC7 /* Pods-NFCPassportReaderApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NFCPassportReaderApp.debug.xcconfig"; path = "Target Support Files/Pods-NFCPassportReaderApp/Pods-NFCPassportReaderApp.debug.xcconfig"; sourceTree = "<group>"; };
A1095E982588FBB100D2A46A /* CHANGELOG */ = {isa = PBXFileReference; lastKnownFileType = text; name = CHANGELOG; path = ../../../CHANGELOG; sourceTree = "<group>"; };
A1095E9B2589032D00D2A46A /* readme.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = readme.md; path = ../../readme.md; sourceTree = "<group>"; };
A1298C9E25D53A5C00F5713E /* ViewExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExt.swift; sourceTree = "<group>"; };
A1614B9125D5856600191749 /* ExportPassportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportPassportView.swift; sourceTree = "<group>"; };
A16EB22D22A9E9E10008F53F /* NFCPassportReaderApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NFCPassportReaderApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
A16EB23022A9E9E10008F53F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
A16EB23222A9E9E10008F53F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
A16EB23922A9E9E20008F53F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A16EB23C22A9E9E20008F53F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
A16EB23E22A9E9E20008F53F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A16EB24422A9EA000008F53F /* NFCPassportReader.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NFCPassportReader.entitlements; sourceTree = "<group>"; };
A17134BD22C8EF7100C457C3 /* MainView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
A17134BF22C8F0A600C457C3 /* PassportUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassportUtils.swift; sourceTree = "<group>"; };
A17375F425F78B0C005995DA /* PACETests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PACETests.swift; sourceTree = "<group>"; };
A1769DF625D3E318006002D1 /* SettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsStore.swift; sourceTree = "<group>"; };
A1816E9B22C9059F00F546A0 /* PassportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassportView.swift; sourceTree = "<group>"; };
A18248C12369D17300581384 /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = "<group>"; };
A182DE6025DD6F1300341204 /* StoredPassportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredPassportView.swift; sourceTree = "<group>"; };
A182DE6225DD730D00341204 /* FileManagerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerExt.swift; sourceTree = "<group>"; };
A182DE6725DD7EA600341204 /* MRZScannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MRZScannerViewController.swift; sourceTree = "<group>"; };
A1A13B9425B8A29E0026074C /* UIApplicationExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationExt.swift; sourceTree = "<group>"; };
A1A4C0F625B5E4D70070908B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
A1C234C125DD19DE003FFD79 /* PassportSummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassportSummaryView.swift; sourceTree = "<group>"; };
A1CE83AB22C91D1300E3EACF /* StringExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExt.swift; sourceTree = "<group>"; };
A1DD3F3E22C535F00067255C /* masterList.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = masterList.pem; sourceTree = "<group>"; };
A1EBC50D2370B88D004DD19E /* NFCPassportReaderAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NFCPassportReaderAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A1EBC5112370B88D004DD19E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A1EBC5212370BAB2004DD19E /* DataGroupParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataGroupParsingTests.swift; sourceTree = "<group>"; };
A1EBC5222370BAB2004DD19E /* NFCPassportReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NFCPassportReaderTests.swift; sourceTree = "<group>"; };
A1FDC52D25D3F15D00D22FF4 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
A1FDC53125D3F19E00D22FF4 /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = "<group>"; };
A1FDC53325D3F1DE00D22FF4 /* CheckBoxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBoxView.swift; sourceTree = "<group>"; };
A1FDC53525D43AB400D22FF4 /* MRZEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MRZEntryView.swift; sourceTree = "<group>"; };
A7BD003B33886ED0F3F03325 /* Pods_NFCPassportReaderApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NFCPassportReaderApp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B239DD4BD3AA9ECF4F595D22 /* Pods-NFCPassportReaderApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NFCPassportReaderApp.release.xcconfig"; path = "Target Support Files/Pods-NFCPassportReaderApp/Pods-NFCPassportReaderApp.release.xcconfig"; sourceTree = "<group>"; };
DA39DA603B1A7EBD8FC38112 /* Pods_NFCPassportReaderAppTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NFCPassportReaderAppTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
A16EB22A22A9E9E10008F53F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A1A4C0F725B5E4D70070908B /* Foundation.framework in Frameworks */,
28BAEE044BE68A5986B97866 /* Pods_NFCPassportReaderApp.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A1EBC50A2370B88D004DD19E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8C4EEF9CDD6DAFEF26F8909A /* Pods_NFCPassportReaderAppTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
A1298C9B25D539CD00F5713E /* HelperViews */ = {
isa = PBXGroup;
children = (
A1FDC53325D3F1DE00D22FF4 /* CheckBoxView.swift */,
A1298C9E25D53A5C00F5713E /* ViewExt.swift */,
A1FDC53125D3F19E00D22FF4 /* ViewModifiers.swift */,
);
path = HelperViews;
sourceTree = "<group>";
};
A16EB22422A9E9E10008F53F = {
isa = PBXGroup;
children = (
A16EB22F22A9E9E10008F53F /* NFCPassportReaderApp */,
A1EBC50E2370B88D004DD19E /* NFCPassportReaderAppTests */,
A16EB22E22A9E9E10008F53F /* Products */,
EE354654AF07CDA5F9366735 /* Pods */,
A1A4C0E325B5D3370070908B /* Frameworks */,
);
sourceTree = "<group>";
};
A16EB22E22A9E9E10008F53F /* Products */ = {
isa = PBXGroup;
children = (
A16EB22D22A9E9E10008F53F /* NFCPassportReaderApp.app */,
A1EBC50D2370B88D004DD19E /* NFCPassportReaderAppTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
A16EB22F22A9E9E10008F53F /* NFCPassportReaderApp */ = {
isa = PBXGroup;
children = (
A16EB24422A9EA000008F53F /* NFCPassportReader.entitlements */,
A1CE83AF22C91DB600E3EACF /* Model */,
A182DE6425DD731900341204 /* Extensions */,
A1CE83B022C91DF100E3EACF /* Views */,
A16EB23022A9E9E10008F53F /* AppDelegate.swift */,
A16EB23222A9E9E10008F53F /* SceneDelegate.swift */,
A16EB23922A9E9E20008F53F /* Assets.xcassets */,
A16EB23B22A9E9E20008F53F /* LaunchScreen.storyboard */,
A16EB23E22A9E9E20008F53F /* Info.plist */,
A1DD3F3E22C535F00067255C /* masterList.pem */,
A1095E982588FBB100D2A46A /* CHANGELOG */,
A1095E9B2589032D00D2A46A /* readme.md */,
);
path = NFCPassportReaderApp;
sourceTree = "<group>";
};
A182DE6425DD731900341204 /* Extensions */ = {
isa = PBXGroup;
children = (
A1CE83AB22C91D1300E3EACF /* StringExt.swift */,
A182DE6225DD730D00341204 /* FileManagerExt.swift */,
A1A13B9425B8A29E0026074C /* UIApplicationExt.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
A1A4C0E325B5D3370070908B /* Frameworks */ = {
isa = PBXGroup;
children = (
A1A4C0F625B5E4D70070908B /* Foundation.framework */,
A7BD003B33886ED0F3F03325 /* Pods_NFCPassportReaderApp.framework */,
DA39DA603B1A7EBD8FC38112 /* Pods_NFCPassportReaderAppTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
A1CE83AF22C91DB600E3EACF /* Model */ = {
isa = PBXGroup;
children = (
A17134BF22C8F0A600C457C3 /* PassportUtils.swift */,
A1769DF625D3E318006002D1 /* SettingsStore.swift */,
);
path = Model;
sourceTree = "<group>";
};
A1CE83B022C91DF100E3EACF /* Views */ = {
isa = PBXGroup;
children = (
A1298C9B25D539CD00F5713E /* HelperViews */,
A17134BD22C8EF7100C457C3 /* MainView.swift */,
A1FDC53525D43AB400D22FF4 /* MRZEntryView.swift */,
A1FDC52D25D3F15D00D22FF4 /* SettingsView.swift */,
A1816E9B22C9059F00F546A0 /* PassportView.swift */,
A1C234C125DD19DE003FFD79 /* PassportSummaryView.swift */,
A18248C12369D17300581384 /* DetailsView.swift */,
A182DE6025DD6F1300341204 /* StoredPassportView.swift */,
A1614B9125D5856600191749 /* ExportPassportView.swift */,
A182DE6725DD7EA600341204 /* MRZScannerViewController.swift */,
);
path = Views;
sourceTree = "<group>";
};
A1EBC50E2370B88D004DD19E /* NFCPassportReaderAppTests */ = {
isa = PBXGroup;
children = (
A1EBC5212370BAB2004DD19E /* DataGroupParsingTests.swift */,
A1EBC5222370BAB2004DD19E /* NFCPassportReaderTests.swift */,
A1EBC5112370B88D004DD19E /* Info.plist */,
A17375F425F78B0C005995DA /* PACETests.swift */,
);
path = NFCPassportReaderAppTests;
sourceTree = "<group>";
};
EE354654AF07CDA5F9366735 /* Pods */ = {
isa = PBXGroup;
children = (
574315DEF74ED0B9CC6A8DC7 /* Pods-NFCPassportReaderApp.debug.xcconfig */,
B239DD4BD3AA9ECF4F595D22 /* Pods-NFCPassportReaderApp.release.xcconfig */,
3B684038F7C81C32BA310074 /* Pods-NFCPassportReaderAppTests.debug.xcconfig */,
29684A8780D423313153D8B6 /* Pods-NFCPassportReaderAppTests.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
A16EB22C22A9E9E10008F53F /* NFCPassportReaderApp */ = {
isa = PBXNativeTarget;
buildConfigurationList = A16EB24122A9E9E20008F53F /* Build configuration list for PBXNativeTarget "NFCPassportReaderApp" */;
buildPhases = (
441F58E6FEE1B28C771C6B72 /* [CP] Check Pods Manifest.lock */,
A16EB22922A9E9E10008F53F /* Sources */,
A16EB22A22A9E9E10008F53F /* Frameworks */,
A16EB22B22A9E9E10008F53F /* Resources */,
4E62328E60F074D699104549 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = NFCPassportReaderApp;
productName = NFCTest;
productReference = A16EB22D22A9E9E10008F53F /* NFCPassportReaderApp.app */;
productType = "com.apple.product-type.application";
};
A1EBC50C2370B88D004DD19E /* NFCPassportReaderAppTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = A1EBC5162370B88D004DD19E /* Build configuration list for PBXNativeTarget "NFCPassportReaderAppTests" */;
buildPhases = (
11A5FABC15C673E24DA3F0C2 /* [CP] Check Pods Manifest.lock */,
A1EBC5092370B88D004DD19E /* Sources */,
A1EBC50A2370B88D004DD19E /* Frameworks */,
A1EBC50B2370B88D004DD19E /* Resources */,
);
buildRules = (
);
dependencies = (
A1EBC5132370B88D004DD19E /* PBXTargetDependency */,
);
name = NFCPassportReaderAppTests;
productName = NFCPassportReaderAppTests;
productReference = A1EBC50D2370B88D004DD19E /* NFCPassportReaderAppTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
A16EB22522A9E9E10008F53F /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1120;
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "Andy Qua";
TargetAttributes = {
A16EB22C22A9E9E10008F53F = {
CreatedOnToolsVersion = 11.0;
LastSwiftMigration = 1100;
};
A1EBC50C2370B88D004DD19E = {
CreatedOnToolsVersion = 11.2;
TestTargetID = A16EB22C22A9E9E10008F53F;
};
};
};
buildConfigurationList = A16EB22822A9E9E10008F53F /* Build configuration list for PBXProject "NFCPassportReaderApp" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = A16EB22422A9E9E10008F53F;
productRefGroup = A16EB22E22A9E9E10008F53F /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
A16EB22C22A9E9E10008F53F /* NFCPassportReaderApp */,
A1EBC50C2370B88D004DD19E /* NFCPassportReaderAppTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
A16EB22B22A9E9E10008F53F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A16EB23D22A9E9E20008F53F /* LaunchScreen.storyboard in Resources */,
A1DD3F3F22C535F10067255C /* masterList.pem in Resources */,
A16EB23A22A9E9E20008F53F /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A1EBC50B2370B88D004DD19E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
11A5FABC15C673E24DA3F0C2 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-NFCPassportReaderAppTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
441F58E6FEE1B28C771C6B72 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-NFCPassportReaderApp-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
4E62328E60F074D699104549 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-NFCPassportReaderApp/Pods-NFCPassportReaderApp-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-NFCPassportReaderApp/Pods-NFCPassportReaderApp-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NFCPassportReaderApp/Pods-NFCPassportReaderApp-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
A16EB22922A9E9E10008F53F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A182DE6825DD7EA600341204 /* MRZScannerViewController.swift in Sources */,
A1CE83AC22C91D1300E3EACF /* StringExt.swift in Sources */,
A1FDC53625D43AB400D22FF4 /* MRZEntryView.swift in Sources */,
A17134BE22C8EF7200C457C3 /* MainView.swift in Sources */,
A16EB23122A9E9E10008F53F /* AppDelegate.swift in Sources */,
A1816E9C22C9059F00F546A0 /* PassportView.swift in Sources */,
A1769DF725D3E318006002D1 /* SettingsStore.swift in Sources */,
A1298C9F25D53A5C00F5713E /* ViewExt.swift in Sources */,
A182DE6325DD730D00341204 /* FileManagerExt.swift in Sources */,
A1FDC52E25D3F15D00D22FF4 /* SettingsView.swift in Sources */,
A182DE6125DD6F1300341204 /* StoredPassportView.swift in Sources */,
A1FDC53225D3F19E00D22FF4 /* ViewModifiers.swift in Sources */,
A1C234C225DD19DE003FFD79 /* PassportSummaryView.swift in Sources */,
A1614B9225D5856600191749 /* ExportPassportView.swift in Sources */,
A18248C22369D17300581384 /* DetailsView.swift in Sources */,
A1FDC53425D3F1DE00D22FF4 /* CheckBoxView.swift in Sources */,
A1A13B9525B8A29E0026074C /* UIApplicationExt.swift in Sources */,
A16EB23322A9E9E10008F53F /* SceneDelegate.swift in Sources */,
A17134C022C8F0A600C457C3 /* PassportUtils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A1EBC5092370B88D004DD19E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A1EBC5252370BAB2004DD19E /* NFCPassportReaderTests.swift in Sources */,
A17375F525F78B0D005995DA /* PACETests.swift in Sources */,
A1EBC5242370BAB2004DD19E /* DataGroupParsingTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
A1EBC5132370B88D004DD19E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = A16EB22C22A9E9E10008F53F /* NFCPassportReaderApp */;
targetProxy = A1EBC5122370B88D004DD19E /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
A16EB23B22A9E9E20008F53F /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
A16EB23C22A9E9E20008F53F /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
A16EB23F22A9E9E20008F53F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
A16EB24022A9E9E20008F53F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
A16EB24222A9E9E20008F53F /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 574315DEF74ED0B9CC6A8DC7 /* Pods-NFCPassportReaderApp.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = NFCPassportReaderApp/NFCPassportReader.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4.1;
DEVELOPMENT_TEAM = BV8DN58CD3;
INFOPLIST_FILE = NFCPassportReaderApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.iag.nfcpassportreader;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
A16EB24322A9E9E20008F53F /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B239DD4BD3AA9ECF4F595D22 /* Pods-NFCPassportReaderApp.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = NFCPassportReaderApp/NFCPassportReader.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4.1;
DEVELOPMENT_TEAM = BV8DN58CD3;
INFOPLIST_FILE = NFCPassportReaderApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.iag.nfcpassportreader;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
A1EBC5142370B88D004DD19E /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 3B684038F7C81C32BA310074 /* Pods-NFCPassportReaderAppTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = A4ZM73UFF5;
INFOPLIST_FILE = NFCPassportReaderAppTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.andyqua.NFCPassportReaderAppTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/NFCPassportReaderApp.app/NFCPassportReaderApp";
};
name = Debug;
};
A1EBC5152370B88D004DD19E /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 29684A8780D423313153D8B6 /* Pods-NFCPassportReaderAppTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = A4ZM73UFF5;
INFOPLIST_FILE = NFCPassportReaderAppTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.andyqua.NFCPassportReaderAppTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/NFCPassportReaderApp.app/NFCPassportReaderApp";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
A16EB22822A9E9E10008F53F /* Build configuration list for PBXProject "NFCPassportReaderApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
A16EB23F22A9E9E20008F53F /* Debug */,
A16EB24022A9E9E20008F53F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
A16EB24122A9E9E20008F53F /* Build configuration list for PBXNativeTarget "NFCPassportReaderApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
A16EB24222A9E9E20008F53F /* Debug */,
A16EB24322A9E9E20008F53F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
A1EBC5162370B88D004DD19E /* Build configuration list for PBXNativeTarget "NFCPassportReaderAppTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
A1EBC5142370B88D004DD19E /* Debug */,
A1EBC5152370B88D004DD19E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = A16EB22522A9E9E10008F53F /* Project object */;
}

View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A16EB22C22A9E9E10008F53F"
BuildableName = "NFCPassportReaderApp.app"
BlueprintName = "NFCPassportReaderApp"
ReferencedContainer = "container:NFCPassportReaderApp.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NFCPassportReaderTests"
BuildableName = "NFCPassportReaderTests"
BlueprintName = "NFCPassportReaderTests"
ReferencedContainer = "container:..">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A1EBC50C2370B88D004DD19E"
BuildableName = "NFCPassportReaderAppTests.xctest"
BlueprintName = "NFCPassportReaderAppTests"
ReferencedContainer = "container:NFCPassportReaderApp.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
disableMainThreadChecker = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A16EB22C22A9E9E10008F53F"
BuildableName = "NFCPassportReaderApp.app"
BlueprintName = "NFCPassportReaderApp"
ReferencedContainer = "container:NFCPassportReaderApp.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "OS_ACTIVITY_MODE"
value = "disable"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A16EB22C22A9E9E10008F53F"
BuildableName = "NFCPassportReaderApp.app"
BlueprintName = "NFCPassportReaderApp"
ReferencedContainer = "container:NFCPassportReaderApp.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:NFCPassportReaderApp.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,39 @@
//
// AppDelegate.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 06/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import UIKit
import CoreNFC
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}

View File

@@ -0,0 +1,158 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"images" : [
{
"size" : "40x40",
"filename" : "icon-40.png",
"idiom" : "ipad",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"scale" : "2x",
"filename" : "icon-40@2x.png"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x",
"filename" : "icon-60@2x.png"
},
{
"idiom" : "ipad",
"filename" : "icon-72.png",
"size" : "72x72",
"scale" : "1x"
},
{
"scale" : "2x",
"idiom" : "ipad",
"size" : "72x72",
"filename" : "icon-72@2x.png"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x",
"filename" : "icon-76.png"
},
{
"scale" : "2x",
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon-76@2x.png"
},
{
"idiom" : "ipad",
"size" : "50x50",
"filename" : "icon-small-50.png",
"scale" : "1x"
},
{
"scale" : "2x",
"filename" : "icon-small-50@2x.png",
"size" : "50x50",
"idiom" : "ipad"
},
{
"scale" : "1x",
"filename" : "icon-small.png",
"idiom" : "iphone",
"size" : "29x29"
},
{
"scale" : "2x",
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon-small@2x.png"
},
{
"filename" : "icon.png",
"idiom" : "iphone",
"size" : "57x57",
"scale" : "1x"
},
{
"scale" : "2x",
"size" : "57x57",
"idiom" : "iphone",
"filename" : "icon@2x.png"
},
{
"filename" : "icon-small@3x.png",
"size" : "29x29",
"idiom" : "iphone",
"scale" : "3x"
},
{
"size" : "40x40",
"scale" : "3x",
"idiom" : "iphone",
"filename" : "icon-40@3x.png"
},
{
"filename" : "icon-60@3x.png",
"scale" : "3x",
"idiom" : "iphone",
"size" : "60x60"
},
{
"size" : "40x40",
"filename" : "icon-40@2x.png",
"idiom" : "iphone",
"scale" : "2x"
},
{
"scale" : "1x",
"idiom" : "ipad",
"size" : "29x29",
"filename" : "icon-small.png"
},
{
"size" : "29x29",
"scale" : "2x",
"filename" : "icon-small@2x.png",
"idiom" : "ipad"
},
{
"idiom" : "ipad",
"filename" : "icon-83.5@2x.png",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"size" : "20x20",
"scale" : "2x",
"filename" : "notification-icon@2x.png",
"idiom" : "iphone"
},
{
"scale" : "3x",
"filename" : "notification-icon@3x.png",
"idiom" : "iphone",
"size" : "20x20"
},
{
"filename" : "notification-icon~ipad.png",
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"filename" : "notification-icon~ipad@2x.png",
"size" : "20x20",
"scale" : "2x",
"idiom" : "ipad"
},
{
"filename" : "ios-marketing.png",
"size" : "1024x1024",
"idiom" : "ios-marketing",
"scale" : "1x"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "background.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "head.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,16 @@
//
// FileManagerExt.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 17/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import SwiftUI
extension FileManager {
static var cachesFolder : URL {
FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
}
}

View File

@@ -0,0 +1,40 @@
//
// StringExt.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 30/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import Foundation
/// Some Utility methods for string - access characters by index
extension String {
subscript(_ i: Int) -> String {
let idx1 = index(startIndex, offsetBy: i)
let idx2 = index(idx1, offsetBy: 1)
return String(self[idx1..<idx2])
}
subscript (bounds: CountableRange<Int>) -> String {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return String(self[start..<end])
}
subscript (bounds: CountableClosedRange<Int>) -> String {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return String(self[start...end])
}
subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
let start = index(startIndex, offsetBy: bounds.lowerBound)
return String(self[start...])
}
func strip() -> String {
let trimmed = self.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed
}
}

View File

@@ -0,0 +1,21 @@
//
// UIApplicationExt.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 20/01/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import UIKit
extension UIApplication {
static var release: String {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String? ?? "x.x"
}
static var build: String {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as! String? ?? "x"
}
static var version: String {
return "\(release).\(build)"
}
}

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>NFCPassportReaderApp</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NFCReaderUsageDescription</key>
<string>This app uses NFC to scan passports</string>
<key>NSCameraUsageDescription</key>
<string>This app uses the camera to read passports</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>nfc</string>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>A0000002471001</string>
<string>A0000002472001</string>
<string>00000000000000</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,76 @@
//
// PassportUtils.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 30/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import NFCPassportReader
class PassportUtils {
func getMRZKey(passportNumber: String, dateOfBirth: String, dateOfExpiry: String ) -> String {
// Pad fields if necessary
let pptNr = pad( passportNumber, fieldLength:9)
let dob = pad( dateOfBirth, fieldLength:6)
let exp = pad( dateOfExpiry, fieldLength:6)
// Calculate checksums
let passportNrChksum = calcCheckSum(pptNr)
let dateOfBirthChksum = calcCheckSum(dob)
let expiryDateChksum = calcCheckSum(exp)
let mrzKey = "\(pptNr)\(passportNrChksum)\(dob)\(dateOfBirthChksum)\(exp)\(expiryDateChksum)"
return mrzKey
}
func pad( _ value : String, fieldLength:Int ) -> String {
// Pad out field lengths with < if they are too short
let paddedValue = (value + String(repeating: "<", count: fieldLength)).prefix(fieldLength)
return String(paddedValue)
}
func calcCheckSum( _ checkString : String ) -> Int {
let characterDict = ["0" : "0", "1" : "1", "2" : "2", "3" : "3", "4" : "4", "5" : "5", "6" : "6", "7" : "7", "8" : "8", "9" : "9", "<" : "0", " " : "0", "A" : "10", "B" : "11", "C" : "12", "D" : "13", "E" : "14", "F" : "15", "G" : "16", "H" : "17", "I" : "18", "J" : "19", "K" : "20", "L" : "21", "M" : "22", "N" : "23", "O" : "24", "P" : "25", "Q" : "26", "R" : "27", "S" : "28","T" : "29", "U" : "30", "V" : "31", "W" : "32", "X" : "33", "Y" : "34", "Z" : "35"]
var sum = 0
var m = 0
let multipliers : [Int] = [7, 3, 1]
for c in checkString {
guard let lookup = characterDict["\(c)"],
let number = Int(lookup) else { return 0 }
let product = number * multipliers[m]
sum += product
m = (m+1) % 3
}
return (sum % 10)
}
static func shareLogs() {
do {
let arr = Log.logData
let data = try JSONSerialization.data(withJSONObject: arr, options: .prettyPrinted)
let temporaryURL = URL(fileURLWithPath: NSTemporaryDirectory() + "passportreader.log")
try data.write(to: temporaryURL)
let av = UIActivityViewController(activityItems: [temporaryURL], applicationActivities: nil)
let keyWindow = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first
keyWindow?.rootViewController?.present(av, animated: true, completion: nil)
} catch {
print( "ERROR - \(error)" )
}
}
}

View File

@@ -0,0 +1,107 @@
//
// SettingsStore.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 10/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import SwiftUI
import Combine
import NFCPassportReader
final class SettingsStore: ObservableObject {
private enum Keys {
static let captureLog = "captureLog"
static let logLevel = "logLevel"
static let useNewVerification = "useNewVerification"
static let savePassportOnScan = "savePassportOnScan"
static let passportNumber = "passportNumber"
static let dateOfBirth = "dateOfBirth"
static let dateOfExpiry = "dateOfExpiry"
static let allVals = [captureLog, logLevel, useNewVerification, passportNumber, dateOfBirth, dateOfExpiry]
}
private let cancellable: Cancellable
private let defaults: UserDefaults
let objectWillChange = PassthroughSubject<Void, Never>()
init(defaults: UserDefaults = .standard) {
self.defaults = defaults
defaults.register(defaults: [
Keys.captureLog: true,
Keys.logLevel: 1,
Keys.useNewVerification: true,
Keys.savePassportOnScan: false,
Keys.passportNumber: "",
Keys.dateOfBirth: Date().timeIntervalSince1970,
Keys.dateOfExpiry: Date().timeIntervalSince1970,
])
cancellable = NotificationCenter.default
.publisher(for: UserDefaults.didChangeNotification)
.map { _ in () }
.subscribe(objectWillChange)
}
func reset() {
if let bundleID = Bundle.main.bundleIdentifier {
UserDefaults.standard.removePersistentDomain(forName: bundleID)
}
}
var shouldCaptureLogs: Bool {
set { defaults.set(newValue, forKey: Keys.captureLog) }
get { defaults.bool(forKey: Keys.captureLog) }
}
var logLevel: LogLevel {
get {
return LogLevel(rawValue:defaults.integer(forKey: Keys.logLevel)) ?? .info
}
set {
defaults.set(newValue.rawValue, forKey: Keys.logLevel)
}
}
var useNewVerificationMethod: Bool {
set { defaults.set(newValue, forKey: Keys.useNewVerification) }
get { defaults.bool(forKey: Keys.useNewVerification) }
}
var savePassportOnScan: Bool {
set { defaults.set(newValue, forKey: Keys.savePassportOnScan) }
get { defaults.bool(forKey: Keys.savePassportOnScan) }
}
var passportNumber: String {
set { defaults.set(newValue, forKey: Keys.passportNumber) }
get { defaults.string(forKey: Keys.passportNumber) ?? "" }
}
var dateOfBirth: Date {
set {
defaults.set(newValue.timeIntervalSince1970, forKey: Keys.dateOfBirth)
}
get {
let d = Date(timeIntervalSince1970: defaults.double(forKey: Keys.dateOfBirth))
return d
}
}
var dateOfExpiry: Date {
set {
defaults.set(newValue.timeIntervalSince1970, forKey: Keys.dateOfExpiry) }
get {
let d = Date(timeIntervalSince1970: defaults.double(forKey: Keys.dateOfExpiry))
return d
}
}
@Published var passport : NFCPassportModel?
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,57 @@
//
// SceneDelegate.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 06/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var settings = SettingsStore()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use a UIHostingController as window root view controller
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: MainView().environmentObject(settings))
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}

View File

@@ -0,0 +1,197 @@
//
// DetailsView.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 30/10/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import SwiftUI
import NFCPassportReader
struct Item : Identifiable {
var id = UUID()
var title : String
var value : String
var textColor : Color {
return value.hasPrefix("FAILED") ? Color.red : Color.primary
}
}
struct DetailsView : View {
private var passport: NFCPassportModel
private var sectionNames = ["Chip information", "Verification information", "Document signing certificate", "Country signing certificate", "Security Info details", "Datagroup Hashes"]
private var sections = [[Item]]()
init( passport : NFCPassportModel ) {
self.passport = passport
sections.append(getChipInfoSection(self.passport))
sections.append(getVerificationDetailsSection(self.passport))
sections.append(getCertificateSigningCertDetails(certItems:self.passport.documentSigningCertificate?.getItemsAsDict()))
sections.append(getCertificateSigningCertDetails(certItems:self.passport.countrySigningCertificate?.getItemsAsDict()))
sections.append(getSecurityInfosSection(self.passport))
sections.append(getDataGroupHashesSection(self.passport))
}
var body: some View {
VStack {
List {
ForEach( 0 ..< self.sectionNames.count ) { i in
if self.sections[i].count > 0 {
SectionGroup(sectionTitle: self.sectionNames[i], items: self.sections[i])
}
}
}
}
}
func getChipInfoSection(_ passport: NFCPassportModel) -> [Item] {
// Build Chip info section
let chipInfo = [Item(title:"LDS Version", value: passport.LDSVersion),
Item(title:"Data groups present", value: passport.dataGroupsPresent.joined(separator: ", ")),
Item(title:"Data groups read", value: passport.dataGroupsAvailable.map { $0.getName()}.joined(separator: ", "))]
return chipInfo
}
func getVerificationDetailsSection(_ passport: NFCPassportModel) -> [Item] {
// Build Verification Info section
var activeAuth : String = "Not supported"
if passport.activeAuthenticationSupported {
activeAuth = passport.activeAuthenticationPassed ? "SUCCESS\nSignature verified" : "FAILED\nCould not verify signature"
}
var chipAuth : String = "Not supported"
if passport.isChipAuthenticationSupported {
switch( passport.chipAuthenticationStatus ) {
case .notDone:
chipAuth = "Supported - Not done"
case .success:
chipAuth = "SUCCESS\nSignature verified"
case .failed:
chipAuth = "FAILED\nCould not verify signature"
}
}
var authType : String = "Authentication not done"
if passport.PACEStatus == .success {
authType = "PACE"
} else if passport.BACStatus == .success {
authType = "BAC"
}
// Do PACE Info
var paceStatus = "Not Supported"
if passport.isPACESupported {
switch( passport.PACEStatus ) {
case .notDone:
paceStatus = "Supported - Not done"
case .success:
paceStatus = "SUCCESS"
case .failed:
paceStatus = "FAILED"
}
}
let verificationDetails : [Item] = [
Item(title: "Access Control", value: authType),
Item(title: "PACE", value: paceStatus),
Item(title: "Chip Authentication", value: chipAuth),
Item(title: "Active Authentication", value: activeAuth),
Item(title: "Document Signing Certificate", value: passport.documentSigningCertificateVerified ? "SUCCESS\nSOD Signature verified" : "FAILED\nCouldn't verify SOD signature"),
Item(title: "Country signing Certificate", value: passport.passportCorrectlySigned ? "SUCCESS\nmatched to country signing certificate" : "FAILED\nCouldn't build trust chain"),
Item(title: "Data group hashes", value: passport.passportDataNotTampered ? "SUCCESS\nAll hashes match" : "FAILED\nCouldn't match hashes" )
]
return verificationDetails
}
func getCertificateSigningCertDetails( certItems : [CertificateItem : String]? ) -> [Item] {
let titles : [String] = ["Serial number", "Signature algorithm", "Public key algorithm", "Certificate fingerprint", "Issuer", "Subject", "Valid from", "Valid to"]
var items = [Item]()
if certItems?.count ?? 0 == 0 {
items.append( Item(title:"Certificate details", value: "NOT FOUND" ) )
} else {
for title in titles {
let ci = CertificateItem(rawValue:title)!
items.append( Item(title:title, value: certItems?[ci] ?? "") )
}
}
return items
}
func getDataGroupHashesSection(_ passport: NFCPassportModel) -> [Item] {
var dgHashes = [Item]()
for id in DataGroupId.allCases {
if let hash = passport.dataGroupHashes[id] {
dgHashes.append( Item(title:hash.id, value:hash.match ? "MATCHED" : "UNMATCHED"))
dgHashes.append( Item(title:"SOD Hash", value: hash.sodHash))
dgHashes.append( Item(title:"Computed Hash", value: hash.computedHash))
}
}
return dgHashes
}
func getSecurityInfosSection( _ passport : NFCPassportModel) -> [Item] {
guard let dg14 = passport.getDataGroup(.DG14) as? DataGroup14 else { return [] }
var items = [Item]()
for secInfo in dg14.securityInfos {
var title : String = ""
var value : String = ""
if let cai = secInfo as? ChipAuthenticationInfo {
title = "ChipAuthenticationInfo"
value = "\(secInfo.getProtocolOIDString())\n\(secInfo.getObjectIdentifier())\nUses Key Id: \(cai.getKeyId())"
} else if let capki = secInfo as? ChipAuthenticationPublicKeyInfo {
title = "ChipAuthenticationPublicKeyInfo"
value = "\(secInfo.getProtocolOIDString())\n\(secInfo.getObjectIdentifier())\nKey Id: \(capki.getKeyId())"
} else if let pacei = secInfo as? PACEInfo {
title = "PACEInfo"
value = "\(pacei.getProtocolOIDString())\n\(pacei.getObjectIdentifier())\nParameter ID: \(pacei.getParameterId() ?? -1)"
} else if let activeAuthInfo = secInfo as? ActiveAuthenticationInfo {
title = "ActiveAuthenticationInfo"
value =
"\(activeAuthInfo.getProtocolOIDString())\n\(activeAuthInfo.getSignatureAlgorithmOIDString() ?? "")"
}
items.append( Item(title:title, value: value))
}
return items
}
}
struct SectionGroup : View {
var sectionTitle : String
var items : [Item]
var body: some View {
Section(header: Text(sectionTitle)) {
ForEach(self.items) { item in
VStack(alignment:.leading, spacing:0) {
Text(item.title)
.font(.headline)
Text(item.value)
.foregroundColor(item.textColor)
.lineLimit(nil)
}
}
}
}
}
struct DetailsView_Previews: PreviewProvider {
static var previews: some View {
let settings = SettingsStore()
let passport = NFCPassportModel()
return DetailsView(passport:passport)
.environmentObject(settings)
.environment( \.colorScheme, .light)
}
}

View File

@@ -0,0 +1,164 @@
//
// ExportPassportView.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 11/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import SwiftUI
import NFCPassportReader
struct MultipleSelectionRow: View {
var title: String
var isSelected: Bool
var action: () -> Void
var body: some View {
Button(action: self.action) {
HStack {
Text(self.title)
if self.isSelected {
Spacer()
Image(systemName: "checkmark")
}
}
}
.foregroundColor(.primary)
}
}
struct ExportPassportView: View {
@EnvironmentObject var settings: SettingsStore
@State var items: [DataGroupId] = []
@State var selections: [DataGroupId] = []
@State var isAASupported : Bool = false
@State var includeAA : Bool = false
var body: some View {
Form {
Section(header: Text("Select Passport items to export"), footer:Text("* contains personal information")) {
List {
ForEach(items, id: \.self) { item in
MultipleSelectionRow(title: dgToText(item), isSelected: self.selections.contains(item)) {
if self.selections.contains(item) {
self.selections.removeAll(where: { $0 == item })
}
else {
self.selections.append(item)
}
}
}
}
if isAASupported {
Toggle("Include Active Authentication Challenge/Response?", isOn: $includeAA)
.foregroundColor(.primary)
}
}
Button(action: {
sharePassport()
}, label: {
Text("Export selected passport details")
.font(.title3)
})
.foregroundColor(.primary)
}
.navigationTitle("Export passport")
.onAppear() {
if let passport = settings.passport {
items = [.SOD, .COM]
items.append(contentsOf: DataGroupId.allCases.filter { passport.dataGroupsAvailable.contains($0) } )
// Default select only the DGs that contain no personal info.
selections = [.SOD, .COM, .DG14, .DG15]
isAASupported = passport.activeAuthenticationSupported
if isAASupported {
includeAA = true
}
}
}
}
}
extension ExportPassportView {
func sharePassport() {
do {
let dict = settings.passport!.dumpPassportData( selectedDataGroups:selections,includeActiveAuthenticationData: includeAA)
let data = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
let temporaryURL = URL(fileURLWithPath: NSTemporaryDirectory() + "passport.json")
try data.write(to: temporaryURL)
let av = UIActivityViewController(activityItems: [temporaryURL], applicationActivities: nil)
let keyWindow = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first
keyWindow?.rootViewController?.present(av, animated: true, completion: nil)
} catch {
print( "ERROR - \(error)" )
}
}
func dgToText( _ dg : DataGroupId ) -> String {
switch ( dg ) {
case .SOD:
return "SOD - Document Security Object"
case .COM:
return "COM - Header and DG Presence"
case .DG1:
return "DG1* - MRZ Info"
case .DG2:
return "DG2* - Face image"
case .DG3:
return "DG3* - Fingerprints"
case .DG4:
return "DG4* - Iris"
case .DG5:
return "DG5* - Displayed portrait"
case .DG6:
return "DG6 - Reserved"
case .DG7:
return "DG7* - Signature"
case .DG8:
return "DG8* - Data features"
case .DG9:
return "DG9* - Structure features"
case .DG10:
return "DG10* - Substance features"
case .DG11:
return "DG11* - Additional personal info"
case .DG12:
return "DG12* - Additional document info"
case .DG13:
return "DG13* - Optional details"
case .DG14:
return "DG14 - Security options"
case .DG15:
return "DG15 - Active Auth PubKey"
case .DG16:
return "DG16 - Person(s) to notify"
default:
return "Unknown"
}
}
}
struct ExportPassportView_Previews: PreviewProvider {
static var previews: some View {
let settings = SettingsStore()
settings.passport = NFCPassportModel()
return NavigationView {
ExportPassportView()
}
.environmentObject(settings)
}
}

View File

@@ -0,0 +1,48 @@
//
// CheckBoxView.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 10/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import SwiftUI
struct CheckBoxButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.foregroundColor(.secondary)
}
}
struct CheckBoxView: View {
@Binding var checked: Bool
var text : String
var body: some View {
HStack() {
Button(action: {
withAnimation {
self.checked.toggle()
}
}) {
HStack(alignment: .center, spacing: 10) {
Text(text)
Image(systemName:self.checked ? "checkmark.square.fill" : "square")
}
}
.frame(height: 44, alignment: .center)
.padding(.trailing)
.buttonStyle(CheckBoxButtonStyle())
}
}
}
struct CheckBoxView_Previews: PreviewProvider {
static var previews: some View {
CheckBoxView( checked: .constant(true), text:"Are you on?")
}
}

View File

@@ -0,0 +1,20 @@
//
// ViewExt.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 11/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import SwiftUI
import UIKit
extension View {
func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
func visibility(hidden: Binding<Bool>) -> some View {
modifier(VisibilityStyle(hidden: hidden))
}
}

View File

@@ -0,0 +1,41 @@
//
// ViewModifiers.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 10/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import SwiftUI
struct ClearButton: ViewModifier {
@Binding var text: String
public func body(content: Content) -> some View {
HStack {
content
if !text.isEmpty {
Button(action: { self.text = "" },
label: {
Image(systemName: "delete.left")
.foregroundColor(Color(UIColor.opaqueSeparator))
}
)
}
}
}
}
struct VisibilityStyle: ViewModifier {
@Binding var hidden: Bool
func body(content: Content) -> some View {
Group {
if hidden {
content.hidden()
} else {
content
}
}
}
}

View File

@@ -0,0 +1,125 @@
//
// MRZEntryView.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 10/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import SwiftUI
// This will be removed once DatePicker inline works correctly
struct DateView : View {
@Binding var date : Date
var title : String
var body : some View {
VStack {
Text(title)
.font(.largeTitle)
Spacer()
DatePicker("Date of birth", selection:$date, displayedComponents: .date)
.environment(\.timeZone, TimeZone(secondsFromGMT: 0)!)
.datePickerStyle(WheelDatePickerStyle())
.labelsHidden()
Spacer()
}
}
}
// This should be a nice simple inline DatePicker here
// BUT there are bugs when you select dates it changes the date format
// from DD MMM YYYY to DD/MM/YYYY!)
// Will update when/if this gets fixed!
struct MRZEntryView : View {
@EnvironmentObject var settings: SettingsStore
// These will be removed once DatePicker inline works correctly
@State private var editDOB = false
@State private var editDOE = false
@State private var editDateTitle : String = ""
var body : some View {
let passportNrBinding = Binding<String>(get: {
settings.passportNumber
}, set: {
settings.passportNumber = $0.uppercased()
})
VStack {
NavigationLink( destination: DateView(date:$settings.dateOfBirth, title:editDateTitle), isActive: $editDOB) { Text("") }
NavigationLink( destination: DateView(date:$settings.dateOfExpiry, title:editDateTitle), isActive: $editDOE) { Text("") }
TextField("Passport number", text: passportNrBinding)
.textCase(.uppercase)
.modifier(ClearButton(text: passportNrBinding))
.textContentType(.name)
.foregroundColor(Color.primary)
.padding([.leading, .trailing])
.ignoresSafeArea(.keyboard, edges: .all)
Divider()
// Replace with DatePicker once it works correctly
HStack {
VStack {
Text( "Date of birth" )
Button(formatDate(settings.dateOfBirth)) {
editDateTitle = "Select date of birth"
editDOB = true
}
.padding(.horizontal, 15)
.padding(.vertical, 8)
.background(Color.black.opacity(0.07))
.cornerRadius(8)
}
Spacer()
VStack {
Text( "Passport expiry date" )
Button(formatDate(settings.dateOfExpiry)) {
editDateTitle = "Select passport expiry date"
editDOE = true
}
.padding(.horizontal, 15)
.padding(.vertical, 8)
.background(Color.black.opacity(0.07))
.cornerRadius(8)
}
}
.padding([.leading, .trailing])
Divider()
}
.ignoresSafeArea(.keyboard, edges: .bottom)
}
}
// This will be removed once DatePicker inline works correctly
extension MRZEntryView {
func formatDate( _ date : Date ) -> String {
let df = DateFormatter()
df.timeZone = TimeZone.init(secondsFromGMT: 0)
df.dateFormat = "dd MMM yyyy"
let dateStr = df.string(from:date)
return dateStr
}
}
#if DEBUG
struct MRZEntryView_Previews : PreviewProvider {
static var previews: some View {
let settings = SettingsStore()
return
Group {
NavigationView {
MRZEntryView()
}
.environmentObject(settings)
.environment( \.colorScheme, .light)
}
}
}
#endif

View File

@@ -0,0 +1,90 @@
//
// MRZScannerViewController.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 17/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import UIKit
import SwiftUI
import QKMRZScanner
// Wraps the QKMRZScanner component in a simple UIView that allows a scan of the MRZ area from a Passport/ID Card
struct MRZScanner: UIViewControllerRepresentable {
let completionHandler: (String,Date,Date) -> Void
func makeUIViewController(context: Context) -> MRZScannerViewController {
let vc = MRZScannerViewController()
vc.mrzScannerView.delegate = context.coordinator
return vc
}
func updateUIViewController(_ uiViewController: MRZScannerViewController, context: Context) {
}
func makeCoordinator() -> Coordinator {
return Coordinator(completionHandler: completionHandler)
}
final class Coordinator: NSObject, QKMRZScannerViewDelegate {
let completionHandler: (String,Date,Date) -> Void
init(completionHandler: @escaping (String,Date,Date) -> Void) {
self.completionHandler = completionHandler
}
func mrzScannerView(_ mrzScannerView: QKMRZScannerView, didFind scanResult: QKMRZScanResult) {
print(scanResult)
if let dob = scanResult.birthdate, let doe = scanResult.expiryDate {
completionHandler(scanResult.documentNumber, dob, doe)
}
}
}
}
// MARK: UIViewController implementation
class MRZScannerViewController: UIViewController {
let mrzScannerView = QKMRZScannerView()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Scan Passport"
self.navigationController?.title = "Scan passport"
let lbl = UILabel()
lbl.text = "Please scan the Machine Readable Zone."
lbl.numberOfLines = 0
self.view.addSubview(mrzScannerView)
self.view.addSubview(lbl)
mrzScannerView.translatesAutoresizingMaskIntoConstraints = false
mrzScannerView.topAnchor.constraint(equalTo: view.topAnchor, constant:50).isActive = true
mrzScannerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
mrzScannerView.widthAnchor.constraint(equalToConstant: view.bounds.width).isActive = true
mrzScannerView.heightAnchor.constraint(equalToConstant: view.bounds.width).isActive = true
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.leadingAnchor.constraint(equalTo: mrzScannerView.leadingAnchor, constant: 10).isActive = true
lbl.topAnchor.constraint(equalTo: mrzScannerView.bottomAnchor, constant: 10).isActive = true
lbl.trailingAnchor.constraint(equalTo: mrzScannerView.trailingAnchor, constant: 10).isActive = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
mrzScannerView.startScanning()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
mrzScannerView.stopScanning()
}
}
extension MRZScannerViewController : QKMRZScannerViewDelegate {
func mrzScannerView(_ mrzScannerView: QKMRZScannerView, didFind scanResult: QKMRZScanResult) {
print(scanResult)
}
}

View File

@@ -0,0 +1,214 @@
//
// 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
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:{ (nr,dob,doe) in
settings.passportNumber = nr
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
}
}
// 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

View File

@@ -0,0 +1,111 @@
//
// PassportSummaryView.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 17/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import SwiftUI
import NFCPassportReader
struct PassportSummaryView: View {
@EnvironmentObject var settings: SettingsStore
@State var passport: NFCPassportModel
var body: some View {
VStack {
PassportDetailsView(passport: passport)
.background( Color.primary.colorInvert() )
.cornerRadius(10)
.shadow(radius: 20)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.primary, lineWidth: 2)
)
.padding()
}
}
}
// Shows the Pzssport details
struct PassportDetailsView : View {
var passport: NFCPassportModel
var body: some View {
HStack(alignment: .top) {
VStack(alignment: .leading) {
Image(uiImage:passport.passportImage ?? UIImage(named:"head")!)
.resizable()
.renderingMode(.original)
.aspectRatio(contentMode: .fit)
.frame(width: 120, height: 180)
.padding([.leading], 10.0)
}
VStack {
HStack {
Text( passport.documentType)
Text( passport.issuingAuthority)
Text( passport.documentNumber)
Spacer()
}
HStack {
VStack(alignment: .leading) {
Text( passport.lastName)
Text( passport.firstName)
Text( passport.nationality)
Text( passport.dateOfBirth)
Text( passport.gender)
Text( passport.documentExpiryDate )
}
Spacer()
VStack {
if !passport.passportDataNotTampered {
Image( systemName:"exclamationmark").foregroundColor(.red)
.font(.system(size: 20))
Text( "Tampered")
.font(.caption)
.foregroundColor(.red)
.padding(.bottom)
}
if passport.passportCorrectlySigned && passport.documentSigningCertificateVerified {
Image( systemName:"checkmark.seal").foregroundColor(.green)
.font(.system(size: 25))
Text( "Genuine")
.font(.caption)
.foregroundColor(.green)
} else {
Image( systemName:"xmark.seal").foregroundColor(.red)
.font(.system(size: 25))
.padding([.leading,.trailing], 15)
Text( "Not Genuine")
.font(.caption)
.foregroundColor(.red)
}
}
.padding(.trailing)
}
}
.padding(.top, 8)
}
}
}
struct PassportSummaryView_Previews: PreviewProvider {
static var previews: some View {
let settings = SettingsStore()
let passport = NFCPassportModel()
return Group {
PassportSummaryView(passport:passport)
.environment( \.colorScheme, .light)
.environmentObject(settings)
}
}
}

View File

@@ -0,0 +1,73 @@
//
// PassportView.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 30/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import SwiftUI
import NFCPassportReader
struct PassportView : View {
@EnvironmentObject var settings: SettingsStore
@State private var showExportPassport : Bool = false
var body: some View {
VStack {
NavigationLink( destination: ExportPassportView(), isActive: $showExportPassport) { Text("") }
PassportSummaryView(passport:settings.passport!)
HStack {
Button(action: {showExportPassport.toggle()}) {
Label("Export passport", systemImage: "square.and.arrow.up")
}
.padding()
Spacer()
Button(action: {shareLogs()}) {
Label("Share logs", systemImage: "square.and.arrow.up")
}
.padding()
}
DetailsView(passport:settings.passport!)
}
.navigationTitle("Passport Details")
.navigationBarTitleDisplayMode(.inline)
}
}
extension PassportView {
func shareLogs() {
hideKeyboard()
PassportUtils.shareLogs()
}
}
#if DEBUG
struct PassportView_Previews : PreviewProvider {
static var previews: some View {
let passport : NFCPassportModel
if let file = Bundle.main.url(forResource: "passport", withExtension: "json"),
let data = try? Data(contentsOf: file),
let json = try? JSONSerialization.jsonObject(with: data, options: []),
let arr = json as? [String:String] {
passport = NFCPassportModel(from: arr)
} else {
passport = NFCPassportModel()
}
let settings = SettingsStore()
settings.passport = passport
return NavigationView {
PassportView()
.environmentObject(settings)
.environment( \.colorScheme, .light)
.navigationTitle("WEEE")
.navigationBarTitleDisplayMode(.inline)
}
}
}
#endif

View File

@@ -0,0 +1,63 @@
//
// SettingsView.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 10/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import SwiftUI
import NFCPassportReader
struct SettingsView: View {
@EnvironmentObject var settings: SettingsStore
private var logLevels = ["Verbose", "Debug", "Info", "Warning", "Error"]
var body: some View {
Form {
Section(header: Text("Logging settings")) {
Toggle(isOn: $settings.shouldCaptureLogs) {
Text("Should capture logs")
}
Picker(
selection: $settings.logLevel,
label: Text("Logging level")
) {
ForEach(LogLevel.allCases, id: \.self) {
Text(logLevels[$0.rawValue] ).tag($0)
}
}
}
Section(header: Text("Passport reading settings")) {
Toggle(isOn: $settings.useNewVerificationMethod) {
Text("Use new Passive Authentication")
}
.padding(.bottom)
VStack {
Toggle(isOn: $settings.savePassportOnScan) {
Text("Save passport on scan & import")
}
HStack {
Text( "Note - currently stored as JSON on device\nWill not be backed up to iCloud" )
.font(.footnote)
Spacer()
}
}
}
}
.navigationBarTitle(Text("Settings"))
}
}
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
let settings = SettingsStore()
SettingsView()
.environmentObject(settings)
}
}

View File

@@ -0,0 +1,152 @@
//
// StorePassportView.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 17/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import SwiftUI
import NFCPassportReader
struct StoredPassportView: View {
@EnvironmentObject var settings: SettingsStore
@State private var showImport : Bool = false
@State private var showDetails = false
@State private var storedPassports = [URL]()
var body: some View {
ZStack {
NavigationLink( destination: PassportView(), isActive: $showDetails) { Text("") }
VStack {
List {
ForEach(self.storedPassports, id: \.self) { item in
Button(action:{
if let data = try? Data(contentsOf: item),
let passport = loadPassport(data:data) {
self.settings.passport = passport
self.showDetails = true
}
}) {
HStack {
Text(item.deletingPathExtension().lastPathComponent)
Spacer()
}
}
.foregroundColor(.primary)
}
.onDelete(perform: deletePassport)
}
Spacer()
if ( !settings.savePassportOnScan ) {
Text( "Imported or scanned passports are currently not being saved.\nThis can be changed in Settings" )
.multilineTextAlignment(.center)
.padding(10)
}
}
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
Menu {
Button(action: {self.showImport.toggle()}) {
Label("Import passport", systemImage: "doc")
}
} label: {
Image(systemName: "square.and.arrow.down")
.foregroundColor(Color.secondary)
}
}
}
.fileImporter(
isPresented: $showImport, allowedContentTypes: [.json,.text],
allowsMultipleSelection: false
) { result in
hideKeyboard()
guard let selectedFile: URL = try? result.get().first else { return }
if selectedFile.startAccessingSecurityScopedResource() {
defer { selectedFile.stopAccessingSecurityScopedResource() }
importFile( url:selectedFile )
} else {
print("Unable to read file contents - denied")
}
}
.onAppear() {
loadStoredPassports()
}
}
}
extension StoredPassportView {
func loadStoredPassports() {
do {
let urls = try FileManager.default.contentsOfDirectory(at: FileManager.cachesFolder, includingPropertiesForKeys: nil, options: [])
storedPassports = urls.filter { $0.pathExtension == "json" }
} catch {
print("Could not search for urls of files in documents directory: \(error)")
}
}
func importFile( url : URL ) {
Log.logLevel = settings.logLevel
Log.storeLogs = settings.shouldCaptureLogs
Log.clearStoredLogs()
do {
let data = try Data(contentsOf: url )
if let passport = loadPassport( data: data ) {
if ( settings.savePassportOnScan ) {
// Save passport to docs folder
let savedPath = FileManager.cachesFolder.appendingPathComponent("\(passport.documentNumber).json")
try? data.write(to: savedPath, options: .completeFileProtection)
}
self.settings.passport = passport
self.showDetails = true
}
} catch {
// Handle failure.
print("Unable to read file contents")
print(error.localizedDescription)
}
}
func loadPassport( data: Data) -> NFCPassportModel? {
let json = try? JSONSerialization.jsonObject(with: data, options: [])
if let arr = json as? [String:String] {
let passport = NFCPassportModel(from: arr)
let masterListURL = Bundle.main.url(forResource: "masterList", withExtension: ".pem")!
passport.verifyPassport(masterListURL: masterListURL)
return passport
}
return nil
}
func deletePassport( at offsets: IndexSet) {
let fm = FileManager.default
offsets.forEach {
let url = storedPassports[$0]
try? fm.removeItem(at: url)
}
storedPassports.remove(atOffsets: offsets)
}
}
struct StoredPassportView_Previews: PreviewProvider {
static var previews: some View {
let settings = SettingsStore()
StoredPassportView()
.environmentObject(settings)
}
}

View File

@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFdDCCA1wCCQDFcc+Un66UsDANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJH
QjESMBAGA1UECAwJU29tZXdoZXJlMRIwEAYDVQQHDAlUaGlzIENpdHkxCzAJBgNV
BAoMAk5BMQswCQYDVQQLDAJOQTENMAsGA1UEAwwEdGVzdDEcMBoGCSqGSIb3DQEJ
ARYNdGVzdEB0ZXN0LmNvbTAeFw0xOTA2MjgxMzAyNTlaFw0yMDA2MjcxMzAyNTla
MHwxCzAJBgNVBAYTAkdCMRIwEAYDVQQIDAlTb21ld2hlcmUxEjAQBgNVBAcMCVRo
aXMgQ2l0eTELMAkGA1UECgwCTkExCzAJBgNVBAsMAk5BMQ0wCwYDVQQDDAR0ZXN0
MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRlc3QuY29tMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEA4sTb0Xd20ATso1VffMGK/N2TuEcsZvQkbhr75dd6f77y
3h5r5ikFCcppGwlgEy6/wn0iEiv4iGDm7cxvVSfwZ+Bm7nbYZVHl8G7vYUZRdLnh
tcUpfa+jPyrQ+XXfTygjoE2shAs8Wv+uB/O0oLbltdx5HMcitHj6MkIz3bs6hY4Z
2kSy6JB1Iw0GwB6vfHbD8MUkn0Aqxwdrd5ks61lVpYx3JyvhUXVjC/9rE+dk6+cN
16WvRhCEH6aClgWcfkSfDtZ85ltcyL3glAqX1YMLl4CFRKZ3NLVgXjHj/QYvAXJq
yWoNbAQvCFv5WK8/JjtAzFYKwjGGUhG5tL6etLrfDF/RudrLDIS2Js9cTMbbB10O
UehVksJvYQBaj8VRSsI7GrGydecfBixwqYD5DxcPzZsGcsfzuQYkotG+poDhjRE4
Ow9zqIG20hgnBn3VTbP21yc4woYLzIK7M7MdWDiDqqcrLwtxolIk6R6lc/3wVR8i
19F5HFwKM0RW3cWZhXqCHrpYBzj8gx7hJAKSFZWDygLFH8h1LtMx93kstxPR7FA6
99737YU1HF0j7kjKJAIQqoJ43ftUN9q8sDN/rRpEgKnRUuFrvcaTdTZCJ7NT3u6p
edC+8rdbKQO6TX0JC8PEl1FjTiVnb3wpMpWPYaI3hspSN/CMUunypoKRJ5IgSK8C
AwEAATANBgkqhkiG9w0BAQsFAAOCAgEAJtrliSVzukKjUJR3AnZrgcnWZ8kTGH9E
TSfpmc9IkMvwWvBs/vgpKMm3XVzTM1RcI/pQYR9Y4beXf4wgTQIg1I2kWLVcRMzU
rwFWl2/h6uA6G+tJpaVVy30rPcYGqLmBqzCCRb3sf/H77/6nknafA4jMASlIWnTE
R7HBd0cJa50jaTgFaIe734oIMqaAZvFFKK5KDnxSBMHwUf7ej1ibT/rB5Ry80qpJ
FsKlxpjaeUDWsnnjR4Z98rjDsWOSI8tUTStdnPYTw/6VFsf+zPKwE2u2Qe7SXg3S
eUoCQqWYaSMqmAfTVJx7zIDIvxa8+LmtijeqYUZcfnos0xxTEeJ6EH65rAPOi23x
1pg8Z28NAAinqFAo74Hr2hTKESmq0mykF/XP4wLhhaxYHy+FFwm984IIpqbKJmuY
/LapNMi6hZXRXft5PQYe57ZXD9/Lp8pn+19nX0nyv5agius0/R4M3CvuPbdose64
jm+2325MQAhihrHmcU0YIt1kwSMuiYOqwD5plx+yenCIhdR6Oa5WX7Xf/hiKZult
5UVNVxfG1tZbsk/u2iwd2fDMBGQ3/D36REA5el6GUPpBOaa6gVmpS38qRCTVTBqH
NY8Qy6N+AHsahMtMakwo/EWsDsGTajcrvXX1psLgpRdMNeXgU3nNzJ+wPMUPCTFB
ZhNynFjgmgY=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,10 @@
# NFCPassportReaderApp
This is a sample app showing how to read a e-passport using the NFCPassportReader library
This is purely a demonstration - you have to manually enter the passport number, the date of birth and passport expiry date but it does calculate the checksums for you.
Also, the last entered passport details are saved to UserDefaults to save you having to re-enter them each time.
It reads my passport (and others I've been able to test) fine, however your milage may vary.

View File

@@ -0,0 +1,154 @@
//
// DataGroupParsingTests.swift
//
//
// Created by Andy Qua on 15/06/2019.
//
import Foundation
import XCTest
import OpenSSL
@testable import NFCPassportReader
final class DataGroupParsingTests: XCTestCase {
func testDatagroup1Parsing() {
// Random generated test MRZ
let mrz = "P<GBRTHATCHER<<BOB<<<<<<<<<<<<<<<<<<<<<<<<<<7125143269GBR3906022M1601013<<<<<<<<<<<<<<08"
let mrzBin = [UInt8](mrz.data(using: .utf8)!)
let tag = try! [0x5F,0x1F] + toAsn1Length(mrzBin.count) + mrzBin
let dg1 = try! [0x61] + toAsn1Length(tag.count) + tag
let dgp = DataGroupParser()
XCTAssertNoThrow(try dgp.parseDG(data: dg1)) { dg in
XCTAssertNotNil(dg)
XCTAssertTrue( dg is DataGroup1 )
}
}
func testDatagroup2ParsingJPEG2000() {
// This is a cut down version of the DG2 record. It contains everything up to the end of the image header - no actuall image data as its way too big to include here
// I've also adjusted the record lengths accordingly
let dg2 = hexRepToBin("75617F61570201017F6082203FA1128002010081010282010087020101880200085F2E38464143003031300000002026000100002018000000000000000000010000000000000001000000000000000000000000000C6A5020200D0A")
let dgp = DataGroupParser()
XCTAssertNoThrow(try dgp.parseDG(data: dg2)) { dg in
XCTAssertNotNil(dg)
XCTAssertTrue( dg is DataGroup2 )
}
}
func testDatagroup2ParsingJPEG() {
// This is a cut down version of the DG2 record. It contains everything up to the begininnig of what would be the image data - no actual image data as its way too big to include here
// I've also adjusted the record lengths accordingly
let dg2 = hexRepToBin("75617F618220470201017F6082203FA1128002010081010282010087020101880200085F2E3846414300303130000000202600010000201800000000000000000001000000000000000100000000000000000000FFD8FFE000104A464946")
let dgp = DataGroupParser()
XCTAssertNoThrow(try dgp.parseDG(data: dg2)) { dg in
XCTAssertNotNil(dg)
XCTAssertTrue( dg is DataGroup2 )
}
}
func testDatagroup7ParsingJPEG() {
// This is a cut down version of the DG7 record. It contains everything up to the end of the image header - no actuall image data as its way too big to include here
// I've also adjusted the record lengths accordingly
let dg7 = hexRepToBin("678220060201015F4300")
let dgp = DataGroupParser()
XCTAssertNoThrow(try dgp.parseDG(data: dg7)) { dg in
XCTAssertNotNil(dg)
XCTAssertTrue( dg is DataGroup7 )
}
}
func testDatagroup11Parsing() {
// This is a cut down version of the DG7 record. It contains everything up to the end of the image header - no actuall image data as its way too big to include here
// I've also adjusted the record lengths accordingly
let dg11Val = hexRepToBin("6B305C065F0E5F2B5F115F0E0C546573743C3C5465737465725F2B0831393730313230315F110B4E6F727468616D70746F6E")
let dgp = DataGroupParser()
XCTAssertNoThrow(try dgp.parseDG(data: dg11Val)) { dg in
XCTAssertNotNil(dg)
XCTAssertTrue( dg is DataGroup11 )
let dg11 = dg as! DataGroup11
XCTAssertEqual(dg11.fullName, "Test<<Tester")
XCTAssertEqual(dg11.dateOfBirth, "19701201")
XCTAssertEqual(dg11.placeOfBirth, "Northampton")
}
}
func testDatagroup12Parsing() {
// This is a cut down version of the DG7 record. It contains everything up to the end of the image header - no actuall image data as its way too big to include here
// I've also adjusted the record lengths accordingly
let dg12Val = hexRepToBin("6C1A5C045F265F195F260832303138303332365F1906544553544552")
let dgp = DataGroupParser()
XCTAssertNoThrow(try dgp.parseDG(data: dg12Val)) { dg in
XCTAssertNotNil(dg)
XCTAssertTrue( dg is DataGroup12 )
let dg12 = dg as! DataGroup12
XCTAssertEqual(dg12.issuingAuthority, "TESTER")
XCTAssertEqual(dg12.dateOfIssue, "20180326")
}
}
func testDatagroup15Parsing() {
// This is a cut down version of the DG7 record. It contains everything up to the end of the image header - no actuall image data as its way too big to include here
// I've also adjusted the record lengths accordingly
let dg15Val = hexRepToBin("6F820137308201333081EC06072A8648CE3D02013081E0020101302C06072A8648CE3D0101022100A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377304404207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9042026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B60441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997022100A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7020101034200049BD24313046EB43CC4652B6FC1AA00E76B5405F4E7016521E95BE53B9C5BAE5A1410F12CF3AE23F886EFCEDE89F7C63AD9CA9E5C6C05DE902DB70F2EB2341F9D")
let dgp = DataGroupParser()
XCTAssertNoThrow(try dgp.parseDG(data: dg15Val)) { dg in
XCTAssertNotNil(dg)
XCTAssertTrue( dg is DataGroup15 )
let dg15 = dg as? DataGroup15
XCTAssertTrue( dg15?.ecdsaPublicKey != nil || dg15?.rsaPublicKey != nil )
}
}
func testCOMDatagroupParsing() {
let com = hexRepToBin("601A5F0104303130375F36063034303030305C08617563676B6C6E6F")
let dgp = DataGroupParser()
XCTAssertNoThrow(try dgp.parseDG(data: com)) { dg in
XCTAssertNotNil(dg)
XCTAssertTrue( dg is COM )
guard let com = dg as? COM else { XCTFail(); return }
// Version should be 0x30313037 or [0x30, 0x31, 0x30, 0x37]
XCTAssertEqual( com.version, "1.7")
// Unicode version should be 0x303430303030 or [0x30, 0x34, 0x30, 0x30, 0x30, 0x30]
XCTAssertEqual( com.unicodeVersion, "4.0.0")
// Datagroups present are COM, DG1, DG2, DG3, DG7, DG11, DG12, DG14, DG15
XCTAssertEqual( com.dataGroupsPresent,["DG1", "DG2", "DG3", "DG7", "DG11", "DG12", "DG14", "DG15"])
}
}
static var allTests = [
("testDatagroup1Parsing", testDatagroup1Parsing),
("testDatagroup2Parsing", testDatagroup2ParsingJPEG2000),
("testDatagroup2ParsingJPEG", testDatagroup2ParsingJPEG),
("testCOMDatagroupParsing", testCOMDatagroupParsing),
]
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -0,0 +1,260 @@
import XCTest
import CoreNFC
import OpenSSL
@testable import NFCPassportReader
public func XCTAssertNoThrow<T>(_ expression: @autoclosure () throws -> T, _ message: String = "", file: StaticString = #file, line: UInt = #line, also validateResult: (T) -> Void) {
func executeAndAssignResult(_ expression: @autoclosure () throws -> T, to: inout T?) rethrows {
to = try expression()
}
var result: T?
XCTAssertNoThrow(try executeAndAssignResult(expression(), to: &result), message, file: file, line: line)
if let r = result {
validateResult(r)
}
}
final class NFCPassportReaderTests: XCTestCase {
func testBinToHexRep() {
let val : [UInt8] = [0x12, 0x24, 0x55, 0x77]
XCTAssertEqual( binToHexRep(val), "12245577" )
}
func testHexRepToBin() {
let val : [UInt8] = [0x12, 0x24, 0x55, 0x77]
XCTAssertEqual( hexRepToBin("12245577"), val )
}
func testAsn1Length() {
// Test < 127
XCTAssertNoThrow(try asn1Length([0x32])) { (len, offset) in
XCTAssertEqual(len, 0x32)
XCTAssertEqual(offset, 1)
}
// Test 127
XCTAssertNoThrow(try asn1Length([0x7f])) { (len, offset) in
XCTAssertEqual(len, 0x7f)
XCTAssertEqual(offset, 1)
}
// Test 128
XCTAssertNoThrow(try asn1Length([0x81, 0x80])) { (len, offset) in
XCTAssertEqual(len, 128)
XCTAssertEqual(offset, 2)
}
// Test 255
XCTAssertNoThrow(try asn1Length([0x81, 0xFF])) { (len, offset) in
XCTAssertEqual(len, 255)
XCTAssertEqual(offset, 2)
}
// Test 256
XCTAssertNoThrow(try asn1Length([0x82, 0x01,0x00])) { (len, offset) in
XCTAssertEqual(len, 256)
XCTAssertEqual(offset, 3)
}
// Test 1000
XCTAssertNoThrow(try asn1Length([0x82, 0x03, 0xE8])) { (len, offset) in
XCTAssertEqual(len, 1000)
XCTAssertEqual(offset, 3)
}
// Test Max value - 65535
XCTAssertNoThrow(try asn1Length([0x82, 0xff, 0xff])) { (len, offset) in
XCTAssertEqual(len, 65535)
XCTAssertEqual(offset, 3)
}
// Test Too Big
XCTAssertThrowsError(try toAsn1Length(65536))
}
func testToASNLength() {
// Test < 127
XCTAssertNoThrow(try toAsn1Length(50)) { data in
XCTAssertEqual(data.count, 1)
XCTAssertEqual(data[0], 0x32)
}
// Test 127
XCTAssertNoThrow(try toAsn1Length(127)) { data in
XCTAssertEqual(data.count, 1)
XCTAssertEqual(data[0], 0x7f)
}
// Test 128
XCTAssertNoThrow(try toAsn1Length(128)) { data in
XCTAssertEqual(data.count, 2)
XCTAssertEqual(data[0], 0x81)
XCTAssertEqual(data[1], 0x80)
}
// Test 255
XCTAssertNoThrow(try toAsn1Length(255)) { data in
XCTAssertEqual(data.count, 2)
XCTAssertEqual(data[0], 0x81)
XCTAssertEqual(data[1], 0xff)
}
// Test 256
XCTAssertNoThrow(try toAsn1Length(256)) { data in
XCTAssertEqual(data.count, 3)
XCTAssertEqual(data[0], 0x82)
XCTAssertEqual(data[1], 0x01)
XCTAssertEqual(data[2], 0x00)
}
// Test 1000
XCTAssertNoThrow(try toAsn1Length(1000)) { data in
XCTAssertEqual(data.count, 3)
XCTAssertEqual(data[0], 0x82)
XCTAssertEqual(data[1], 0x03)
XCTAssertEqual(data[2], 0xE8)
}
// Test Max value - 65535
XCTAssertNoThrow(try toAsn1Length(65535)) { data in
XCTAssertEqual(data.count, 3)
XCTAssertEqual(data[0], 0x82)
XCTAssertEqual(data[1], 0xff)
XCTAssertEqual(data[2], 0xff)
}
// Test Too Big
XCTAssertThrowsError(try toAsn1Length(65536))
}
func testDES3Encryption() {
let msg = [UInt8]("maryhadalittlelambaaaaaa".data(using: .utf8)!)
let iv : [UInt8] = [0, 0, 0, 0, 0, 0, 0, 0]
let key : [UInt8] = [191, 73, 56, 112, 158, 148, 146, 127, 157, 76, 117, 8, 239, 128, 87, 42]
let enc = tripleDESEncrypt(key: key, message: msg, iv: iv)
Log.debug("KEY: \(binToHexRep(key))")
Log.debug("MSG: \(binToHexRep(msg))")
Log.debug("ENC: \(binToHexRep(enc))")
XCTAssertEqual( binToHexRep(enc), "4DAF068AB358BC9E8F5E916D3DEDE750D92315370E44D9B3" )
}
func testDES3Decryption() {
let enc = hexRepToBin("4DAF068AB358BC9E8F5E916D3DEDE750D92315370E44D9B3")
let iv : [UInt8] = [0, 0, 0, 0, 0, 0, 0, 0]
let key : [UInt8] = [191, 73, 56, 112, 158, 148, 146, 127, 157, 76, 117, 8, 239, 128, 87, 42]
let dec = tripleDESDecrypt(key: key, message: enc, iv: iv)
Log.debug("KEY: \(binToHexRep(key))")
Log.debug("ENC: \(binToHexRep(enc))")
Log.debug("DEC: \(binToHexRep(dec))")
let val = String(data:Data(dec), encoding:.utf8)
XCTAssertEqual( val, "maryhadalittlelambaaaaaa" )
}
func testSecureMessagingProtect() {
let KSenc = hexRepToBin("8FDCFE759E40A4DF4575160B3BFB79FB")
let KSmac = hexRepToBin("2AE92531E55707D9C4CEF8C2D6E5AD70")
let ssc = hexRepToBin("73061884A0E57AA7")
let sm = SecureMessaging(ksenc: KSenc, ksmac: KSmac, ssc: ssc)
let data : [UInt8] = [0x00, 0xA4, 0x02, 0x0C, 0x02, 0x01, 0x01, 0x00]
let apdu = NFCISO7816APDU(data:Data(data))!
let protApdu = try! sm.protect( apdu: apdu )
XCTAssertNotNil(protApdu.data )
XCTAssertEqual( protApdu.instructionClass, 0x0c )
XCTAssertEqual( protApdu.instructionCode, 0xA4 )
XCTAssertEqual( protApdu.p1Parameter, 0x02 )
XCTAssertEqual( protApdu.p2Parameter, 0x0c )
let hexDataRep = binToHexRep( [UInt8](protApdu.data!))
XCTAssertEqual( hexDataRep, "870901CC69089F8F1AB4698E08B6334B3ABD5A9E09" )
XCTAssertEqual( protApdu.expectedResponseLength, 0 )
}
func testSecureMessagingUnprotectNoData() {
// Note - same keys as above but SSC incremented by 1 as per spec
let KSenc = hexRepToBin("8FDCFE759E40A4DF4575160B3BFB79FB")
let KSmac = hexRepToBin("2AE92531E55707D9C4CEF8C2D6E5AD70")
let ssc = hexRepToBin("73061884A0E57AA8")
let sm = SecureMessaging(ksenc: KSenc, ksmac: KSmac, ssc: ssc)
let data : [UInt8] = hexRepToBin("990290008E08C61E440E5DD415469000")
let protRespApdu = ResponseAPDU(data: data, sw1: 0x90, sw2: 0x00)
XCTAssertNoThrow(try sm.unprotect( rapdu: protRespApdu )) { rapdu in
XCTAssertEqual(binToHexRep(rapdu.data), "")
XCTAssertEqual( rapdu.sw1, 0x90 )
XCTAssertEqual( rapdu.sw2, 0x00 )
}
}
func testSecureMessagingUnprotectWithData() {
let KSenc = hexRepToBin("8FDCFE759E40A4DF4575160B3BFB79FB")
let KSmac = hexRepToBin("2AE92531E55707D9C4CEF8C2D6E5AD70")
let ssc = hexRepToBin("73061884A0E57AAA")
let sm = SecureMessaging(ksenc: KSenc, ksmac: KSmac, ssc: ssc)
let data : [UInt8] = hexRepToBin("87090156D0EFCC887F8973990290008E08D6B9C0DA21DC965F9000")
let protRespApdu = ResponseAPDU(data: data, sw1: 0x90, sw2: 0x00)
XCTAssertNoThrow(try sm.unprotect( rapdu: protRespApdu )) { rapdu in
XCTAssertEqual(binToHexRep(rapdu.data), "615B5F1F")
XCTAssertEqual( rapdu.sw1, 0x90 )
XCTAssertEqual( rapdu.sw2, 0x00 )
}
}
func testConvertECDSAPlainTODer() {
let sigText = "67e147aac644325792dfa0b1615956dc4ed54e8cd859341571db98003431936e0651e9a3cdbcea3c8accd75a6f6bf07eb6bcf9ad1728e21aa854049e634e6fbf"
let sig = hexRepToBin(sigText)
let ecsig = ECDSA_SIG_new()
defer { ECDSA_SIG_free(ecsig) }
sig.withUnsafeBufferPointer { (unsafeBufPtr) in
let unsafePointer = unsafeBufPtr.baseAddress!
let r = BN_bin2bn(unsafePointer, 32, nil)
let s = BN_bin2bn(unsafePointer + 32, 32, nil)
ECDSA_SIG_set0(ecsig, r, s)
}
//print( "Sig - \(ecsig)" )
var derEncodedSignature: UnsafeMutablePointer<UInt8>? = nil
let derLength = i2d_ECDSA_SIG(ecsig, &derEncodedSignature)
var derBytes = [UInt8](repeating: 0, count: Int(derLength))
for b in 0..<Int(derLength) {
derBytes[b] = derEncodedSignature![b]
}
XCTAssertNoThrow(try OpenSSLUtils.ASN1Parse(data: Data(derBytes)), "Successfully parsed" )
}
static var allTests = [
("testBinToHexRep", testBinToHexRep),
("testHexRepToBin", testHexRepToBin),
("testAsn1Length", testAsn1Length),
("testToASNLength", testToASNLength),
("testDES3Encryption", testDES3Encryption),
("testDES3Decryption", testDES3Decryption),
("testSecureMessagingProtect", testSecureMessagingProtect),
("testSecureMessagingUnprotectNoData", testSecureMessagingUnprotectNoData),
("testSecureMessagingUnprotectWithData", testSecureMessagingUnprotectWithData),
]
}

View File

@@ -0,0 +1,112 @@
//
// PACETests.swift
// NFCPassportReaderAppTests
//
// Created by Andy Qua on 09/03/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import Foundation
import OpenSSL
import NFCPassportReader
// Currently not actual tests - maybe at some point but currently just a holding place for some testing stuff I didn't want to get rid of!
// Hopefully will re-organise into proper tests!
class PACETests {
func testDHKeyMapping() -> OpaquePointer {
var resp : [UInt8] = [0x7c, 0x82, 0x01, 0x04, 0x82, 0x82, 0x01, 0x00, 0x70, 0x5a, 0xce, 0x59, 0x1c, 0x0b, 0xe1, 0xa1, 0x19, 0x5b, 0x14, 0x1c, 0x97, 0x38, 0x51, 0xde, 0x07, 0xf1, 0x66, 0xa6, 0x74, 0x7d, 0x18, 0x9f, 0x5e, 0x3b, 0x51, 0x0d, 0xbf, 0xff, 0xe2, 0x16, 0xca, 0x96, 0x0e, 0x63, 0x8d, 0x16, 0x49, 0x5f, 0x53, 0x15, 0x60, 0x9b, 0xd6, 0x6e, 0x41, 0x52, 0x69, 0x08, 0x6a, 0x37, 0x6f, 0xf4, 0xd6, 0x14, 0xcc, 0x9b, 0x9d, 0x57, 0x0e, 0x64, 0x53, 0x49, 0xa3, 0xe6, 0xd6, 0xdb, 0x80, 0xfb, 0x08, 0x39, 0xb7, 0x66, 0xdc, 0xcc, 0xb1, 0xda, 0xe0, 0xe5, 0x21, 0xaf, 0xca, 0x9b, 0xdc, 0xe2, 0xc2, 0xd9, 0xb2, 0x58, 0xd8, 0x22, 0x94, 0x23, 0x76, 0x0f, 0x40, 0x74, 0xea, 0x73, 0xf1, 0xa4, 0x4d, 0xdd, 0xfc, 0x72, 0x27, 0xda, 0x8b, 0xce, 0x03, 0x75, 0x98, 0xfe, 0x38, 0x81, 0x1d, 0x45, 0xc9, 0xd6, 0x8b, 0x81, 0x8d, 0xef, 0xdc, 0xda, 0x5f, 0xc8, 0x1a, 0x12, 0x51, 0x0d, 0xfa, 0x02, 0x67, 0xf9, 0x89, 0x5e, 0x6d, 0xb8, 0x55, 0x43, 0xb0, 0x71, 0x47, 0x0d, 0xf0, 0x27, 0xda, 0x62, 0x3b, 0xd4, 0xa9, 0x4d, 0x5e, 0xde, 0xba, 0x81, 0x7b, 0xde, 0x79, 0x5f, 0x57, 0xbf, 0x0b, 0x35, 0xf4, 0x29, 0x6f, 0x5a, 0xd4, 0xe8, 0xd9, 0xfd, 0x2c, 0x87, 0x2e, 0x84, 0x26, 0x10, 0x9d, 0x1b, 0x50, 0xe4, 0x1d, 0xb3, 0x91, 0x02, 0xac, 0xa4, 0x58, 0x52, 0x61, 0x77, 0x1f, 0x97, 0x02, 0xd6, 0x48, 0x90, 0x1f, 0x69, 0xb6, 0xa0, 0x53, 0x53, 0xf9, 0xcb, 0x57, 0x01, 0xfe, 0x9f, 0xf2, 0xa1, 0x29, 0x60, 0x3f, 0xa5, 0xa4, 0xf6, 0x3d, 0xf1, 0xc6, 0x7a, 0xf3, 0xc6, 0xfd, 0x11, 0x6c, 0x38, 0xe8, 0x91, 0x82, 0x3c, 0xd9, 0x33, 0xef, 0x57, 0x9e, 0x6c, 0x04, 0x10, 0x65, 0x6a, 0xc5, 0xaa, 0x1f, 0xc4, 0x50, 0x1d, 0xe6, 0xbd, 0x27, 0xe0, 0x79, 0x30, ]
let mappingKey : OpaquePointer = EVP_PKEY_new()
let dhKey = DH_get_2048_256()
let rc = DH_generate_key(dhKey)
EVP_PKEY_set1_DH(mappingKey, dhKey)
let pubKeyData = try! unwrapDO( tag:0x7c, wrappedData:resp)
let encodedPublicKey = try! unwrapDO(tag: 0x82, wrappedData: pubKeyData)
let piccMappingPublicKey = OpenSSLUtils.decodePublicKeyFromBytes(pubKeyData: encodedPublicKey, params: mappingKey)!
let asecret = OpenSSLUtils.computeSharedSecret(privateKeyPair: mappingKey, publicKey: piccMappingPublicKey)
let dh_mapping_key = EVP_PKEY_get1_DH(mappingKey)
print( "DONE" )
let piccNonce : [UInt8] = [0xed, 0xb6, 0xc4, 0x07, 0x04, 0xae, 0x98, 0xfd,]
let bn_nonce = BN_bin2bn(piccNonce, Int32(piccNonce.count), nil);
let bn = BN_bin2bn(encodedPublicKey, Int32(encodedPublicKey.count), nil);
var secret = [UInt8](repeating: 0, count: Int(DH_size(dh_mapping_key)))
DH_compute_key( &secret, bn, dh_mapping_key)
let bn_h = BN_bin2bn(secret, Int32(secret.count), nil);
var p : OpaquePointer? = nil
var q : OpaquePointer? = nil
var g : OpaquePointer? = nil
DH_get0_pqg(dh_mapping_key, &p, &q, &g);
// map to new generator#imageLiteral(resourceName: "simulator_screenshot_33B42CB4-EA6E-47B4-A2A2-D96CA95B0F15.png")
let bn_g = BN_new()
let new_g = BN_new()
// bn_g = g^nonce mod p
// ephemeral_key->g = bn_g mod p * h => (g^nonce mod p) * h mod p
let bn_ctx = BN_CTX_new()
let rc3 = BN_mod_exp(bn_g, g, bn_nonce, p, bn_ctx)
let rc4 = BN_mod_mul(new_g, bn_g, bn_h, p, bn_ctx)
let ephemeral_key = DHparams_dup(dh_mapping_key)
let rc5 = DH_set0_pqg(ephemeral_key, BN_dup(p), BN_dup(q), BN_dup(new_g))
BN_free( bn_nonce )
BN_free( bn )
BN_free(bn_h)
BN_free(bn_g)
BN_free(new_g)
BN_CTX_free(bn_ctx)
// Set the ephemeral params
let ephemeralParams = EVP_PKEY_new()
let rc6 = EVP_PKEY_set1_DH(ephemeralParams, ephemeral_key)
EVP_PKEY_free(mappingKey)
var ephemeralKeyPair : OpaquePointer? = nil
let pctx = EVP_PKEY_CTX_new(ephemeralParams, nil)
EVP_PKEY_keygen_init(pctx)
EVP_PKEY_keygen(pctx, &ephemeralKeyPair)
EVP_PKEY_CTX_free(pctx)
EVP_PKEY_free( ephemeralParams )
let publicKey = OpenSSLUtils.getPublicKeyData( from: ephemeralKeyPair! )
print( "Ephemeral public key - \(binToHexRep(publicKey!, asArray: true))")
return ephemeralKeyPair!
}
func doTest() {
var passportEphKey : [UInt8] = [0x01, 0xd5, 0xa8, 0xb2, 0xea, 0xb6, 0x18, 0x55, 0x34, 0x24, 0xfe, 0x25, 0x10, 0x15, 0xaf, 0xc3, 0x85, 0xda, 0x13, 0x94, 0xe7, 0x2b, 0x76, 0x81, 0x2d, 0x8c, 0xe0, 0x6f, 0x60, 0x65, 0xfb, 0x33, 0x8c, 0x5b, 0x2d, 0xc9, 0x38, 0x11, 0xb3, 0x80, 0xd7, 0x49, 0xaa, 0xeb, 0x36, 0xa5, 0xdd, 0xa0, 0x78, 0x33, 0xe0, 0xd5, 0x52, 0xd4, 0x4e, 0xaa, 0xf0, 0xa3, 0x5f, 0xd3, 0x9c, 0x89, 0x44, 0xca, 0xac, 0xf1, 0x7a, 0x1e, 0xd4, 0x61, 0x58, 0x1d, 0x81, 0x83, 0xb6, 0x98, 0x85, 0x15, 0x2d, 0xeb, 0xd7, 0xc2, 0x66, 0xe4, 0xe0, 0xd1, 0x73, 0x1c, 0x52, 0xd7, 0x3a, 0x33, 0x20, 0x1f, 0x64, 0x8b, 0x85, 0xb7, 0xe5, 0xbb, 0x09, 0xe8, 0x8a, 0xd3, 0x79, 0x8c, 0x23, 0x7a, 0x35, 0xb2, 0xc5, 0xfa, 0x42, 0xcb, 0xe4, 0xc4, 0x86, 0xa7, 0x8f, 0x96, 0x22, 0xc1, 0x2b, 0x07, 0xc6, 0x29, 0xa2, 0xfd, 0x31, 0xcb, 0xba, 0x1b, 0x05, 0xca, 0xc2, 0xaa, 0xc3, 0x6f, 0x5a, 0xa8, 0x43, 0xa1, 0x60, 0x35, 0x97, 0xda, 0x35, 0xfd, 0x76, 0x16, 0xa9, 0x32, 0x91, 0xd1, 0xd8, 0x7b, 0x0b, 0xfc, 0x30, 0x99, 0x06, 0xa7, 0x62, 0x44, 0x85, 0x2b, 0xb9, 0x9b, 0x5a, 0xfb, 0x94, 0x28, 0x47, 0x40, 0x7f, 0x7a, 0x6a, 0xb2, 0xe9, 0xcc, 0xe3, 0x13, 0x52, 0x7c, 0xc0, 0x87, 0x1d, 0x72, 0xc5, 0x4b, 0x69, 0x0a, 0x2a, 0xc7, 0x37, 0xd5, 0x17, 0xd2, 0xce, 0xd2, 0x54, 0x66, 0x92, 0x42, 0x91, 0x84, 0x24, 0xef, 0xb6, 0x98, 0x6d, 0x78, 0x95, 0x0d, 0x7e, 0x49, 0xd0, 0xee, 0xcc, 0x61, 0x5d, 0x2f, 0x5a, 0xa1, 0xb2, 0xc0, 0xa9, 0xe7, 0x30, 0x94, 0x58, 0x58, 0x7a, 0x59, 0xd7, 0x96, 0xe9, 0x8c, 0x2c, 0xfd, 0xe9, 0x58, 0xe4, 0x4b, 0xa3, 0x31, 0x47, 0x03, 0x57, 0x05, 0x84, 0x6d, 0x3c, 0x4c, 0x3a, 0xaa, ];
let ephKeyPair = testDHKeyMapping()
let passportPublicKey = OpenSSLUtils.decodePublicKeyFromBytes(pubKeyData: passportEphKey, params: ephKeyPair)
let dh = EVP_PKEY_get1_DH(ephKeyPair);
var out = [UInt8](repeating:0, count:Int(DH_size(dh)))
let dh_ppub = EVP_PKEY_get1_DH(passportPublicKey)
var bn = BN_new()
DH_get0_key( dh_ppub, &bn, nil )
let len = DH_compute_key(&out, bn, dh);
return
}
}

View File

@@ -0,0 +1,22 @@
# Uncomment the next line to define a global platform for your project
platform :ios, '15.0'
# Disable use of input/output files here so that when we build it includes any changes to the development pod
# rather than having to manually clean and rebuuild each time
# Note this is commented out as it breaks archive project! But useful for dev
#install! 'cocoapods', :disable_input_output_paths => true
target 'NFCPassportReaderApp' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for NFCPassportReaderApp
pod 'NFCPassportReader', :path => '../..'
pod 'QKMRZScanner'
target 'NFCPassportReaderAppTests' do
inherit! :search_paths
# Pods for testing
end
end

View File

@@ -0,0 +1,35 @@
PODS:
- NFCPassportReader (1.1.9):
- OpenSSL-Universal (= 1.1.180)
- OpenSSL-Universal (1.1.180)
- QKMRZParser (2.0.0)
- QKMRZScanner (3.0.0):
- QKMRZParser (~> 2.0.0)
- SwiftyTesseract (~> 3.1.3)
- SwiftyTesseract (3.1.3)
DEPENDENCIES:
- NFCPassportReader (from `../..`)
- QKMRZScanner
SPEC REPOS:
trunk:
- OpenSSL-Universal
- QKMRZParser
- QKMRZScanner
- SwiftyTesseract
EXTERNAL SOURCES:
NFCPassportReader:
:path: "../.."
SPEC CHECKSUMS:
NFCPassportReader: 3a4b15d28c89a1f747c957f2bce1230c268dba25
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
QKMRZParser: 6b419b6f07d6bff6b50429b97de10846dc902c29
QKMRZScanner: cf2348fd6ce441e758328da4adf231ef2b51d769
SwiftyTesseract: 1f3d96668ae92dc2208d9842c8a59bea9fad2cbb
PODFILE CHECKSUM: b90c09d0214453ca8e10fa6c4bc0d09e4919d7a1
COCOAPODS: 1.11.3

View File

@@ -0,0 +1,2 @@
The CocoaPods example is here purely informational - no real updates will be done to it.

View File

@@ -0,0 +1,667 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
A117B7EB2695AD2400E13C18 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A117B7E62695AD2400E13C18 /* ViewController.swift */; };
A117B7EC2695AD2400E13C18 /* PreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A117B7E72695AD2400E13C18 /* PreviewView.swift */; };
A117B7ED2695AD2400E13C18 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = A117B7E82695AD2400E13C18 /* LICENSE */; };
A117B7EE2695AD2400E13C18 /* VisionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A117B7E92695AD2400E13C18 /* VisionViewController.swift */; };
A117B7EF2695AD2400E13C18 /* StringUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A117B7EA2695AD2400E13C18 /* StringUtils.swift */; };
A117B7F22695AE2300E13C18 /* MRZParser in Frameworks */ = {isa = PBXBuildFile; productRef = A117B7F12695AE2300E13C18 /* MRZParser */; };
A1298C9F25D53A5C00F5713E /* ViewExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1298C9E25D53A5C00F5713E /* ViewExt.swift */; };
A13F2A22268EF8BA0097C2D1 /* NFCPassportReader in Frameworks */ = {isa = PBXBuildFile; productRef = A13F2A21268EF8BA0097C2D1 /* NFCPassportReader */; };
A1614B9225D5856600191749 /* ExportPassportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1614B9125D5856600191749 /* ExportPassportView.swift */; };
A16EB23122A9E9E10008F53F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A16EB23022A9E9E10008F53F /* AppDelegate.swift */; };
A16EB23322A9E9E10008F53F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A16EB23222A9E9E10008F53F /* SceneDelegate.swift */; };
A16EB23A22A9E9E20008F53F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A16EB23922A9E9E20008F53F /* Assets.xcassets */; };
A16EB23D22A9E9E20008F53F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A16EB23B22A9E9E20008F53F /* LaunchScreen.storyboard */; };
A17134BE22C8EF7200C457C3 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A17134BD22C8EF7100C457C3 /* MainView.swift */; };
A17134C022C8F0A600C457C3 /* PassportUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A17134BF22C8F0A600C457C3 /* PassportUtils.swift */; };
A17375F525F78B0D005995DA /* PACETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A17375F425F78B0C005995DA /* PACETests.swift */; };
A1769DF725D3E318006002D1 /* SettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1769DF625D3E318006002D1 /* SettingsStore.swift */; };
A1816E9C22C9059F00F546A0 /* PassportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1816E9B22C9059F00F546A0 /* PassportView.swift */; };
A18248C22369D17300581384 /* DetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A18248C12369D17300581384 /* DetailsView.swift */; };
A182DE6125DD6F1300341204 /* StoredPassportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A182DE6025DD6F1300341204 /* StoredPassportView.swift */; };
A182DE6325DD730D00341204 /* FileManagerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = A182DE6225DD730D00341204 /* FileManagerExt.swift */; };
A1A13B9525B8A29E0026074C /* UIApplicationExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1A13B9425B8A29E0026074C /* UIApplicationExt.swift */; };
A1A4C0F725B5E4D70070908B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A4C0F625B5E4D70070908B /* Foundation.framework */; };
A1C234C225DD19DE003FFD79 /* PassportSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1C234C125DD19DE003FFD79 /* PassportSummaryView.swift */; };
A1CE83AC22C91D1300E3EACF /* StringExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1CE83AB22C91D1300E3EACF /* StringExt.swift */; };
A1DD3F3F22C535F10067255C /* masterList.pem in Resources */ = {isa = PBXBuildFile; fileRef = A1DD3F3E22C535F00067255C /* masterList.pem */; };
A1EBC5242370BAB2004DD19E /* DataGroupParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EBC5212370BAB2004DD19E /* DataGroupParsingTests.swift */; };
A1EBC5252370BAB2004DD19E /* NFCPassportReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EBC5222370BAB2004DD19E /* NFCPassportReaderTests.swift */; };
A1FDC52E25D3F15D00D22FF4 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1FDC52D25D3F15D00D22FF4 /* SettingsView.swift */; };
A1FDC53225D3F19E00D22FF4 /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1FDC53125D3F19E00D22FF4 /* ViewModifiers.swift */; };
A1FDC53425D3F1DE00D22FF4 /* CheckBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1FDC53325D3F1DE00D22FF4 /* CheckBoxView.swift */; };
A1FDC53625D43AB400D22FF4 /* MRZEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1FDC53525D43AB400D22FF4 /* MRZEntryView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
A1EBC5122370B88D004DD19E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = A16EB22522A9E9E10008F53F /* Project object */;
proxyType = 1;
remoteGlobalIDString = A16EB22C22A9E9E10008F53F;
remoteInfo = NFCPassportReaderApp;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
A117B7E62695AD2400E13C18 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
A117B7E72695AD2400E13C18 /* PreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewView.swift; sourceTree = "<group>"; };
A117B7E82695AD2400E13C18 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
A117B7E92695AD2400E13C18 /* VisionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VisionViewController.swift; sourceTree = "<group>"; };
A117B7EA2695AD2400E13C18 /* StringUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringUtils.swift; sourceTree = "<group>"; };
A1298C9E25D53A5C00F5713E /* ViewExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExt.swift; sourceTree = "<group>"; };
A1614B9125D5856600191749 /* ExportPassportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportPassportView.swift; sourceTree = "<group>"; };
A16EB22D22A9E9E10008F53F /* NFCPassportReaderApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NFCPassportReaderApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
A16EB23022A9E9E10008F53F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
A16EB23222A9E9E10008F53F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
A16EB23922A9E9E20008F53F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A16EB23C22A9E9E20008F53F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
A16EB23E22A9E9E20008F53F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A16EB24422A9EA000008F53F /* NFCPassportReader.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NFCPassportReader.entitlements; sourceTree = "<group>"; };
A17134BD22C8EF7100C457C3 /* MainView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
A17134BF22C8F0A600C457C3 /* PassportUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassportUtils.swift; sourceTree = "<group>"; };
A17375F425F78B0C005995DA /* PACETests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PACETests.swift; sourceTree = "<group>"; };
A1769DF625D3E318006002D1 /* SettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsStore.swift; sourceTree = "<group>"; };
A1816E9B22C9059F00F546A0 /* PassportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassportView.swift; sourceTree = "<group>"; };
A18248C12369D17300581384 /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = "<group>"; };
A182DE6025DD6F1300341204 /* StoredPassportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredPassportView.swift; sourceTree = "<group>"; };
A182DE6225DD730D00341204 /* FileManagerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerExt.swift; sourceTree = "<group>"; };
A182DE6725DD7EA600341204 /* MRZScannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MRZScannerViewController.swift; sourceTree = "<group>"; };
A1A13B9425B8A29E0026074C /* UIApplicationExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationExt.swift; sourceTree = "<group>"; };
A1A4C0F625B5E4D70070908B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
A1AE9E142686235E00D0E764 /* NFCPassportReader */ = {isa = PBXFileReference; lastKnownFileType = folder; name = NFCPassportReader; path = ../..; sourceTree = "<group>"; };
A1C234C125DD19DE003FFD79 /* PassportSummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassportSummaryView.swift; sourceTree = "<group>"; };
A1CE83AB22C91D1300E3EACF /* StringExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExt.swift; sourceTree = "<group>"; };
A1DD3F3E22C535F00067255C /* masterList.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = masterList.pem; sourceTree = "<group>"; };
A1EBC50D2370B88D004DD19E /* NFCPassportReaderAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NFCPassportReaderAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A1EBC5112370B88D004DD19E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A1EBC5212370BAB2004DD19E /* DataGroupParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataGroupParsingTests.swift; sourceTree = "<group>"; };
A1EBC5222370BAB2004DD19E /* NFCPassportReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NFCPassportReaderTests.swift; sourceTree = "<group>"; };
A1FDC52D25D3F15D00D22FF4 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
A1FDC53125D3F19E00D22FF4 /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = "<group>"; };
A1FDC53325D3F1DE00D22FF4 /* CheckBoxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBoxView.swift; sourceTree = "<group>"; };
A1FDC53525D43AB400D22FF4 /* MRZEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MRZEntryView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
A16EB22A22A9E9E10008F53F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A1A4C0F725B5E4D70070908B /* Foundation.framework in Frameworks */,
A117B7F22695AE2300E13C18 /* MRZParser in Frameworks */,
A13F2A22268EF8BA0097C2D1 /* NFCPassportReader in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A1EBC50A2370B88D004DD19E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
A117B7E52695AD2400E13C18 /* MrzScanner */ = {
isa = PBXGroup;
children = (
A117B7E62695AD2400E13C18 /* ViewController.swift */,
A117B7E72695AD2400E13C18 /* PreviewView.swift */,
A117B7E82695AD2400E13C18 /* LICENSE */,
A117B7E92695AD2400E13C18 /* VisionViewController.swift */,
A117B7EA2695AD2400E13C18 /* StringUtils.swift */,
);
path = MrzScanner;
sourceTree = "<group>";
};
A1298C9B25D539CD00F5713E /* HelperViews */ = {
isa = PBXGroup;
children = (
A1FDC53325D3F1DE00D22FF4 /* CheckBoxView.swift */,
A1298C9E25D53A5C00F5713E /* ViewExt.swift */,
A1FDC53125D3F19E00D22FF4 /* ViewModifiers.swift */,
);
path = HelperViews;
sourceTree = "<group>";
};
A16EB22422A9E9E10008F53F = {
isa = PBXGroup;
children = (
A1AE9E142686235E00D0E764 /* NFCPassportReader */,
A16EB22F22A9E9E10008F53F /* NFCPassportReaderApp */,
A1EBC50E2370B88D004DD19E /* NFCPassportReaderAppTests */,
A16EB22E22A9E9E10008F53F /* Products */,
A1A4C0E325B5D3370070908B /* Frameworks */,
);
sourceTree = "<group>";
};
A16EB22E22A9E9E10008F53F /* Products */ = {
isa = PBXGroup;
children = (
A16EB22D22A9E9E10008F53F /* NFCPassportReaderApp.app */,
A1EBC50D2370B88D004DD19E /* NFCPassportReaderAppTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
A16EB22F22A9E9E10008F53F /* NFCPassportReaderApp */ = {
isa = PBXGroup;
children = (
A16EB24422A9EA000008F53F /* NFCPassportReader.entitlements */,
A117B7E52695AD2400E13C18 /* MrzScanner */,
A1CE83AF22C91DB600E3EACF /* Model */,
A182DE6425DD731900341204 /* Extensions */,
A1CE83B022C91DF100E3EACF /* Views */,
A16EB23022A9E9E10008F53F /* AppDelegate.swift */,
A16EB23222A9E9E10008F53F /* SceneDelegate.swift */,
A16EB23922A9E9E20008F53F /* Assets.xcassets */,
A16EB23B22A9E9E20008F53F /* LaunchScreen.storyboard */,
A16EB23E22A9E9E20008F53F /* Info.plist */,
A1DD3F3E22C535F00067255C /* masterList.pem */,
);
path = NFCPassportReaderApp;
sourceTree = "<group>";
};
A182DE6425DD731900341204 /* Extensions */ = {
isa = PBXGroup;
children = (
A1CE83AB22C91D1300E3EACF /* StringExt.swift */,
A182DE6225DD730D00341204 /* FileManagerExt.swift */,
A1A13B9425B8A29E0026074C /* UIApplicationExt.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
A1A4C0E325B5D3370070908B /* Frameworks */ = {
isa = PBXGroup;
children = (
A1A4C0F625B5E4D70070908B /* Foundation.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
A1CE83AF22C91DB600E3EACF /* Model */ = {
isa = PBXGroup;
children = (
A17134BF22C8F0A600C457C3 /* PassportUtils.swift */,
A1769DF625D3E318006002D1 /* SettingsStore.swift */,
);
path = Model;
sourceTree = "<group>";
};
A1CE83B022C91DF100E3EACF /* Views */ = {
isa = PBXGroup;
children = (
A1298C9B25D539CD00F5713E /* HelperViews */,
A17134BD22C8EF7100C457C3 /* MainView.swift */,
A1FDC53525D43AB400D22FF4 /* MRZEntryView.swift */,
A1FDC52D25D3F15D00D22FF4 /* SettingsView.swift */,
A1816E9B22C9059F00F546A0 /* PassportView.swift */,
A1C234C125DD19DE003FFD79 /* PassportSummaryView.swift */,
A18248C12369D17300581384 /* DetailsView.swift */,
A182DE6025DD6F1300341204 /* StoredPassportView.swift */,
A1614B9125D5856600191749 /* ExportPassportView.swift */,
A182DE6725DD7EA600341204 /* MRZScannerViewController.swift */,
);
path = Views;
sourceTree = "<group>";
};
A1EBC50E2370B88D004DD19E /* NFCPassportReaderAppTests */ = {
isa = PBXGroup;
children = (
A1EBC5212370BAB2004DD19E /* DataGroupParsingTests.swift */,
A1EBC5222370BAB2004DD19E /* NFCPassportReaderTests.swift */,
A17375F425F78B0C005995DA /* PACETests.swift */,
A1EBC5112370B88D004DD19E /* Info.plist */,
);
path = NFCPassportReaderAppTests;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
A16EB22C22A9E9E10008F53F /* NFCPassportReaderApp */ = {
isa = PBXNativeTarget;
buildConfigurationList = A16EB24122A9E9E20008F53F /* Build configuration list for PBXNativeTarget "NFCPassportReaderApp" */;
buildPhases = (
A16EB22922A9E9E10008F53F /* Sources */,
A16EB22A22A9E9E10008F53F /* Frameworks */,
A16EB22B22A9E9E10008F53F /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = NFCPassportReaderApp;
packageProductDependencies = (
A13F2A21268EF8BA0097C2D1 /* NFCPassportReader */,
A117B7F12695AE2300E13C18 /* MRZParser */,
);
productName = NFCTest;
productReference = A16EB22D22A9E9E10008F53F /* NFCPassportReaderApp.app */;
productType = "com.apple.product-type.application";
};
A1EBC50C2370B88D004DD19E /* NFCPassportReaderAppTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = A1EBC5162370B88D004DD19E /* Build configuration list for PBXNativeTarget "NFCPassportReaderAppTests" */;
buildPhases = (
A1EBC5092370B88D004DD19E /* Sources */,
A1EBC50A2370B88D004DD19E /* Frameworks */,
A1EBC50B2370B88D004DD19E /* Resources */,
);
buildRules = (
);
dependencies = (
A1EBC5132370B88D004DD19E /* PBXTargetDependency */,
);
name = NFCPassportReaderAppTests;
productName = NFCPassportReaderAppTests;
productReference = A1EBC50D2370B88D004DD19E /* NFCPassportReaderAppTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
A16EB22522A9E9E10008F53F /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1120;
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "Andy Qua";
TargetAttributes = {
A16EB22C22A9E9E10008F53F = {
CreatedOnToolsVersion = 11.0;
LastSwiftMigration = 1100;
};
A1EBC50C2370B88D004DD19E = {
CreatedOnToolsVersion = 11.2;
TestTargetID = A16EB22C22A9E9E10008F53F;
};
};
};
buildConfigurationList = A16EB22822A9E9E10008F53F /* Build configuration list for PBXProject "NFCPassportReaderApp" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = A16EB22422A9E9E10008F53F;
packageReferences = (
A183989A26848C7600DC98C1 /* XCRemoteSwiftPackageReference "NFCPassportReader" */,
A117B7F02695AE2300E13C18 /* XCRemoteSwiftPackageReference "MRZParser" */,
);
productRefGroup = A16EB22E22A9E9E10008F53F /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
A16EB22C22A9E9E10008F53F /* NFCPassportReaderApp */,
A1EBC50C2370B88D004DD19E /* NFCPassportReaderAppTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
A16EB22B22A9E9E10008F53F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A16EB23D22A9E9E20008F53F /* LaunchScreen.storyboard in Resources */,
A1DD3F3F22C535F10067255C /* masterList.pem in Resources */,
A117B7ED2695AD2400E13C18 /* LICENSE in Resources */,
A16EB23A22A9E9E20008F53F /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A1EBC50B2370B88D004DD19E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
A16EB22922A9E9E10008F53F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A1CE83AC22C91D1300E3EACF /* StringExt.swift in Sources */,
A117B7EB2695AD2400E13C18 /* ViewController.swift in Sources */,
A1FDC53625D43AB400D22FF4 /* MRZEntryView.swift in Sources */,
A17134BE22C8EF7200C457C3 /* MainView.swift in Sources */,
A117B7EE2695AD2400E13C18 /* VisionViewController.swift in Sources */,
A16EB23122A9E9E10008F53F /* AppDelegate.swift in Sources */,
A117B7EC2695AD2400E13C18 /* PreviewView.swift in Sources */,
A1816E9C22C9059F00F546A0 /* PassportView.swift in Sources */,
A117B7EF2695AD2400E13C18 /* StringUtils.swift in Sources */,
A1769DF725D3E318006002D1 /* SettingsStore.swift in Sources */,
A1298C9F25D53A5C00F5713E /* ViewExt.swift in Sources */,
A182DE6325DD730D00341204 /* FileManagerExt.swift in Sources */,
A1FDC52E25D3F15D00D22FF4 /* SettingsView.swift in Sources */,
A182DE6125DD6F1300341204 /* StoredPassportView.swift in Sources */,
A1FDC53225D3F19E00D22FF4 /* ViewModifiers.swift in Sources */,
A1C234C225DD19DE003FFD79 /* PassportSummaryView.swift in Sources */,
A1614B9225D5856600191749 /* ExportPassportView.swift in Sources */,
A18248C22369D17300581384 /* DetailsView.swift in Sources */,
A1FDC53425D3F1DE00D22FF4 /* CheckBoxView.swift in Sources */,
A1A13B9525B8A29E0026074C /* UIApplicationExt.swift in Sources */,
A16EB23322A9E9E10008F53F /* SceneDelegate.swift in Sources */,
A17134C022C8F0A600C457C3 /* PassportUtils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A1EBC5092370B88D004DD19E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A1EBC5252370BAB2004DD19E /* NFCPassportReaderTests.swift in Sources */,
A17375F525F78B0D005995DA /* PACETests.swift in Sources */,
A1EBC5242370BAB2004DD19E /* DataGroupParsingTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
A1EBC5132370B88D004DD19E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = A16EB22C22A9E9E10008F53F /* NFCPassportReaderApp */;
targetProxy = A1EBC5122370B88D004DD19E /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
A16EB23B22A9E9E20008F53F /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
A16EB23C22A9E9E20008F53F /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
A16EB23F22A9E9E20008F53F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
A16EB24022A9E9E20008F53F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
A16EB24222A9E9E20008F53F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = NFCPassportReaderApp/NFCPassportReader.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = A4ZM73UFF5;
INFOPLIST_FILE = NFCPassportReaderApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.andyqua.nfcpassportreader;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
A16EB24322A9E9E20008F53F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = NFCPassportReaderApp/NFCPassportReader.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = A4ZM73UFF5;
INFOPLIST_FILE = NFCPassportReaderApp/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.andyqua.nfcpassportreader;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
A1EBC5142370B88D004DD19E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = A4ZM73UFF5;
INFOPLIST_FILE = NFCPassportReaderAppTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.andyqua.NFCPassportReaderAppTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/NFCPassportReaderApp.app/NFCPassportReaderApp";
};
name = Debug;
};
A1EBC5152370B88D004DD19E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = A4ZM73UFF5;
INFOPLIST_FILE = NFCPassportReaderAppTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.andyqua.NFCPassportReaderAppTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/NFCPassportReaderApp.app/NFCPassportReaderApp";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
A16EB22822A9E9E10008F53F /* Build configuration list for PBXProject "NFCPassportReaderApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
A16EB23F22A9E9E20008F53F /* Debug */,
A16EB24022A9E9E20008F53F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
A16EB24122A9E9E20008F53F /* Build configuration list for PBXNativeTarget "NFCPassportReaderApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
A16EB24222A9E9E20008F53F /* Debug */,
A16EB24322A9E9E20008F53F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
A1EBC5162370B88D004DD19E /* Build configuration list for PBXNativeTarget "NFCPassportReaderAppTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
A1EBC5142370B88D004DD19E /* Debug */,
A1EBC5152370B88D004DD19E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
A117B7F02695AE2300E13C18 /* XCRemoteSwiftPackageReference "MRZParser" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/appintheair/MRZParser";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.0.0;
};
};
A183989A26848C7600DC98C1 /* XCRemoteSwiftPackageReference "NFCPassportReader" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/AndyQ/NFCPassportReader";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.0.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
A117B7F12695AE2300E13C18 /* MRZParser */ = {
isa = XCSwiftPackageProductDependency;
package = A117B7F02695AE2300E13C18 /* XCRemoteSwiftPackageReference "MRZParser" */;
productName = MRZParser;
};
A13F2A21268EF8BA0097C2D1 /* NFCPassportReader */ = {
isa = XCSwiftPackageProductDependency;
productName = NFCPassportReader;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = A16EB22522A9E9E10008F53F /* Project object */;
}

View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A16EB22C22A9E9E10008F53F"
BuildableName = "NFCPassportReaderApp.app"
BlueprintName = "NFCPassportReaderApp"
ReferencedContainer = "container:NFCPassportReaderApp.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "NFCPassportReaderTests"
BuildableName = "NFCPassportReaderTests"
BlueprintName = "NFCPassportReaderTests"
ReferencedContainer = "container:..">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A1EBC50C2370B88D004DD19E"
BuildableName = "NFCPassportReaderAppTests.xctest"
BlueprintName = "NFCPassportReaderAppTests"
ReferencedContainer = "container:NFCPassportReaderApp.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
disableMainThreadChecker = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A16EB22C22A9E9E10008F53F"
BuildableName = "NFCPassportReaderApp.app"
BlueprintName = "NFCPassportReaderApp"
ReferencedContainer = "container:NFCPassportReaderApp.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "OS_ACTIVITY_MODE"
value = "disable"
isEnabled = "NO">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A16EB22C22A9E9E10008F53F"
BuildableName = "NFCPassportReaderApp.app"
BlueprintName = "NFCPassportReaderApp"
ReferencedContainer = "container:NFCPassportReaderApp.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,39 @@
//
// AppDelegate.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 06/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import UIKit
import CoreNFC
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}

View File

@@ -0,0 +1,158 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"images" : [
{
"size" : "40x40",
"filename" : "icon-40.png",
"idiom" : "ipad",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"scale" : "2x",
"filename" : "icon-40@2x.png"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x",
"filename" : "icon-60@2x.png"
},
{
"idiom" : "ipad",
"filename" : "icon-72.png",
"size" : "72x72",
"scale" : "1x"
},
{
"scale" : "2x",
"idiom" : "ipad",
"size" : "72x72",
"filename" : "icon-72@2x.png"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x",
"filename" : "icon-76.png"
},
{
"scale" : "2x",
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon-76@2x.png"
},
{
"idiom" : "ipad",
"size" : "50x50",
"filename" : "icon-small-50.png",
"scale" : "1x"
},
{
"scale" : "2x",
"filename" : "icon-small-50@2x.png",
"size" : "50x50",
"idiom" : "ipad"
},
{
"scale" : "1x",
"filename" : "icon-small.png",
"idiom" : "iphone",
"size" : "29x29"
},
{
"scale" : "2x",
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon-small@2x.png"
},
{
"filename" : "icon.png",
"idiom" : "iphone",
"size" : "57x57",
"scale" : "1x"
},
{
"scale" : "2x",
"size" : "57x57",
"idiom" : "iphone",
"filename" : "icon@2x.png"
},
{
"filename" : "icon-small@3x.png",
"size" : "29x29",
"idiom" : "iphone",
"scale" : "3x"
},
{
"size" : "40x40",
"scale" : "3x",
"idiom" : "iphone",
"filename" : "icon-40@3x.png"
},
{
"filename" : "icon-60@3x.png",
"scale" : "3x",
"idiom" : "iphone",
"size" : "60x60"
},
{
"size" : "40x40",
"filename" : "icon-40@2x.png",
"idiom" : "iphone",
"scale" : "2x"
},
{
"scale" : "1x",
"idiom" : "ipad",
"size" : "29x29",
"filename" : "icon-small.png"
},
{
"size" : "29x29",
"scale" : "2x",
"filename" : "icon-small@2x.png",
"idiom" : "ipad"
},
{
"idiom" : "ipad",
"filename" : "icon-83.5@2x.png",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"size" : "20x20",
"scale" : "2x",
"filename" : "notification-icon@2x.png",
"idiom" : "iphone"
},
{
"scale" : "3x",
"filename" : "notification-icon@3x.png",
"idiom" : "iphone",
"size" : "20x20"
},
{
"filename" : "notification-icon~ipad.png",
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"filename" : "notification-icon~ipad@2x.png",
"size" : "20x20",
"scale" : "2x",
"idiom" : "ipad"
},
{
"filename" : "ios-marketing.png",
"size" : "1024x1024",
"idiom" : "ios-marketing",
"scale" : "1x"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "background.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "head.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,16 @@
//
// FileManagerExt.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 17/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import SwiftUI
extension FileManager {
static var cachesFolder : URL {
FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
}
}

View File

@@ -0,0 +1,40 @@
//
// StringExt.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 30/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import Foundation
/// Some Utility methods for string - access characters by index
extension String {
subscript(_ i: Int) -> String {
let idx1 = index(startIndex, offsetBy: i)
let idx2 = index(idx1, offsetBy: 1)
return String(self[idx1..<idx2])
}
subscript (bounds: CountableRange<Int>) -> String {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return String(self[start..<end])
}
subscript (bounds: CountableClosedRange<Int>) -> String {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return String(self[start...end])
}
subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
let start = index(startIndex, offsetBy: bounds.lowerBound)
return String(self[start...])
}
func strip() -> String {
let trimmed = self.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed
}
}

View File

@@ -0,0 +1,21 @@
//
// UIApplicationExt.swift
// NFCPassportReaderApp
//
// Created by Andy Qua on 20/01/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import UIKit
extension UIApplication {
static var release: String {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String? ?? "x.x"
}
static var build: String {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as! String? ?? "x"
}
static var version: String {
return "\(release).\(build)"
}
}

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>NFCPassportReaderApp</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NFCReaderUsageDescription</key>
<string>This app uses NFC to scan passports</string>
<key>NSCameraUsageDescription</key>
<string>This app uses the camera to read passports</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>nfc</string>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>A0000002471001</string>
<string>A0000002472001</string>
<string>00000000000000</string>
</array>
</dict>
</plist>

Some files were not shown because too many files have changed in this diff Show More