Skip to content

Commit

Permalink
Merge pull request #80 from twostraws/coordinator
Browse files Browse the repository at this point in the history
Refactoring coordinator
  • Loading branch information
nathanfallet authored Aug 28, 2022
2 parents b1acc06 + 078e8ca commit 0568b94
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 119 deletions.
10 changes: 2 additions & 8 deletions Sources/CodeScanner/CodeScanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,12 @@ public struct CodeScannerView: UIViewControllerRepresentable {
self.completion = completion
}

public func makeCoordinator() -> ScannerCoordinator {
ScannerCoordinator(parent: self)
}

public func makeUIViewController(context: Context) -> ScannerViewController {
let viewController = ScannerViewController(showViewfinder: showViewfinder)
viewController.delegate = context.coordinator
return viewController
return ScannerViewController(showViewfinder: showViewfinder, parentView: self)
}

public func updateUIViewController(_ uiViewController: ScannerViewController, context: Context) {
context.coordinator.parent = self
uiViewController.parentView = self
uiViewController.updateViewController(
isTorchOn: isTorchOn,
isGalleryPresented: isGalleryPresented.wrappedValue,
Expand Down
93 changes: 0 additions & 93 deletions Sources/CodeScanner/ScannerCoordinator.swift

This file was deleted.

107 changes: 89 additions & 18 deletions Sources/CodeScanner/ScannerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,25 @@ import UIKit
@available(macCatalyst 14.0, *)
extension CodeScannerView {

public class ScannerViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
public class ScannerViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, AVCaptureMetadataOutputObjectsDelegate {

var delegate: ScannerCoordinator?
var parentView: CodeScannerView!
var codesFound = Set<String>()
var didFinishScanning = false
var lastTime = Date(timeIntervalSince1970: 0)
private let showViewfinder: Bool

private var isGalleryShowing: Bool = false {
didSet {
// Update binding
if delegate?.parent.isGalleryPresented.wrappedValue != isGalleryShowing {
delegate?.parent.isGalleryPresented.wrappedValue = isGalleryShowing
if parentView.isGalleryPresented.wrappedValue != isGalleryShowing {
parentView.isGalleryPresented.wrappedValue = isGalleryShowing
}
}
}

public init(showViewfinder: Bool = false) {
public init(showViewfinder: Bool = false, parentView: CodeScannerView) {
self.parentView = parentView
self.showViewfinder = showViewfinder
super.init(nibName: nil, bundle: nil)
}
Expand Down Expand Up @@ -62,10 +66,10 @@ extension CodeScannerView {
}

if qrCodeLink == "" {
delegate?.didFail(reason: .badOutput)
didFail(reason: .badOutput)
} else {
let result = ScanResult(string: qrCodeLink, type: .qr)
delegate?.found(result)
found(result)
}
} else {
print("Something went wrong")
Expand Down Expand Up @@ -203,7 +207,7 @@ extension CodeScannerView {
view.layer.addSublayer(previewLayer)
addviewfinder()

delegate?.reset()
reset()

if (captureSession.isRunning == false) {
DispatchQueue.global(qos: .userInteractive).async {
Expand All @@ -217,7 +221,7 @@ extension CodeScannerView {
case .restricted:
break
case .denied:
self.delegate?.didFail(reason: .permissionDenied)
self.didFail(reason: .permissionDenied)
case .notDetermined:
self.requestCameraAccess {
self.setupCaptureDevice()
Expand All @@ -237,7 +241,7 @@ extension CodeScannerView {
private func requestCameraAccess(completion: (() -> Void)?) {
AVCaptureDevice.requestAccess(for: .video) { [weak self] status in
guard status else {
self?.delegate?.didFail(reason: .permissionDenied)
self?.didFail(reason: .permissionDenied)
return
}
completion?()
Expand All @@ -260,7 +264,7 @@ extension CodeScannerView {
private func setupCaptureDevice() {
captureSession = AVCaptureSession()

guard let videoCaptureDevice = delegate?.parent.videoCaptureDevice ?? fallbackVideoCaptureDevice else {
guard let videoCaptureDevice = parentView.videoCaptureDevice ?? fallbackVideoCaptureDevice else {
return
}

Expand All @@ -269,14 +273,14 @@ extension CodeScannerView {
do {
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
} catch {
delegate?.didFail(reason: .initError(error))
didFail(reason: .initError(error))
return
}

if (captureSession!.canAddInput(videoInput)) {
captureSession!.addInput(videoInput)
} else {
delegate?.didFail(reason: .badInput)
didFail(reason: .badInput)
return
}

Expand All @@ -285,10 +289,10 @@ extension CodeScannerView {
if (captureSession!.canAddOutput(metadataOutput)) {
captureSession!.addOutput(metadataOutput)

metadataOutput.setMetadataObjectsDelegate(delegate, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = delegate?.parent.codeTypes
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = parentView.codeTypes
} else {
delegate?.didFail(reason: .badOutput)
didFail(reason: .badOutput)
return
}
}
Expand Down Expand Up @@ -330,7 +334,7 @@ extension CodeScannerView {
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard touches.first?.view == view,
let touchPoint = touches.first,
let device = delegate?.parent.videoCaptureDevice ?? fallbackVideoCaptureDevice,
let device = parentView.videoCaptureDevice ?? fallbackVideoCaptureDevice,
device.isFocusPointOfInterestSupported
else { return }

Expand All @@ -355,7 +359,7 @@ extension CodeScannerView {
}

@objc func manualCapturePressed(_ sender: Any?) {
self.delegate?.readyManualCapture()
self.readyManualCapture()
}

func showManualCaptureButton(_ isManualCapture: Bool) {
Expand Down Expand Up @@ -408,5 +412,72 @@ extension CodeScannerView {
#endif
}

public func reset() {
codesFound.removeAll()
didFinishScanning = false
lastTime = Date(timeIntervalSince1970: 0)
}

public func readyManualCapture() {
guard parentView.scanMode == .manual else { return }
self.reset()
lastTime = Date()
}

public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
guard let stringValue = readableObject.stringValue else { return }
guard didFinishScanning == false else { return }
let result = ScanResult(string: stringValue, type: readableObject.type)

switch parentView.scanMode {
case .once:
found(result)
// make sure we only trigger scan once per use
didFinishScanning = true

case .manual:
if !didFinishScanning, isWithinManualCaptureInterval() {
found(result)
didFinishScanning = true
}

case .oncePerCode:
if !codesFound.contains(stringValue) {
codesFound.insert(stringValue)
found(result)
}

case .continuous:
if isPastScanInterval() {
found(result)
}
}
}
}

func isPastScanInterval() -> Bool {
Date().timeIntervalSince(lastTime) >= parentView.scanInterval
}

func isWithinManualCaptureInterval() -> Bool {
Date().timeIntervalSince(lastTime) <= 0.5
}

func found(_ result: ScanResult) {
lastTime = Date()

if parentView.shouldVibrateOnSuccess {
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
}

parentView.completion(.success(result))
}

func didFail(reason: ScanError) {
parentView.completion(.failure(reason))
}

}
}

0 comments on commit 0568b94

Please sign in to comment.