Implement scanning QRCode ViewController via AVCaptureSession, AVCaptureMetadataOutput and AVCaptureVideoPreviewLayer

My sample project source code in GitHub

Prerequisite

Initialization

Carry out all the initializations in viewDidLoad

Initialize AVCaptureSession

1
captureSession = AVCaptureSession()

Add video input to the capture session

1
2
3
4
5
6
7
8
9
10
11
12
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else{
return
}
guard let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice) else{
return
}

if (captureSession.canAddInput(videoInput)) {
captureSession.addInput(videoInput)
} else {
return
}

Add metadata output to the capture session

Use AVCaptureMetadataOutput for capturing QRCode or others
Metadata object type:
.qr -> QRCode
.code128 -> Bar code

1
2
3
4
5
6
7
8
9
let metadataOutput = AVCaptureMetadataOutput()
if (captureSession.canAddOutput(metadataOutput)) {
captureSession.addOutput(metadataOutput)

metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [.qr, .code128]
} else {
return
}

Add preview layer to the ViewController

1
2
3
4
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(previewLayer)

Start capture session

1
2
3
DispatchQueue.global().async {
self.captureSession.startRunning()
}

Start & Stop capture session

Start the capture session when view appears and stop the capture session when view disappears

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

if (captureSession?.isRunning == false) {
DispatchQueue.global().async {
self.captureSession.startRunning()
}
}
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)

if (captureSession?.isRunning == true) {
captureSession.stopRunning()
}
}

Implement the delegate

Implement the AVCaptureMetadataOutputObjectsDelegate to get the scan result

1
2
3
4
5
6
7
8
9
10
11
12
13
extension ScannerViewController: AVCaptureMetadataOutputObjectsDelegate{
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
captureSession.stopRunning()

if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
guard let value = readableObject.stringValue else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))

// value here is the scanning result string
}
}
}