Files
raspberry/iOS/Wallet/Sources/Cards/Controller/CardsScanViewController.swift
2022-07-25 11:26:14 +03:00

126 lines
4.5 KiB
Swift

//
// 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<CreditCard, CreditCardScannerError>) {
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)
}
}
}