115 lines
3.9 KiB
Swift
115 lines
3.9 KiB
Swift
//
|
|
// CodeScanner.swift
|
|
// https://github.com/twostraws/CodeScanner
|
|
//
|
|
// Created by Paul Hudson on 14/12/2021.
|
|
// Copyright © 2021 Paul Hudson. All rights reserved.
|
|
//
|
|
|
|
import AVFoundation
|
|
import SwiftUI
|
|
|
|
/// An enum describing the ways CodeScannerView can hit scanning problems.
|
|
public enum ScanError: Error {
|
|
/// The camera could not be accessed.
|
|
case badInput
|
|
|
|
/// The camera was not capable of scanning the requested codes.
|
|
case badOutput
|
|
|
|
/// Initialization failed.
|
|
case initError(_ error: Error)
|
|
}
|
|
|
|
/// The result from a successful scan: the string that was scanned, and also the type of data that was found.
|
|
/// The type is useful for times when you've asked to scan several different code types at the same time, because
|
|
/// it will report the exact code type that was found.
|
|
public struct ScanResult {
|
|
/// The contents of the code.
|
|
public let string: String
|
|
|
|
/// The type of code that was matched.
|
|
public let type: AVMetadataObject.ObjectType
|
|
}
|
|
|
|
/// The operating mode for CodeScannerView.
|
|
public enum ScanMode {
|
|
/// Scan exactly one code, then stop.
|
|
case once
|
|
|
|
/// Scan each code no more than once.
|
|
case oncePerCode
|
|
|
|
/// Keep scanning all codes until dismissed.
|
|
case continuous
|
|
}
|
|
|
|
/// A SwiftUI view that is able to scan barcodes, QR codes, and more, and send back what was found.
|
|
/// To use, set `codeTypes` to be an array of things to scan for, e.g. `[.qr]`, and set `completion` to
|
|
/// a closure that will be called when scanning has finished. This will be sent the string that was detected or a `ScanError`.
|
|
/// For testing inside the simulator, set the `simulatedData` property to some test data you want to send back.
|
|
public struct CodeScannerView: UIViewControllerRepresentable {
|
|
|
|
public let codeTypes: [AVMetadataObject.ObjectType]
|
|
public let scanMode: ScanMode
|
|
public let scanInterval: Double
|
|
public let showViewfinder: Bool
|
|
public var simulatedData = ""
|
|
public var shouldVibrateOnSuccess: Bool
|
|
public var isTorchOn: Bool
|
|
public var isGalleryPresented: Binding<Bool>
|
|
public var videoCaptureDevice: AVCaptureDevice?
|
|
public var completion: (Result<ScanResult, ScanError>) -> Void
|
|
|
|
public init(
|
|
codeTypes: [AVMetadataObject.ObjectType],
|
|
scanMode: ScanMode = .once,
|
|
scanInterval: Double = 2.0,
|
|
showViewfinder: Bool = false,
|
|
simulatedData: String = "",
|
|
shouldVibrateOnSuccess: Bool = true,
|
|
isTorchOn: Bool = false,
|
|
isGalleryPresented: Binding<Bool> = .constant(false),
|
|
videoCaptureDevice: AVCaptureDevice? = AVCaptureDevice.default(for: .video),
|
|
completion: @escaping (Result<ScanResult, ScanError>) -> Void
|
|
) {
|
|
self.codeTypes = codeTypes
|
|
self.scanMode = scanMode
|
|
self.showViewfinder = showViewfinder
|
|
self.scanInterval = scanInterval
|
|
self.simulatedData = simulatedData
|
|
self.shouldVibrateOnSuccess = shouldVibrateOnSuccess
|
|
self.isTorchOn = isTorchOn
|
|
self.isGalleryPresented = isGalleryPresented
|
|
self.videoCaptureDevice = videoCaptureDevice
|
|
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
|
|
}
|
|
|
|
public func updateUIViewController(_ uiViewController: ScannerViewController, context: Context) {
|
|
uiViewController.updateViewController(
|
|
isTorchOn: isTorchOn,
|
|
isGalleryPresented: isGalleryPresented.wrappedValue
|
|
)
|
|
}
|
|
|
|
}
|
|
|
|
@available(macCatalyst 14.0, *)
|
|
struct CodeScannerView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
CodeScannerView(codeTypes: [.qr]) { result in
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|