Skip to content
Snippets Groups Projects
SearchQRController.swift 4.4 KiB
Newer Older
import UIKit
import Permissions
import DependencyInjection

enum SearchQRStatus: Equatable {
    case reading
    case processing
    case success
    case failed(SearchQRError)
}

enum SearchQRError: Equatable {
    case requestOpened
    case unknown(String)
    case cameraPermission
    case alreadyFriends(String)
}

final class SearchQRController: UIViewController {
    @Dependency private var permissions: PermissionHandling

    lazy private var screenView = SearchQRView()

    private let camera = Camera()
    private var status: SearchQRStatus?

    override func loadView() {
        view = screenView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        ///screenView.layer.insertSublayer(camera.previewLayer, at: 0)
        ///setupBindings()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        ///camera.previewLayer.frame = screenView.bounds
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        ///viewModel.resetScanner()
        ///startCamera()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
//        backgroundScheduler.schedule { [weak self] in
//            guard let self = self else { return }
//            self.camera.stop()
//        }
    }

    private func startCamera() {
        permissions.requestCamera { [weak self] granted in
            guard let self = self else { return }

            if granted {
                DispatchQueue.main.async { [weak self] in
                    guard let self = self else { return }
                    self.camera.start()
                }
            } else {
                DispatchQueue.main.async {
                    self.status = .failed(.cameraPermission)
                    self.screenView.update(with: .failed(.cameraPermission))
                }
            }
        }
    }
}


import Combine
import AVFoundation

protocol CameraType {
    func start()
    func stop()

    var previewLayer: CALayer { get }
    var dataPublisher: AnyPublisher<Data, Never> { get }
}

final class Camera: NSObject, CameraType {
    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]
        }
    }
}

extension Camera: AVCaptureMetadataOutputObjectsDelegate {
    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)
    }
}

final class MockCamera: NSObject, CameraType {
    private let dataSubject = PassthroughSubject<Data, Never>()

    func start() {
        DispatchQueue.global().asyncAfter(deadline: .now() + 2) { [weak self] in
            self?.dataSubject.send("###".data(using: .utf8)!)
        }
    }

    func stop() {}

    var previewLayer: CALayer { CALayer() }

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