DataScannerDemo
Hi there thanks for visiting this repo. If you like it I encourage you to please leave a like.
Before
Before iOS 16 it was quite unintuitave and tedious. There was also the difficulty for figuring out how to work this into your flow. The code below will get you up and running prior to iOS 16. But you would quickly see how tedious it is because of the ever publishing delegate method.
func configureCaptureDevice() {
startRunningSession()
let metadataOutput = AVCaptureMetadataOutput()
session.sessionPreset = AVCaptureSession.Preset.iFrame1280x720
guard let captureDevice = AVCaptureDevice.default(for: .video), let deviceInput = try? AVCaptureDeviceInput(device: captureDevice) else {
return
}
session.addInput(deviceInput)
session.addOutput(metadataOutput)
configureOutput()
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [.ean8, .ean13, .pdf417]
metadataOutput.rectOfInterest = aVCaptureVideoPreviewLayer?.metadataOutputRectConverted(fromLayerRect: CGRect(x: view.frame.width * 0.065, y: view.center.y - 48, width: view.frame.width * 0.85, height: view.frame.width * 0.5)) ?? .zero
}
private func configureOutput() {
aVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
aVCaptureVideoPreviewLayer?.frame = view.frame
aVCaptureVideoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
if let previewLayerUnwrap = aVCaptureVideoPreviewLayer {
view.layer.addSublayer(previewLayerUnwrap)
}
}
}
extension VideoSessionController: AVCaptureMetadataOutputObjectsDelegate {
// MARK: - AVCaptureMetadataOutputObjectsDelegate
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
guard let metadataObject = metadataObjects.first else {
return
}
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else {
return
}
guard let stringValue = readableObject.stringValue else {
return
}
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
self.barcodeStringSubject.send(stringValue)
}
}
After
After iOS 16 we are give an abstracted API to integrate data scanner without the hassles of working with AVFoundation. The code below wraps the new DataScannerViewController
in UIViewControllerRepresentable
to be able to use it in SwiftUI. I also want to cal out a few thing when using this API.
- Apple has nicely integrated Guidance into this controller so it displays on screen prompts to guide the user in a direction which would allow the camera to pickup the target barcode more easily.
- In order to receive the value of the scanned barcode the user must tap the screen after the barcode is recognised. There is no on screen prompt to indicate that the scanning has been complete.
- If you have enabled
isHighlightingEnabled
in the initializer of theDataScannerViewController
an on screen rectangle will be presented to highlight the frame of the barcode which was scanned, which I think is a nice touch. - Dont forget to set the
delegate
in order to receive any changes you must set thedelegate
as an object and conformm to it. - This API is also in beta changes may come to it but not to worry, I will make updates as they arise.
/// A view that allows for the scanning of a barcode.
struct BarcodeScannerView: UIViewControllerRepresentable {
/// Manages the logic related to scanning data.
@ObservedObject var dataScannerManager: DataScannerManager
func makeUIViewController(context: Context) -> DataScannerViewController {
let dataScannerViewController = DataScannerViewController(recognizedDataTypes: [.barcode(symbologies: [.upce,.ean8,.ean13])], qualityLevel: .fast, isHighlightingEnabled: true)
dataScannerViewController.delegate = dataScannerManager
try? dataScannerViewController.startScanning()
return dataScannerViewController
}
func updateUIViewController(_ uiViewController: DataScannerViewController, context: Context) {}
}
/// Manages the properties and methods related to data scanning.
final class DataScannerManager: NSObject, ObservableObject, DataScannerViewControllerDelegate {
/// Value indicating that scanning has failed.
@Published private(set) var dataScannerFailure: DataScannerViewController.ScanningUnavailable?
/// The string of the recognized barcode.
@Published var recognisedBarcodeString: String = ""
// MARK: - DataScannerViewControllerDelegate
func dataScanner(_ dataScanner: DataScannerViewController, didTapOn item: RecognizedItem) {
switch item {
case .text: break
case let .barcode(barcode):
recognisedBarcodeString = barcode.payloadStringValue ?? ""
@unknown default: break
}
}
func dataScanner(_ dataScanner: DataScannerViewController, becameUnavailableWithError error: DataScannerViewController.ScanningUnavailable) {
self.dataScannerFailure = error
}
}