import Foundation import VisionKit // Don't forget to import VisionKit @objc(DataScannerModule) class DataScannerModule: NSObject, DataScannerViewControllerDelegate { private var scannerViewController: DataScannerViewController? private var reactPromiseResolver: RCTPromiseResolveBlock? private var reactPromiseRejecter: RCTPromiseRejectBlock? @objc func presentDataScanner(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.main.async { guard DataScannerViewController.isSupported else { reject("UNSUPPORTED", "DataScanner is not supported on this device.", nil) return } self.reactPromiseResolver = resolve self.reactPromiseRejecter = reject self.scannerViewController = DataScannerViewController( recognizedDataTypes: [.text(), .barcode(symbologies: [.aztec, .qr])], qualityLevel: .fast, recognizesMultipleItems: true, isPinchToZoomEnabled: true, isGuidanceEnabled: true, isHighlightingEnabled: true ) self.scannerViewController?.delegate = self if let rootViewController = UIApplication.shared.windows.first?.rootViewController { rootViewController.present(self.scannerViewController!, animated: true, completion: nil) } else { reject("NO_ROOT_VC", "Could not find root view controller.", nil) } } } @objc func dismissDataScanner(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.main.async { self.scannerViewController?.dismiss(animated: true, completion: { self.scannerViewController = nil resolve("Dismissed") }) } } // MARK: - DataScannerViewControllerDelegate func dataScanner(_ dataScanner: DataScannerViewController, didAdd addedItems: [DataScannerViewController.RecognizedItem], allItems: [DataScannerViewController.RecognizedItem]) { // Process recognized items and send them back to React Native var results: [[String: Any]] = [] for item in addedItems { switch item { case .text(let text): results.append(["type": "text", "value": text.transcript]) case .barcode(let barcode): results.append(["type": "barcode", "value": barcode.payloadStringValue ?? ""]) @unknown default: break } } // You'd likely send updates more frequently, or send all items when the scanner is dismissed. // For simplicity, this example just sends added items. // You'd use self.reactPromiseResolver to send the data back. // Example: self.reactPromiseResolver?(results) // Or use RCTEventEmitter for continuous updates. } func dataScanner(_ dataScanner: DataScannerViewController, didTapOn item: DataScannerViewController.RecognizedItem) { // Handle tap if needed } func dataScanner(_ dataScanner: DataScannerViewController, didRemove removedItems: [DataScannerViewController.RecognizedItem], allItems: [DataScannerViewController.RecognizedItem]) { // Handle removal if needed } func dataScanner(_ dataScanner: DataScannerViewController, didUpdate updatedItems: [DataScannerViewController.RecognizedItem], allItems: [DataScannerViewController.RecognizedItem]) { // Handle updates if needed } func dataScanner(_ dataScanner: DataScannerViewController, didTerminateWithError error: Error) { self.reactPromiseRejecter?("SCANNER_ERROR", error.localizedDescription, error) self.scannerViewController?.dismiss(animated: true, completion: nil) self.scannerViewController = nil } }