Just An Application

November 15, 2015

Reading QR Codes using the iOS AV Foundation Framework: Prerequisites

Having discovered that one of my iOS applications just maybe needed to be able to read a QR code I was pleased to discover that from iOS 7.0 onwards it is possible to do so using the AV Foundation framework.

There was little bit of trial and error involved in working out how to do set up but after that it “just worked” as they say

1.0 A Session

The first thing we need is a session

    let session = AVCaptureSession()

2.0 An Input

An AVCaptureSession needs an input, an instance of AVCaptureInput, from which to capture things.

In our case we need the input from a camera which, in the context of the AV Foundation framework, is a kind of AVCaptureDevice associaed with the media type

    AVMediaTypeVideo

We can obtain the default device for capturing a given media type by calling the AVCaptureDevice method

    class func defaultDeviceWithMediaType(_ mediaType: String!) -> AVCaptureDevice!

or, alternatively, all the devices capable of capturing a given media type by calling the AVCaptureDevice method

    class func devicesWithMediaType(_ mediaType: String!) -> [AnyObject]!

Using the first method we can do

    ...
    
    if let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
    {
       ...
    }

    ...

On an iPad or an iPhone running iOS 9.0 this returns the ‘back camera’.

If we want to ensure that we get the ‘back camera’, rather than just hoping we do, we can do

    ...
    
    if let objects = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
    {
        for device in objects
        {
            if device.position == .Back
            {
                ...
            }
        }
    }
    
    ...

Once we have our desired device we can contruct an instance of AVCaptureDeviceInput which is a sub-class of AVCaptureInput

    let input = try AVCaptureDeviceInput(device:device)

amd then add it to the session

    session.addInput(input)

3,0 An Output

To identify the QR code(s) if any, in the input from the camera we need an instance of the class AVCaptureMetadataOutput.

    let output = AVCaptureMetadataOutput()

We can then add this to the session as an output.

    session.addOutput(output)

An AVCaptureMetadataOutput object is capable of recognizing more than just QR codes.

To find out exactly what we can access the availableMetadataObjectTypes property.

On an iPad running iOS 9.0 the following code

    ...
    
    for o in output.availableMetadataObjectTypes
    {
        print(o)
    }
    
    ...

produces this output

    ...

    org.iso.Aztec
    org.iso.Code128
    org.iso.Code39
    org.iso.Code39Mod43
    com.intermec.Code93
    org.iso.DataMatrix
    org.gs1.EAN-13
    org.gs1.EAN-8
    org.ansi.Interleaved2of5
    org.gs1.ITF14
    org.iso.PDF417
    org.iso.QRCode
    org.gs1.UPC-E
    face
    
    ...

and on an iPhone running iOS 9.0 this output

    ...

    face
    org.iso.Aztec
    org.iso.Code128
    org.iso.Code39
    org.iso.Code39Mod43
    com.intermec.Code93
    org.iso.DataMatrix
    org.gs1.EAN-13
    org.gs1.EAN-8
    org.ansi.Interleaved2of5
    org.gs1.ITF14
    org.iso.PDF417
    org.iso.QRCode
    org.gs1.UPC-E

    ...

To specify that we are only interested in QR codes we can set the metadataObjectTypes property.

    output.metadataObjectTypes = [AVMetadataObjectTypeQRCode]

Note

In the AVCaptureMetadataOutput case the order in which the input and output are added is significant.

If it is added before the camera input then the availableMetadataTypes property is simply an empty array and attempting to set the metadataTypes property will result in an NSInvalidArgumentException being thrown.

4.0 A Delegate

An AVCaptureMetadataOutput object passes metadata objects obtained from the input to a delegate which is required to implement the AVCaptureMetadataOutputObjectsDelegate protocol.

The AVCaptureMetadataOutputObjectsDelegate protocol defines a single method

    optional func captureOutput(
                      _
                          captureOutput:
                              AVCaptureOutput!,
                      didOutputMetadataObjects
                          metadataObjects:
                              [AnyObject]!,
                      fromConnection
                          connection:
                              AVCaptureConnection!)

Each element of the array passed as the metadataObjects argument is an instance of a sub-class of the class AVMetadataObect.

In the case of a QR code the element will be an instance of the class AVMetadataMachineReadableCodeObject.

The value of the stringValue property of an instance of AVMetadataMachineReadableCodeObject is a human readable version of, in this case, the QR code.

    func captureOutput(
             captureOutput:
                 AVCaptureOutput!,
             didOutputMetadataObjects
                 metadataObjects: [AnyObject]!,
             fromConnection
                 connection:
                    AVCaptureConnection!)
    {
        for metadataObject in metadataObjects
        {
            if metadataObject.type == AVMetadataObjectTypeQRCode
            {
                let value = metadataObject.stringValue
                
                ...
            }
        }
    }

The delegate for an AVCaptureMetadataOutput object can be set using the method

    public func setMetadataObjectsDelegate(
                    objectsDelegate:
                        AVCaptureMetadataOutputObjectsDelegate!,
                    queue
                        objectsCallbackQueue:
                            dispatch_queue_t!)

The queue argument specifies the dispatch queue on which the delegate's method will be invoked.


Copyright (c) 2015 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog's author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

Blog at WordPress.com.