// // CardsScanViewController.swift // Wallet // // Created by Alexandr Serpokrylow on 10.02.2022. // Copyright © 2022 AM. All rights reserved. // import AVFoundation import UIKit public protocol CardsScanViewControllerDelegate: AnyObject { func creditCardScannerViewControllerDidCancel(_ viewController: CardsScanViewController) func creditCardScannerViewController(_ viewController: CardsScanViewController, didErrorWith error: CreditCardScannerError) func creditCardScannerViewController(_ viewController: CardsScanViewController, didFinishWith card: CreditCard) } open class CardsScanViewController: UIViewController { @IBOutlet weak var cameraView: CameraView! @IBOutlet weak var descriptionLabel: UILabel! private lazy var analyzer = ImageAnalyzer(delegate: self) weak var delegate: CardsScanViewControllerDelegate? override open func viewDidLoad() { super.viewDidLoad() configureView() } override open func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() cameraView.setupRegionOfInterest() } private func configureView() { cameraView.delegate = self descriptionLabel.textAlignment = .center AVCaptureDevice.authorize { [weak self] authoriazed in guard let strongSelf = self else { return } guard authoriazed else { strongSelf.delegate?.creditCardScannerViewController(strongSelf, didErrorWith: CreditCardScannerError(kind: .authorizationDenied, underlyingError: nil)) return } strongSelf.cameraView.setupCamera() } } @IBAction func cancel(_ sender: Any) { delegate?.creditCardScannerViewControllerDidCancel(self) } } extension CardsScanViewController: CameraViewDelegate { internal func didCapture(image: CGImage) { analyzer.analyze(image: image) } internal func didError(with error: CreditCardScannerError) { DispatchQueue.main.async { [weak self] in guard let strongSelf = self else { return } strongSelf.delegate?.creditCardScannerViewController(strongSelf, didErrorWith: error) strongSelf.cameraView.stopSession() } } } extension CardsScanViewController: ImageAnalyzerProtocol { internal func didFinishAnalyzation(with result: Result) { switch result { case let .success(creditCard): DispatchQueue.main.async { [weak self] in guard let strongSelf = self else { return } strongSelf.cameraView.stopSession() if let number = creditCard.number, CCValidator.validate(creditCardNumber: Cards.Model.cleaned(number: number), validatePrefix: false, validateLength: true, useLuhnAlgorithm: true) { strongSelf.delegate?.creditCardScannerViewController(strongSelf, didFinishWith: creditCard) strongSelf.delegate?.creditCardScannerViewControllerDidCancel(strongSelf) } else { Alert.error(text: L10n.Cards.Card.Recognize.notValid) self?.dismiss(animated: true) } } case let .failure(error): DispatchQueue.main.async { [weak self] in guard let strongSelf = self else { return } strongSelf.cameraView.stopSession() strongSelf.delegate?.creditCardScannerViewController(strongSelf, didErrorWith: error) strongSelf.delegate?.creditCardScannerViewControllerDidCancel(strongSelf) } } } } public extension CardsScanViewControllerDelegate where Self: UIViewController { func creditCardScannerViewControllerDidCancel(_ viewController: CardsScanViewController) { viewController.dismiss(animated: true) } } extension AVCaptureDevice { static func authorize(authorizedHandler: @escaping ((Bool) -> Void)) { let mainThreadHandler: ((Bool) -> Void) = { isAuthorized in DispatchQueue.main.async { authorizedHandler(isAuthorized) } } switch authorizationStatus(for: .video) { case .authorized: mainThreadHandler(true) case .notDetermined: requestAccess(for: .video, completionHandler: { granted in mainThreadHandler(granted) }) default: mainThreadHandler(false) } } }