Issue #973

Before iOS 11, we used to use CIDetector and CIDetectorTypeQRCode to detect QR code

CIDetector

An image processor that identifies notable features, such as faces and barcodes, in a still image or video.

CIDetectorTypeQRCode

A detector that searches for Quick Response codes (a type of 2D barcode) in a still image or video, returning CIQRCodeFeature objects that provide information about detected barcodes.

func readQrOnly(image: CGImage) async -> String? {
    let ciImage = CIImage(cgImage: image)
    
    let detector = CIDetector(
        ofType: CIDetectorTypeQRCode,
        context: nil,
        options: [
            CIDetectorAccuracy: CIDetectorAccuracyHigh
        ]
    )
    
    guard
        let detector = detector
    else { return nil }
    
    let features = detector
        .features(in: ciImage)
        .compactMap { $0 as? CIQRCodeFeature }
    
    guard
        !features.isEmpty
    else { return nil }
    
    return features
        .compactMap(\.messageString)
        .joined(separator: " ")
}

In macOS 10.13, iOS 11, and tvOS 11 or later, the Vision framework replaces these classes for identifying and analyzing image features.

Using VNRequest, and particular VNDetectBarcodesRequest, we can detect barcodes in an image. It supports a bunch of code types, in form of VNBarcodeSymbology

func read(image: CGImage) async -> VNBarcodeObservation? {
    await withCheckedContinuation { con in
        do {
            let request = VNDetectBarcodesRequest(completionHandler: { request, error in
                if let request = request as? VNDetectBarcodesRequest,
                   let result = request.results?.first {
                    con.resume(returning: result)
                } else {
                    con.resume(returning: VNBarcodeObservation?.none)
                }
            })
            request.symbologies = try request.supportedSymbologies()
            request.revision = VNDetectBarcodesRequestRevision1
            
            let handler = VNImageRequestHandler(cgImage: image)
            try handler.perform([request])
        } catch {
            con.resume(returning: VNBarcodeObservation?.none)
        }
    }
}

Note we have to use VNDetectBarcodesRequestRevision1 for now since the default VNDetectBarcodesRequestRevision4 does not seem to work.

With VNBarcodeObservation, we can read payloadStringValue and symbology to get content and which code type it has detected