SimpleQRCodeScannerView.swift 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. // LoopFollow
  2. // SimpleQRCodeScannerView.swift
  3. import AVFoundation
  4. import SwiftUI
  5. struct SimpleQRCodeScannerView: UIViewControllerRepresentable {
  6. @Environment(\.presentationMode) var presentationMode
  7. var completion: (Result<String, Error>) -> Void
  8. // MARK: - Coordinator
  9. class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate {
  10. var parent: SimpleQRCodeScannerView
  11. var session: AVCaptureSession?
  12. init(parent: SimpleQRCodeScannerView) {
  13. self.parent = parent
  14. }
  15. func metadataOutput(_: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from _: AVCaptureConnection) {
  16. if let session, session.isRunning {
  17. if let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
  18. metadataObject.type == .qr,
  19. let stringValue = metadataObject.stringValue
  20. {
  21. DispatchQueue.global(qos: .userInitiated).async {
  22. session.stopRunning()
  23. }
  24. parent.completion(.success(stringValue))
  25. }
  26. }
  27. }
  28. }
  29. // MARK: - UIViewControllerRepresentable Methods
  30. func makeCoordinator() -> Coordinator {
  31. Coordinator(parent: self)
  32. }
  33. func makeUIViewController(context: Context) -> UIViewController {
  34. let controller = UIViewController()
  35. let session = AVCaptureSession()
  36. context.coordinator.session = session // Assign session to coordinator
  37. guard let videoCaptureDevice = AVCaptureDevice.default(for: .video),
  38. let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice),
  39. session.canAddInput(videoInput)
  40. else {
  41. let error = NSError(domain: "QRCodeScannerError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to set up camera input."])
  42. completion(.failure(error))
  43. return controller
  44. }
  45. session.addInput(videoInput)
  46. let metadataOutput = AVCaptureMetadataOutput()
  47. if session.canAddOutput(metadataOutput) {
  48. session.addOutput(metadataOutput)
  49. metadataOutput.setMetadataObjectsDelegate(context.coordinator, queue: DispatchQueue.main)
  50. metadataOutput.metadataObjectTypes = [.qr]
  51. } else {
  52. let error = NSError(domain: "QRCodeScannerError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to set up metadata output."])
  53. completion(.failure(error))
  54. return controller
  55. }
  56. let previewLayer = AVCaptureVideoPreviewLayer(session: session)
  57. previewLayer.frame = controller.view.layer.bounds
  58. previewLayer.videoGravity = .resizeAspectFill
  59. controller.view.layer.addSublayer(previewLayer)
  60. DispatchQueue.global(qos: .userInitiated).async {
  61. session.startRunning()
  62. }
  63. return controller
  64. }
  65. func updateUIViewController(_: UIViewController, context _: Context) {}
  66. func dismantleUIViewController(_: UIViewController, coordinator: Coordinator) {
  67. DispatchQueue.global(qos: .userInitiated).async {
  68. if let session = coordinator.session, session.isRunning {
  69. session.stopRunning()
  70. }
  71. }
  72. }
  73. }