How to Build a QR Code Scanner in SwiftUI
In this tutorial, I will show you how to build a QR code scanner in SwiftUI.
Will start with a simple and basic QR code scanner. There will be no button. On opening the App the camera will open automatically and once it finds a QR code, it will show the value of the QR code as a string. That’s all.
Then we will add copy button and open URL option if any URL found in the QR code.
I will use AVFoundation to interact with the device’s camera.
So you have to keep in mind this:
- Your app must request for device’s camera permission in order to access the camera.
To make it possible you have to add a new entry in info.plist
Take a look at Add NSCameraUsageDescription key in info.plist – This tutorial will guide you to add that to your project’s info.plist
If you do not do this, your app will crash. Believe me, it will take less than a minute to add it.
We need to import AVFoundation for adding vibration and sound when a QR code is found.
QR Code scanner in SwiftUI (Open camera and scan QR code)
import SwiftUI import AVFoundation struct QRCodeScannerView: UIViewControllerRepresentable { class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate { var parent: QRCodeScannerView init(parent: QRCodeScannerView) { self.parent = parent } 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 } AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate)) AudioServicesPlaySystemSound(SystemSoundID(1106)) parent.didFindCode(stringValue) } } } var didFindCode: (String) -> Void func makeCoordinator() -> Coordinator { return Coordinator(parent: self) } func makeUIViewController(context: Context) -> UIViewController { let viewController = UIViewController() let session = AVCaptureSession() guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return viewController } let videoInput: AVCaptureDeviceInput do { videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice) } catch { return viewController } if (session.canAddInput(videoInput)) { session.addInput(videoInput) } else { return viewController } let metadataOutput = AVCaptureMetadataOutput() if (session.canAddOutput(metadataOutput)) { session.addOutput(metadataOutput) metadataOutput.setMetadataObjectsDelegate(context.coordinator, queue: DispatchQueue.main) metadataOutput.metadataObjectTypes = [.qr] } else { return viewController } let previewLayer = AVCaptureVideoPreviewLayer(session: session) previewLayer.frame = viewController.view.layer.bounds previewLayer.videoGravity = .resizeAspectFill viewController.view.layer.addSublayer(previewLayer) session.startRunning() return viewController } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} } struct ContentView: View { @State private var scannedCode: String? var body: some View { VStack { if let scannedCode = scannedCode { Text("Scanned code: \(scannedCode)") } else { QRCodeScannerView { code in self.scannedCode = code } .edgesIgnoringSafeArea(.all) } } } }
Output:
Once the QR code is found, it makes a small beep sound a slight vibration that you can feel.
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
this is for the vibration and
AudioServicesPlaySystemSound(SystemSoundID(1106
))
this is for the beep sound.
If you wish you can change the sound. You can check this: Haptic Feedback with Sound in SwiftUI
VStack { if let scannedCode = scannedCode { Text("Scanned code: \(scannedCode)") } else { QRCodeScannerView { code in self.scannedCode = code } .edgesIgnoringSafeArea(.all) } }
This is the part of ContentView where I used Text("Scanned code: \(scannedCode)")
You can customize this part to make it creative and better.
You might be thinking that this is too simple that there is no button to start the Scanner or no button to scan again.
Don’t worry, we will add those too.
Adding start scanning and scan again button to our QR code Scanner
In order to add these button, we just need to modify the ContentView and rest of the code will be same.
This is our new updated ContentView struct.
struct ContentView: View { @State private var scannedCode: String? @State private var isScanning = false var body: some View { VStack { if let scannedCode = scannedCode { Text("Scanned code: \(scannedCode)") .padding() Button("Scan Again") { self.isScanning = true self.scannedCode = nil } .padding() } else { if isScanning { QRCodeScannerView(didFindCode: { code in self.scannedCode = code }, didTapStartScanning: { self.isScanning = true }) .edgesIgnoringSafeArea(.all) } else { Button("Start Scanning") { self.isScanning = true } .padding() } } } } }
This will run perfectly.
It would be great if we add a copy to the clipboard option to copy our scanned code.
Adding copy to clipboard option to our QR code scanner
Update the ContentView struct like this:
struct ContentView: View { @State private var scannedCode: String? @State private var isScanning = false var body: some View { VStack { if let scannedCode = scannedCode { Text("Scanned code: \(scannedCode)") .padding() HStack { Button("Copy") { UIPasteboard.general.string = scannedCode } .padding() Button("Scan Again") { self.isScanning = true self.scannedCode = nil } .padding() } } else { if isScanning { QRCodeScannerView(didFindCode: { code in self.scannedCode = code }, didTapStartScanning: { self.isScanning = true }) .edgesIgnoringSafeArea(.all) } else { Button("Start Scanning") { self.isScanning = true } .padding() } } } } }
If you intend to scan URL too with QR code:
We often scan QR codes that is actually an URL and we need to open that. So copying the code and pasting in a browser might be a time-consuming task. To make it easier for us, we will add an open URL button if a URL is found.
Update the ContentView part like this:
struct ContentView: View { @State private var scannedCode: String? @State private var isScanning = false var body: some View { VStack { if let scannedCode = scannedCode { Text("Scanned code: \(scannedCode)") .padding() if let url = URL(string: scannedCode), UIApplication.shared.canOpenURL(url) { HStack { Button("Open URL") { UIApplication.shared.open(url) } .padding() } } HStack { Button("Copy") { UIPasteboard.general.string = scannedCode } .padding() Button("Scan Again") { self.isScanning = true self.scannedCode = nil } .padding() } } else { if isScanning { QRCodeScannerView(didFindCode: { code in self.scannedCode = code }, didTapStartScanning: { self.isScanning = true }) .edgesIgnoringSafeArea(.all) } else { Button("Start Scanning") { self.isScanning = true } .padding() } } } } }
Output:
There are a lot of things you can add with this app. Let everyone know what you want to add in the comment section below.
If you are a beginner, you can check my another tutorial on Create SwiftUI App for Movie Listing using TMDB API
Leave a Reply