diff --git a/app/ios/Podfile b/app/ios/Podfile index d3fd26274..eb097dd25 100644 --- a/app/ios/Podfile +++ b/app/ios/Podfile @@ -41,7 +41,8 @@ target 'ProofOfPassport' do pod 'NFCPassportReader', git: 'https://github.com/0xturboblitz/NFCPassportReader.git', commit: '310ecb519655d9ed8b1afc5eb490b2f51a4d3595' pod 'QKMRZScanner' pod 'RNFS', :path => '../node_modules/react-native-fs' - pod 'lottie-ios' + pod 'lottie-ios' + pod 'SwiftQRScanner', :git => 'https://github.com/vinodiOS/SwiftQRScanner' use_react_native!( :path => config[:reactNativePath], @@ -80,4 +81,4 @@ target 'ProofOfPassport' do ) __apply_Xcode_12_5_M1_post_install_workaround(installer) end -end +end \ No newline at end of file diff --git a/app/ios/Podfile.lock b/app/ios/Podfile.lock index a54fa4845..f27957896 100644 --- a/app/ios/Podfile.lock +++ b/app/ios/Podfile.lock @@ -292,10 +292,14 @@ PODS: - React-jsinspector (0.72.3) - React-logger (0.72.3): - glog + - react-native-date-picker (5.0.4): + - React-Core - react-native-get-random-values (1.11.0): - React-Core - react-native-netinfo (11.3.1): - React-Core + - react-native-nfc-manager (3.15.1): + - React-Core - React-NativeModulesApple (0.72.3): - React-callinvoker - React-Core @@ -420,6 +424,7 @@ PODS: - SSZipArchive (~> 2.2) - SocketRocket (0.6.1) - SSZipArchive (2.4.3) + - SwiftQRScanner (1.1.6) - SwiftyTesseract (3.1.3) - Yoga (1.14.0) @@ -449,8 +454,10 @@ DEPENDENCIES: - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - React-logger (from `../node_modules/react-native/ReactCommon/logger`) + - react-native-date-picker (from `../node_modules/react-native-date-picker`) - react-native-get-random-values (from `../node_modules/react-native-get-random-values`) - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)" + - react-native-nfc-manager (from `../node_modules/react-native-nfc-manager`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) @@ -474,6 +481,7 @@ DEPENDENCIES: - RNKeychain (from `../node_modules/react-native-keychain`) - RNSVG (from `../node_modules/react-native-svg`) - RNZipArchive (from `../node_modules/react-native-zip-archive`) + - SwiftQRScanner (from `https://github.com/vinodiOS/SwiftQRScanner`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: @@ -533,10 +541,14 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/jsinspector" React-logger: :path: "../node_modules/react-native/ReactCommon/logger" + react-native-date-picker: + :path: "../node_modules/react-native-date-picker" react-native-get-random-values: :path: "../node_modules/react-native-get-random-values" react-native-netinfo: :path: "../node_modules/@react-native-community/netinfo" + react-native-nfc-manager: + :path: "../node_modules/react-native-nfc-manager" React-NativeModulesApple: :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" React-perflogger: @@ -583,6 +595,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-svg" RNZipArchive: :path: "../node_modules/react-native-zip-archive" + SwiftQRScanner: + :git: https://github.com/vinodiOS/SwiftQRScanner Yoga: :path: "../node_modules/react-native/ReactCommon/yoga" @@ -590,6 +604,9 @@ CHECKOUT OPTIONS: NFCPassportReader: :commit: 310ecb519655d9ed8b1afc5eb490b2f51a4d3595 :git: https://github.com/0xturboblitz/NFCPassportReader.git + SwiftQRScanner: + :commit: fddcabcb431cd6110cea0394660082661dbafa7e + :git: https://github.com/vinodiOS/SwiftQRScanner SPEC CHECKSUMS: amplitude-react-native: 43098dfcfe1dcd6f0e9426017258ba79289d7a21 @@ -619,8 +636,10 @@ SPEC CHECKSUMS: React-jsiexecutor: 2c15ba1bace70177492368d5180b564f165870fd React-jsinspector: b511447170f561157547bc0bef3f169663860be7 React-logger: c5b527272d5f22eaa09bb3c3a690fee8f237ae95 + react-native-date-picker: 6891317e850deae5b53d51355226e07a495aba61 react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06 react-native-netinfo: bdb108d340cdb41875c9ced535977cac6d2ff321 + react-native-nfc-manager: 3134770f9afcb586c0ae859e747fda3106ec1f76 React-NativeModulesApple: 1d8e4abb2fc6d80bdd13dcf128ee548804eb4a24 React-perflogger: 6bd153e776e6beed54c56b0847e1220a3ff92ba5 React-RCTActionSheet: c0b62af44e610e69d9a2049a682f5dba4e9dff17 @@ -646,9 +665,10 @@ SPEC CHECKSUMS: RNZipArchive: ef9451b849c45a29509bf44e65b788829ab07801 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef + SwiftQRScanner: e85a25f9b843e9231dab89a96e441472fe54a724 SwiftyTesseract: 1f3d96668ae92dc2208d9842c8a59bea9fad2cbb Yoga: 8796b55dba14d7004f980b54bcc9833ee45b28ce -PODFILE CHECKSUM: 0d076339cbbbe7ad7559e35aae50e0036863e47e +PODFILE CHECKSUM: ed2a230fe418418ecb7e6525cb769af59fc9dae2 COCOAPODS: 1.15.2 diff --git a/app/ios/ProofOfPassport.xcodeproj/project.pbxproj b/app/ios/ProofOfPassport.xcodeproj/project.pbxproj index c4f2becb8..e6ca8ac1d 100644 --- a/app/ios/ProofOfPassport.xcodeproj/project.pbxproj +++ b/app/ios/ProofOfPassport.xcodeproj/project.pbxproj @@ -27,6 +27,9 @@ 165E76BF2B8DC53A0000FA90 /* MRZScannerModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 165E76BE2B8DC53A0000FA90 /* MRZScannerModule.m */; }; 165E76C32B8DC8370000FA90 /* ScannerHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165E76C22B8DC8370000FA90 /* ScannerHostingController.swift */; }; 1686F0DA2C4EA0F600841CDE /* passport.json in Resources */ = {isa = PBXBuildFile; fileRef = 1686F0D92C4EA0F600841CDE /* passport.json */; }; + 1686F0DC2C500F3800841CDE /* QRScannerBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1686F0DB2C500F3800841CDE /* QRScannerBridge.swift */; }; + 1686F0DE2C500F4F00841CDE /* QRScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1686F0DD2C500F4F00841CDE /* QRScannerViewController.swift */; }; + 1686F0E02C500FBD00841CDE /* QRScannerBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 1686F0DF2C500FBD00841CDE /* QRScannerBridge.m */; }; 16E0838A2C4E7AF100CE8DB2 /* LottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16E083892C4E7AF100CE8DB2 /* LottieView.swift */; }; 16E6646E2B8D292500FDD6A0 /* QKMRZScannerViewRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16E6646D2B8D292500FDD6A0 /* QKMRZScannerViewRepresentable.swift */; }; 1BA25F26C91C45F697D55099 /* Inter-ExtraLight.otf in Resources */ = {isa = PBXBuildFile; fileRef = 568162F4DC4B4CDC8B341853 /* Inter-ExtraLight.otf */; }; @@ -93,6 +96,9 @@ 165E76BE2B8DC53A0000FA90 /* MRZScannerModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MRZScannerModule.m; sourceTree = ""; }; 165E76C22B8DC8370000FA90 /* ScannerHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScannerHostingController.swift; sourceTree = ""; }; 1686F0D92C4EA0F600841CDE /* passport.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = passport.json; path = ../../../FreedomTool/FreedomTools/Resources/Animations/passport.json; sourceTree = ""; }; + 1686F0DB2C500F3800841CDE /* QRScannerBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerBridge.swift; sourceTree = ""; }; + 1686F0DD2C500F4F00841CDE /* QRScannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerViewController.swift; sourceTree = ""; }; + 1686F0DF2C500FBD00841CDE /* QRScannerBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QRScannerBridge.m; sourceTree = ""; }; 16E083892C4E7AF100CE8DB2 /* LottieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieView.swift; sourceTree = ""; }; 16E6646D2B8D292500FDD6A0 /* QKMRZScannerViewRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QKMRZScannerViewRepresentable.swift; sourceTree = ""; }; 1CA9D245CD5A439D88F01D4F /* Inter-ThinItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-ThinItalic.otf"; path = "../node_modules/@tamagui/font-inter/otf/Inter-ThinItalic.otf"; sourceTree = ""; }; @@ -182,6 +188,9 @@ 165E76BC2B8DC4A00000FA90 /* MRZScannerModule.swift */, 165E76BE2B8DC53A0000FA90 /* MRZScannerModule.m */, 16E6646D2B8D292500FDD6A0 /* QKMRZScannerViewRepresentable.swift */, + 1686F0DB2C500F3800841CDE /* QRScannerBridge.swift */, + 1686F0DF2C500FBD00841CDE /* QRScannerBridge.m */, + 1686F0DD2C500F4F00841CDE /* QRScannerViewController.swift */, ); name = ProofOfPassport; sourceTree = ""; @@ -530,7 +539,10 @@ buildActionMask = 2147483647; files = ( 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, + 1686F0DE2C500F4F00841CDE /* QRScannerViewController.swift in Sources */, + 1686F0E02C500FBD00841CDE /* QRScannerBridge.m in Sources */, 05EDEDC72B52D25D00AA51AD /* Prover.swift in Sources */, + 1686F0DC2C500F3800841CDE /* QRScannerBridge.swift in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, 905B70072A72774000AFA232 /* PassportReader.m in Sources */, 16E6646E2B8D292500FDD6A0 /* QKMRZScannerViewRepresentable.swift in Sources */, diff --git a/app/ios/ProofOfPassport/AppDelegate.mm b/app/ios/ProofOfPassport/AppDelegate.mm index 9fd251767..fd3025c99 100644 --- a/app/ios/ProofOfPassport/AppDelegate.mm +++ b/app/ios/ProofOfPassport/AppDelegate.mm @@ -1,6 +1,8 @@ #import "AppDelegate.h" #import +#import +#import @implementation AppDelegate @@ -23,4 +25,4 @@ #endif } -@end +@end \ No newline at end of file diff --git a/app/ios/QRScannerBridge.m b/app/ios/QRScannerBridge.m new file mode 100644 index 000000000..dae2a3c71 --- /dev/null +++ b/app/ios/QRScannerBridge.m @@ -0,0 +1,15 @@ +// +// QRScannerBridge.m +// ProofOfPassport +// +// Created by Rémi Colin on 23/07/2024. +// + +#import +#import + +@interface RCT_EXTERN_MODULE(QRScannerBridge, NSObject) + +RCT_EXTERN_METHOD(scanQRCode:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) + +@end diff --git a/app/ios/QRScannerBridge.swift b/app/ios/QRScannerBridge.swift new file mode 100644 index 000000000..b36916be0 --- /dev/null +++ b/app/ios/QRScannerBridge.swift @@ -0,0 +1,30 @@ +// +// QRScannerBridge.swift +// ProofOfPassport +// +// Created by Rémi Colin on 23/07/2024. +// + +import Foundation +import SwiftQRScanner +import React + +@objc(QRScannerBridge) +class QRScannerBridge: NSObject { + @objc + static func requiresMainQueueSetup() -> Bool { + return false + } + + @objc + func scanQRCode(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) { + DispatchQueue.main.async { + let rootViewController = UIApplication.shared.keyWindow?.rootViewController + let qrScannerViewController = QRScannerViewController() + qrScannerViewController.completionHandler = { result in + resolve(result) + } + rootViewController?.present(qrScannerViewController, animated: true, completion: nil) + } + } +} diff --git a/app/ios/QRScannerViewController.swift b/app/ios/QRScannerViewController.swift new file mode 100644 index 000000000..f52d2d54c --- /dev/null +++ b/app/ios/QRScannerViewController.swift @@ -0,0 +1,45 @@ +// +// QRScannerViewController.swift +// ProofOfPassport +// +// Created by Rémi Colin on 23/07/2024. +// + +import Foundation +import UIKit +import SwiftQRScanner + +class QRScannerViewController: UIViewController, QRScannerCodeDelegate { + var completionHandler: ((String) -> Void)? + + override func viewDidLoad() { + super.viewDidLoad() + + var configuration = QRScannerConfiguration() + configuration.cameraImage = UIImage(named: "camera") + configuration.flashOnImage = UIImage(named: "flash-on") + configuration.galleryImage = UIImage(named: "photos") + + let scanner = QRCodeScannerController(qrScannerConfiguration: configuration) + scanner.delegate = self + + addChild(scanner) + view.addSubview(scanner.view) + scanner.view.frame = view.bounds + scanner.didMove(toParent: self) + } + + func qrScanner(_ controller: UIViewController, didScanQRCodeWithResult result: String) { + completionHandler?(result) + dismiss(animated: true, completion: nil) + } + + func qrScanner(_ controller: UIViewController, didFailWithError error: QRCodeError) { + print("QR Scanner Error: \(error.localizedDescription)") + dismiss(animated: true, completion: nil) + } + + func qrScannerDidCancel(_ controller: UIViewController) { + dismiss(animated: true, completion: nil) + } +} diff --git a/app/src/screens/AppScreen.tsx b/app/src/screens/AppScreen.tsx index a0099a20f..69c45e4c3 100644 --- a/app/src/screens/AppScreen.tsx +++ b/app/src/screens/AppScreen.tsx @@ -13,13 +13,14 @@ import { BadgeCheck, Binary, LockKeyhole, QrCode, ShieldCheck, Smartphone, UserP import { bgBlue, bgGreen, separatorColor, textBlack } from '../utils/colors'; import { orange } from '@tamagui/colors'; import useUserStore from '../stores/userStore'; - +import { NativeModules } from 'react-native'; const AppScreen: React.FC = () => { const { selectedApp, update, selectedTab, - setSelectedTab + setSelectedTab, + toast } = useNavigationStore(); const { @@ -110,7 +111,21 @@ const AppScreen: React.FC = () => { {registered ? setRegistered(false)} Icon={} /> : - { setSelectedTab("start"); setRegistered(true); }} Icon={} />} + { + NativeModules.QRScannerBridge.scanQRCode() + .then((result: string) => { + toast.show('QR Code result', { + message: result, + customData: { + type: "success", + }, + }); + // Handle the scanned QR code result here + }) + .catch((error: any) => { + console.error('QR Scanner Error:', error); + }); + }} Icon={} />} {/* { cardsData.map(app => ( @@ -132,4 +147,4 @@ const AppScreen: React.FC = () => { ); } -export default AppScreen; +export default AppScreen; \ No newline at end of file