Untitled
unknown
plain_text
a year ago
3.3 kB
17
Indexable
//
// QRCodeScannerView.swift
// PocketCaddy
//
// Created by Dylan Anderson on 10/12/23.
//
import SwiftUI
import AVFoundation
final class CaptureSessionManager: ObservableObject {
var captureSession: AVCaptureSession?
init() {
captureSession = AVCaptureSession()
}
}
struct QRCodeScannerView<T>: UIViewControllerRepresentable {
@ObservedObject var captureSessionManager = CaptureSessionManager()
var didFindCode: (T?) -> Void
var onError: (Error) -> Void
class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate {
var parent: QRCodeScannerView
init(parent: QRCodeScannerView) {
self.parent = parent
}
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
if let metadataObject = metadataObjects.first,
let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject,
let stringValue = readableObject.stringValue {
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
parent.didFindCode(stringValue as? T)
parent.captureSessionManager.captureSession?.stopRunning()
}
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
func makeUIViewController(context: Context) -> UIViewController {
let viewController = UIViewController()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video),
let captureSession = captureSessionManager.captureSession else {
onError(QRCodeScannerError.noCamera)
return viewController
}
do {
let videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
if captureSession.canAddInput(videoInput) {
captureSession.addInput(videoInput)
} else {
onError(QRCodeScannerError.videoInputInitFail)
return viewController
}
} catch {
onError(error)
return viewController
}
let metadataOutput = AVCaptureMetadataOutput()
if captureSession.canAddOutput(metadataOutput) {
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(context.coordinator, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [.qr]
} else {
onError(QRCodeScannerError.outputInitFail)
return viewController
}
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = viewController.view.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
viewController.view.layer.addSublayer(previewLayer)
DispatchQueue.global(qos: .userInitiated).async {
captureSession.startRunning()
}
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
DispatchQueue.global(qos: .userInitiated).async {
if let captureSession = self.captureSessionManager.captureSession,
!captureSession.isRunning {
captureSession.startRunning()
}
}
}
}
extension QRCodeScannerView {
enum QRCodeScannerError: LocalizedError {
case noCamera
case videoInputInitFail
case outputInitFail
var errorDescription: String? {
switch self {
case .noCamera:
return "No camera available"
case .videoInputInitFail:
return "Video input initialization failed"
case .outputInitFail:
return "Output initialization failed"
}
}
}
}
Editor is loading...
Leave a Comment