Skip to content
Snippets Groups Projects
CameraController.swift 1.85 KiB
import Combine
import AVFoundation

final class CameraController: NSObject {
    var dataPublisher: AnyPublisher<Data, Never> {
        dataSubject
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    }

    lazy var previewLayer: CALayer = {
        let layer = AVCaptureVideoPreviewLayer(session: session)
        layer.videoGravity = .resizeAspectFill
        return layer
    }()

    private let session = AVCaptureSession()
    private let metadataOutput = AVCaptureMetadataOutput()
    private let dataSubject = PassthroughSubject<Data, Never>()

    override init() {
        super.init()
        setupCameraDevice()
    }

    func start() {
        guard session.isRunning == false else { return }
        session.startRunning()
    }

    func stop() {
        guard session.isRunning == true else { return }
        session.stopRunning()
    }

    private func setupCameraDevice() {
        if let captureDevice = AVCaptureDevice.default(for: .video),
           let input = try? AVCaptureDeviceInput(device: captureDevice) {

            if session.canAddInput(input) && session.canAddOutput(metadataOutput) {
                session.addInput(input)
                session.addOutput(metadataOutput)
            }

            metadataOutput.setMetadataObjectsDelegate(self, queue: .main)
            metadataOutput.metadataObjectTypes = [.qr]
        }
    }

    func metadataOutput(
        _ output: AVCaptureMetadataOutput,
        didOutput metadataObjects: [AVMetadataObject],
        from connection: AVCaptureConnection
    ) {
        guard let object = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
              let data = object.stringValue?.data(using: .nonLossyASCII), object.type == .qr else { return }
        dataSubject.send(data)
    }
}

extension CameraController: AVCaptureMetadataOutputObjectsDelegate {}