126 lines
4.5 KiB
Swift
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)
|
|
}
|
|
}
|
|
}
|