Ali
Ali

Reputation: 1012

Swift 4: Scanning QR codes with dictionary inputs

I am trying to scan a qr code that I have generated using Adding Multiple Key-Value Pairs to QR Code as a guide. I can generate the qr code, but when I try to scan it, it yields a metadataObj.stringValue of null.

Here is my code to read the metadata output:

func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
    // Check if the metadataObjects array is not nil and it contains at least one object.
    if metadataObjects == [] || metadataObjects.count == 0 {
        qrCodeFrameView?.frame = CGRect.zero
        print("No QR code is detected")
        return
    }

    // Get the metadata object.
    let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject

    if metadataObj.type == AVMetadataObject.ObjectType.qr {
        // If the found metadata is equal to the QR code metadata then update the status label's text and set the bounds
        if let layer = previewLayer{
            let barCodeObject = layer.transformedMetadataObject(for: metadataObj)
            qrCodeFrameView?.frame = barCodeObject!.bounds
        }


        guard let inputData = metadataObj.stringValue?.data(using: String.Encoding.isoLatin1, allowLossyConversion: false),
            let dictionary = NSKeyedUnarchiver.unarchiveObject(with: inputData) as? [String: NSData] else { return }

        print(dictionary["firstName"] ?? "None")
    }
}

How do I unarchive the data if the meta data object is outputting a string value of null?

Upvotes: 4

Views: 2876

Answers (2)

no_time_left
no_time_left

Reputation: 41

I was able to encode a Swift dictionary into a QR code and decode it from the metadataObject output, using the Codable protocol

Here's the thought process for encoding your data into a QR code:

  1. conform your data type (could be a dictionary, could be your own struct) to the Codable protocol
  2. serialize your data to JSON via JSON Encoder
  3. put your serialized data into the Core Image filter that generates your QR code

Here's the code I used to convert a swift dictionary into a QR code:

func generateQRCode(from dictionary: [String:String]) -> UIImage? {
    do {
        let data = try JSONEncoder().encode(dictionary)
        if let validData = String(data: data,encoding: .utf8){
            print(validData)
        }

        if let filter = CIFilter(name: "CIQRCodeGenerator"){
            filter.setValue(data, forKey: "inputMessage")
            let transform = CGAffineTransform(scaleX: 10, y: 10)
            if let output = filter.outputImage?.transformed(by: transform){
                return UIImage(ciImage: output)
            }
        }
    } catch {
        print(error.localizedDescription)
    }
    return nil
}

Here's the thought process for decoding your data from the QR code:

  1. check to make sure your metadataObject is indeed a qr code
  2. convert your metadataObject to a Data object with utf-8 encoding
  3. decode the data object using JSON decoder and specify the data type you are decoding

Here's the code I wrote for decoding the QR code back into a Swift dictionary using the QR delegate function metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection).

func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {


    if let metadataObject = metadataObjects.first{
        if metadataObject.type == .qr{
            let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject
            do {
                if let validData = readableObject?.stringValue?.data(using: .utf8){

                    let dict = try JSONDecoder().decode([String:String].self,from:validData)
                    //do stuff with dict                        
                    print(dict)
                    AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
                }
            } catch {
                print(error.localizedDescription)
            }

        }
    }
    navigationController?.popViewController(animated: true)
}

Hope that helps anyone still stuck on this question. Codable is very convenient for serializing data types into JSON, which suits QR codes quite well IMO.

Upvotes: 4

user3211359
user3211359

Reputation: 49

have you tried to iterate over the dictionary to double check if the value is valid?

perhaps if the lossyconversion is set to false, then the string is set to nil.

Upvotes: 0

Related Questions