import SwiftUI import AVFoundation struct QRScannerView: UIViewControllerRepresentable { let onScan: (String) -> Void func makeUIViewController(context: Context) -> ScannerViewController { let vc = ScannerViewController() vc.onScan = onScan return vc } func updateUIViewController(_ uiViewController: ScannerViewController, context: Context) {} } final class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate { var onScan: ((String) -> Void)? private let session = AVCaptureSession() private var previewLayer: AVCaptureVideoPreviewLayer? override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .black setupCapture() } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() previewLayer?.frame = view.bounds } private func setupCapture() { guard let device = AVCaptureDevice.default(for: .video), let input = try? AVCaptureDeviceInput(device: device), session.canAddInput(input) else { return } session.addInput(input) let output = AVCaptureMetadataOutput() guard session.canAddOutput(output) else { return } session.addOutput(output) output.setMetadataObjectsDelegate(self, queue: .main) output.metadataObjectTypes = [.qr] let layer = AVCaptureVideoPreviewLayer(session: session) layer.videoGravity = .resizeAspectFill view.layer.addSublayer(layer) previewLayer = layer DispatchQueue.global(qos: .userInitiated).async { self.session.startRunning() } } func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput objects: [AVMetadataObject], from connection: AVCaptureConnection) { guard let obj = objects.first as? AVMetadataMachineReadableCodeObject, let value = obj.stringValue else { return } session.stopRunning() onScan?(value) } }