If your app requires QR code scanning capabilities, or if you’re simply interested in this topic, you might find this post useful.
First, we’ll craft the primary view, integrating a toolbar button that unveils a sheet dedicated to scanning, with the QR code displayed centrally upon detection.
Subsequently, we’ll delineate the sheet featuring a closure mechanism, ensuring it automatically dismisses upon QR code identification.
Lastly, we’ll delve into the crux of the matter: the scanning process itself.
First thing we have to add to the permission the Privacy – Camera Usage Description otherwise the app will not start.
Introducing the ContentView:
struct ContentView: View { @State var qr = "" @State var scan = false var body: some View { NavigationStack { VStack { Text(qr) }.sheet(isPresented: $scan) { SheetView(qrCode: $qr) } .padding() .toolbar { Button(action: { qr = "" scan = true }) { Image(systemName: "qrcode.viewfinder") } } } } }
Thus, the SheetView is activated when the scan button is pressed, and any QR code scanned is stored in the QR variable.
Here’s a closer look at the SheetView:
import SwiftUI struct SheetView: View { @Environment(\.dismiss) private var dismiss @Binding var qrCode: String var body: some View { VStack { QrScannerView(qr: $qrCode) }.overlay( VStack { Spacer() Button("Close") { dismiss() }.foregroundStyle(.black) .frame(maxWidth: 60, maxHeight: 60) .background(.yellow) .cornerRadius(10) } ).onChange(of: qrCode) { dismiss() } } } #Preview { @State var dum = "" return SheetView(qrCode: $dum) }
This view incorporates the QrScannerView, which stands as the cornerstone of the application, alongside a button for closing the view. Upon detecting a QR code, the sheet is automatically dismissed via the onChange mechanism.
Now, onto the crucial part:
import SwiftUI import VisionKit @MainActor struct QrScannerView: UIViewControllerRepresentable { @Binding var qr: String var scannerViewController: DataScannerViewController = DataScannerViewController( recognizedDataTypes: [.barcode()], qualityLevel: .accurate, recognizesMultipleItems: false, isHighFrameRateTrackingEnabled: false, isHighlightingEnabled: false ) var scannerAvailable: Bool { DataScannerViewController.isSupported && DataScannerViewController.isAvailable } func makeUIViewController(context: Context) -> DataScannerViewController { scannerViewController.delegate = context.coordinator return scannerViewController } func updateUIViewController(_ uiViewController: DataScannerViewController, context: Context) { } func makeCoordinator() -> Coordinator { return Coordinator(self) } class Coordinator: NSObject, DataScannerViewControllerDelegate { var parent: QrScannerView var qr = "" init(_ parent: QrScannerView) { self.parent = parent parent.start() } func dataScanner(_ dataScanner: DataScannerViewController, didAdd addedItems: [RecognizedItem], allItems: [RecognizedItem]) { processAddedItems(items: addedItems) } func dataScanner(_ dataScanner: DataScannerViewController, didRemove removedItems: [RecognizedItem], allItems: [RecognizedItem]) { } func dataScanner(_ dataScanner: DataScannerViewController, didUpdate updatedItems: [RecognizedItem], allItems: [RecognizedItem]) { } func dataScanner(_ dataScanner: DataScannerViewController, didTapOn item: RecognizedItem) { processItem(item: item) } func processAddedItems(items: [RecognizedItem]) { for item in items { processItem(item: item) } } func processItem(item: RecognizedItem) { switch item { case .barcode(let code): parent.qr = code.payloadStringValue ?? "" case .text(_): break @unknown default: print("Should not happen") } } } }
To delve deeper, it’s essential to highlight a few key points, starting with the importation of VisionKit. This toolkit empowers developers to scan not only QR codes and barcodes but also text.
The view is annotated with @MainActor
, indicating it executes on the main thread, ensuring the UI remains responsive and updated in real-time.
It implements the UIViewControllerRepresentable
protocol, facilitating the integration of a UIViewController
within SwiftUI. This approach enables SwiftUI views to manage traditional UIKit controllers seamlessly.
The qr
variable is designated to store the string extracted from the QR code, serving as a bridge for data retrieved from scans.
scannerViewController
encapsulates the scanner controller, initialized with specific properties tailored for barcode scanning, although it’s also capable of scanning text.
scannerAvailable
assesses the availability of the scanner (and by extension, the camera), ensuring the device can perform the required operations.
updateUIViewController
is responsible for configuring the controller post-initialization, while makeController
initializes and returns the controller, setting the stage for its operation.
The controller adheres to the DataScannerControllerViewDelegate
protocol, which is defined as an object that analyzes live video from the camera for text, data within text, and machine-readable codes, essentially handling the intricate aspects of scanning.
Scanning starts automatically upon the controller’s initialization, with a call to start
from the parent object, streamlining the process for immediate action.
processItem
meticulously analyzes discovered items; if an item is identified as a QR code, its string value is captured and processed accordingly.
The use of @unknown default
in Swift provides a safeguard against potential incompatibilities that might arise in the future, ensuring the application remains robust across updates and changes in the development landscape.
The code https://github.com/niqt/swift-qr-code
Note: English is not my native language, so I apologize for any errors. I use AI solely to generate the banner of the post; the content is human-generated.
To subscribe to my newsletter [https://nicoladefilippo.com/#mailinglist]