
Detecting Barcodes on an image with the Vision Framework
Use the new barcode detection API from the Vision framework to identify barcodes on images.
In the reference Reading QR codes and barcodes with the Vision framework we explored how to use VNDetectBarcodeRequest
to retrieve data from various types of barcodes. With iOS 18, Apple introduced DetectBarcodesRequest
, simplifying barcode detection in an image.
Let's see how to transition from using VNDetectBarcodeRequest
to DetectBarcodeRequest
with just a few modifications.
Here is the ScannerView
implementation, responsible for providing access to the camera, performing the barcode detection, and returning the result of reading the barcode to its parent view through a binding.
Focus on the method detectBarcode(in:)
in the Coordinator
class.
import SwiftUI
import Vision
import AVFoundation
struct ScannerView: UIViewControllerRepresentable {
@Binding var scannedString: String
let captureSession = AVCaptureSession()
func makeUIViewController(context: Context) -> UIViewController {
let viewController = UIViewController()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video),
let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice),
captureSession.canAddInput(videoInput) else { return viewController }
captureSession.addInput(videoInput)
let videoOutput = AVCaptureVideoDataOutput()
if captureSession.canAddOutput(videoOutput) {
videoOutput.setSampleBufferDelegate(context.coordinator, queue: DispatchQueue(label: "videoQueue"))
captureSession.addOutput(videoOutput)
}
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = viewController.view.bounds
previewLayer.videoGravity = .resizeAspectFill
viewController.view.layer.addSublayer(previewLayer)
// Start capture session on a background thread
Task(priority: .userInitiated) {
self.captureSession.startRunning()
}
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
// Nothing to do here.
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
var parent: ScannerView
init(_ parent: ScannerView) {
self.parent = parent
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
// Offload barcode detection to a background task
Task {
await self.detectBarcode(in: pixelBuffer)
}
}
func detectBarcode(in pixelBuffer: CVPixelBuffer) async {
var request = DetectBarcodesRequest()
request.symbologies = [.qr, .ean13, .code128]
do {
let results = try await request.perform(on: pixelBuffer)
if let observation = results.first?.payloadString {
Task { @MainActor in
self.parent.scannedString = observation
}
}
} catch {
print("Error processing frame: \(error.localizedDescription)")
}
}
}
}
The primary change is switching from using VNDetectBarcodesRequest
to DetectBarcodesRequest
. This new API is designed to be fully asynchronous and leverages Swift concurrency.
The detectBarcode(in:)
method is now marked as async
, so when analyzing the buffer we use await request.perform(on: pixelBuffer)
. This ensures barcode detection runs without blocking the main thread.
Since the results are retrieved asynchronously, we assign the payloadString
value from the resulting observation to the binding property scannedString
of the ScannerView
, on the MainActor
since it updates the user interface.
Here is an example of how to use it:
import SwiftUI
struct ContentView: View {
@State private var scannedString: String = "Scan a QR code or barcode"
var body: some View {
ZStack(alignment: .bottom) {
ScannerView(scannedString: $scannedString)
.edgesIgnoringSafeArea(.all)
Text(scannedString)
.padding()
.background(.ultraThinMaterial)
.clipShape(RoundedRectangle(cornerRadius: 10))
.padding()
}
}
}
The DetectBarcodeRequest
API is only available on iOS 18 and later. If you need backward compatibility, consider conditionally using VNDetectBarcodeRequest
for earlier versions. This new API aligns with Swift 6’s concurrency model, preparing your code for a more robust and error-free future.