How to scan QRCode with SwiftUI

SwiftUI + UIViewControllerRepresentable scanning QRCode

  • Xcode version: 11.0 beta 4
  • macOS version: 10.15 Beta

Prerequisites

This blog post is a demo for integrating a custom UIViewController with SwiftUI using the Coordinator pattern, which I think is really awesome - with SwiftUI you can build nice UI very easily and leave the backend implementation fallback to UIKit and bridging them together using UIViewControllerRepresentable and in the Coordinator you create the delegates/datasources so that the data can be passed back from a UIViewController to the SwiftUI @State or elsewhere.

Get Started

Create the ViewController that does the scan

This part is quite simple, just copy and paste the demo code from here into a new file, say, ScannerViewController

And make two changes:

  1. Add a new delegate for SwiftUI. Add a new line after:
var previewLayer: AVCaptureVideoPreviewLayer!

So it becomes:

var previewLayer: AVCaptureVideoPreviewLayer!
var delegate: QRCodeScannerDelegate?
  1. Change the found function from
func found(code: String) {
    print(code)
}

to

func found(code: String) {
    self.delegate?.codeDidFind(code)
}

Now create a delegate protocol in the same file:

protocol QRCodeScannerDelegate {
    func codeDidFind(_ code: String)
}

Now we are done with the ViewController, let's go back to the SwiftUI part!

Create the UIViewControllerRepresentable

Create a new file, named QRCodeScane.swift with this content:

import SwiftUI

struct QRCodeScan: UIViewControllerRepresentable {
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIViewController(context: Context) -> QRCodeScanVC {
        let vc = QRCodeScanVC()
        vc.delegate = context.coordinator
        return vc
    }
    
    func updateUIViewController(_ vc: QRCodeScanVC, context: Context) {
    }

    class Coordinator: NSObject, QRCodeScannerDelegate {
        
        func codeDidFind(_ code: String) {
            print(code)
        }
        
        var parent: QRCodeScan
        
        init(_ parent: QRCodeScan) {
            self.parent = parent
        }
    }
}

#if DEBUG
struct QRCodeScan_Previews: PreviewProvider {
    static var previews: some View {
        QRCodeScan()
    }
}
#endif

In the sample, we made the Coordinator implement the QRCodeScannerDelegate protocol that we just created earlier.

We are done!