iOS

Building a QR Code Reader in Swift


So, what’s QR code? I believe most of you know what a QR code is. In case you haven’t heard of it, just take a look at the image below. That’s QR code.

QR (short for Quick Response) code is a kind of 2-dimensional bar code developed by Denso. Originally designed for tracking parts in manufacturing, QR code has gained popularity in consumer space in recent years as a way to encode URL of a landing page or marketing information. Unlike the basic barcode that you’re familiar with, QR code contains information in both horizontal and vertical direction. Thus this contributes to its capability of storing larger amount of data in both numeric and letter form. Here I don’t want to go into the technical details of QR code. If you’re interested, you can check out the official website of QR code to learn more.

qrcode-featured

In recent years, the use of QR code has been on the rise. It appears in magazines, newspapers, advertisement, billboards and even name cards. As an iOS developer, you may wonder how to empower your app to read QR code. Some time earlier, Gabriel wrote a great tutorial on QR code. In this tutorial, we will build a similar QR code reader app but in Swift. After going through the tutorial, you will understand how to use the AVFoundation framework to discover and read QR code in real-time.

Let’s get started.

Demo App

The demo app that we’re going to build is fairly simple and straightforward. Before I proceed to discuss the demo app, it’s important to understand that any barcode scanning in iOS including QR code is totally based on video capturing. That’s why the barcode scanning feature is added in the AVFoundation framework. Keep this point in mind as it’ll help you understand the entire chapter.

So, how does the demo app work?

Take a look at the screenshot below. This is how the app UI looks like. The app works pretty much like a video capturing app but without recording feature. When the app is launched, it takes advantage of the iPhone’s rear camera to spot the QR code and recognizes it automatically. The decoded information (e.g. an URL) is displayed right at the bottom of the screen.

qrcode-demo-app

To build the app, you can start by downloading the project template from here. I have pre-built the storyboard and link up the message label for you.

Using AVFoundation Framework

I have created the user interface of the app in the project template. The label in the UI will be used to display decoded information of QR code and it is associated with the messageLabel property of the ViewController class.

As mentioned, we rely on the AVFoundation framework to implement the QR code scanning feature. First, open ViewController.swift and import the framework:

Later, we’ll need to implement the AVCaptureMetadataOutputObjectsDelegate protocol. We’ll talk about that in a while. For now, update the following line of code:

Before moving on, declare the following variables in ViewController class. We’ll talk about them one by one later.

Implementing video capture

As mentioned in the earlier section, QR code reading is totally based on video capture. To perform a real-time capture, all we need to do is instantiate an AVCaptureSession object with input set to the appropriate AVCaptureDevice for video capturing. Insert the following code in the viewDidLoad method of ViewController class:

An AVCaptureDevice object represents a physical capture device. You use a capture device to configure the properties of the underlying hardware. As we are going to capture video data, we call the defaultDeviceWithMediaType method with AVMediaTypeVideo to get the video capture device. To perform a real-time capture, we instantiate an AVCaptureSession object and add the input of the video capture device. The AVCaptureSession object is used to coordinate the flow of data from the video input device to outputs.

In this case, the output of the session is set to an AVCaptureMetaDataOutput object. The AVCaptureMetaDataOutput class is the core part of QR code reading. This class in combination with theAVCaptureMetadataOutputObjectsDelegate protocol is used to intercept any metadata found in the input device (that is the QR code captured by the device’s camera) and translate it to a human readable format. Don’t worry if something sounds weird or if you don’t totally understand right now. Everything will become clear in a while. For now, continue to add the following lines of code in the viewDidLoad method:

Next, proceed to add the below lines of code. We set self as the delegate of the captureMetadataOutput object. This is the reason why the QRReaderViewController class adopts the AVCaptureMetadataOutputObjectsDelegate protocol. When new metadata objects are captured, they are forwarded to the delegate object for further processing. Here we also need to specify the dispatch queue on which to execute the delegate’s methods. According to Apple’s documentation, the queue must be a serial queue. So we simply use dispatch_get_main_queue() function to get the default serial queue.

The metadataObjectTypes property is also quite important, as this is the point where we tell the app what kind of metadata we are interested in. The AVMetadataObjectTypeQRCode clearly indicates our purpose.

Now that we have set and configured a AVCaptureMetadataOutput object, we need to display the video captured by the device’s camera on screen. This can be done using a AVCaptureVideoPreviewLayer, which actually is a CALayer. You use this preview layer in conjunction with an AV capture session to display video and the preview layer is added as a sublayer of the current view. Here is the code:

Finally, we start the video capture by calling the startRunning method of the capture session:

If you compile and run the app, it should start capturing video when launched. But wait, it seems the message label is hidden.

You can fix it by adding the following line of code:

Re-run the app after changes. The message label “No QR code is detected” should now appear on screen.

Implementing QR Code Reading

As of now the app looks pretty much like a video capture app. So how can it scan QR code and translate the code into something meaningful? The app itself is already capable to detect QR code. We just don’t aware of that, however. Here are what we are going to tweak the app:

1. When a QR code is detected, the app will highlight the code using a green box.
2. The QR code will be decoded and the decoded information will be displayed at the bottom of the screen.

Initializing the Green Box

In order to highlight the QR code, we’ll first create an UIView object and set its border to green. Add the following code in the viewDidLoad method:

The UIView is invisible on screen as the size of the qrCodeFrameView object is set to zero by default. Later, when a QR code is detected, we will change its size and turn it into a green box.

Decoding the QR Code

As mentioned earlier, when the AVCaptureMetadataOutput object recognizes a QR code, the following delegate method of AVCaptureMetadataOutputObjectsDelegate will be called:

So far we haven’t implemented the method and this is why the app can’t translate the QR code. In order to capture the QR code and decode the information, we need to implement the method to perform additional processing on metadata objects. Here is the code:

The second parameter (i.e. metadataObjects) of the method is an Array object, which contains all the metadata objects have been read. The very first thing is to make sure that this array is not nil and of course, if it contains even one object. Otherwise, we reset the size of qrCodeFrameView to zero and set the messageLabel to its default message.

If a metadata object is found, we check to see if it is a QR code. If that’s the case, we’ll proceed to find out the bounds of the QR code.

These couple lines of code are used to set up the green box for highlighting the QR code. By calling the transformedMetadataObjectForMetadataObject method of viewPreviewLayer, the metadata object’s visual properties is converted to layer coordinates. From that, we can find out the bounds of QR code for constructing the green box.

Lastly, we decode the QR code into human readable information. It’s fairly simple. The decoded information can be accessed by using the stringValue property of an AVMetadataMachineReadableCodeObject.

Now you’re ready to go! Hit the Run button to compile and run the app on a real device. Once launched, point it to a QR code like the one on your left. The app immediately detects the code and decode the information.

qrcode-demo-app-final
Tip: You can generate your own QR code. Simply access http://www.qrcode-monkey.com to create one.

Summary

With AVFoundation framework, creating a QR code reader app has never been so easy. Other than QR code, the AVFoundation framework supports various barcodes such as Code 39, Code 128, Aztec and PDF417. Try to tweak the existing Xcode project and enable the demo to scan other types of barcode.

For your reference, you can download the complete Xcode project from here.

If you like this tutorial, you’ll probably like our new Swift programming book. Check it out to learn more.

iOS
Introducing Firebase with Swift 3: Login and Sign Up
iOS
Using App Groups for communication between macOS/iOS apps from the Same Vendor
iOS
An Introduction to AR Quick Look in iOS 12
  • Chris Kong

    Chris KongChris Kong

    Author Reply

    can you post the swift code version for this tutorial? The zip file has the old version.


    • Simon Ng

      Simon NgSimon Ng

      Author Reply

      Please try again. The download link is now fixed.


  • Isuru Nanayakkara

    Thanks for the tutorial. The square around the QR code is a nice addition to the old version. By the way, it’ll be nice if you guys could get syntax highlighting for the code samples in the tutorials.


  • joe

    joejoe

    Author Reply

    Hey I was wondering… What do I do when I want the app to open the link in safari
    … I want to use a button instead of a label but I don’t know how to make it work.


    • EntreGuy

      EntreGuyEntreGuy

      Author Reply

      Did you ever get this to work? I’ve added a button and a sequel to go to another view controller but I can’t get the button to show up.


  • Jorge Valenzuela

    Hello, ill start off by saying that a new programming student.

    With that said, I would appreaciate help on how to add the other supported barcode to the app (Code 39, Code 128, Aztec, PDF417)

    Any help, hints, or leads will be of much help.

    Thank you for the well explained tutorial.


    • Kristofer Doman

      You just pass in more metadataObjectTypes to your AVCaptureMetadataOutput Object.

      So, for instance, I didn’t want QR Codes, so my code looks like this: captureMetadataOutput.metadataObjectTypes = [AVMetadataObjectTypeCode128Code,
      AVMetadataObjectTypeCode39Code, AVMetadataObjectTypeEAN13Code]

      Then, you just need to make sure in your if statement inside the function captureOutput that it’s checking metaDataObj.type == AVMetadataObjectTypeCode128Code etc. You do this with an || (or) seperator.

      So if metaDataObj.type == something || metaDataObj.type = somethingElse { }


  • Alfred

    AlfredAlfred

    Author Reply

    How to fix landscape view?


    • Allen

      AllenAllen

      Author Reply

      Hey that make me for 5 hours creepy. But I am have the solution. Replace this code and everything works fine. This codeine make the landscape or portrait or landscape right “AVCaptureVideoOrientation.LandscapeLeft”

      videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)

      videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill

      videoPreviewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft

      videoPreviewLayer?.frame = view.layer.bounds

      view.layer.addSublayer(videoPreviewLayer!)

      captureSession?.startRunning()


      • Allen

        AllenAllen

        Author Reply

        This code is not bad and now it is perfect for every rotation similar photo app from apple.

        override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {

        switch UIDevice.currentDevice().orientation{

        case .Portrait:

        print(“Portrait”)

        videoPreviewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.Portrait

        case .PortraitUpsideDown:

        print(“PortraitUpsideDown”)

        videoPreviewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.PortraitUpsideDown

        case .LandscapeLeft:

        print(“LandscapeLeft”)

        videoPreviewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.LandscapeRight

        case .LandscapeRight:

        print(“LandscapeRight”)

        videoPreviewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft

        default:

        // print(“Another”)

        initialisierer()

        }

        }


  • Jaimin

    JaiminJaimin

    Author Reply

    Its dumb to ask, but I’ll anyway go ahead and ask – Is there any way to enable the camera when running in xcode? or I’ll need to use phone or other device as simulator? When I run this code, all I get is white screen with ‘No QR Code is detected.’


    • Jorge Valenzuela

      I believe the only way to access the camera in the simulator is with an actual device.


      • Jaimin

        JaiminJaimin

        Author Reply

        Thanks. I did register and was able to run on my phone. Thanks.


  • MarieDM

    MarieDMMarieDM

    Author Reply

    Is it possible to read a QR code from an image? With AVFoundation or something else? (Except with Zbar SDK). I tried to do the same way the tuto did and add an image view or an image in the layer (of videoPreviewLayer) but it won’t work.


  • beatbears

    beatbearsbeatbears

    Author Reply

    What is the best way to tear down an AVCaptureSession after it has been run? I’m working on a project where I’d like to scan, see the results, then start a second scan. My app crashes whenever I try to relaunch the AVCaptureSession. I close the previous session with Session.stopRunning(), but this does not seem sufficient.


  • Brice Amram

    Hi. Until I’ve got more questions to go a bit further , I just wanted to say a HUGE THANKS for this neat tutorial. It’s clear, concise, and the result works like a charm ! Big up!. Brice


  • Martin

    MartinMartin

    Author Reply

    Great tutorial BUT it gives me an error on
    let input: AnyObject! = AVCaptureDeviceInput.deviceInputWithDevice(captureDevice, error: &error)
    error is: Extra argument ‘error’ in call


    • Joe

      JoeJoe

      Author Reply

      Hi, I get this error too!


    • Joe

      JoeJoe

      Author Reply

      I think it is because of Xcode 7 beta and Swift 2.0. You are using that, right?


      • 俨 小良

        俨 小良俨 小良

        Author Reply

        Yes, but how to resolve this problem?


        • Ambrose

          AmbroseAmbrose

          Author Reply

          Use try catch block feature of swift 2.0. and print the error in catch block.It will beresolved 🙂


          • Richard Willis

            Like this:

            let input: AnyObject?

            do {
            let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType( AVMediaTypeVideo )
            input = try AVCaptureDeviceInput.init( device: captureDevice )
            } catch {
            if let error = error as NSError?
            {
            print( “”, error.code, error.domain, error.localizedDescription )
            }
            return
            }

            if let input = input as! AVCaptureInput? {
            // tutorial should be fine from this point…
            }


          • 俨 小良

            俨 小良俨 小良

            Author

            Thanks a lot.


          • 俨 小良

            俨 小良俨 小良

            Author

            OK, I have fix it. Thanks.


    • OneCar Wood

      I get the same error that starts there I think. I’m still using v.6.2.


  • Andrew W. Donoho

    All of your example code is listed with a Copyright for AppCoda. All Rights Reserved. Do you have a more permissive license aligned to the spirit of access you provide in your tutorial? I rarely copy code directly. As there seems to be so little source code required to capture a QR code, it seems quite likely that I could be accused of copyright infringement. Any license except GPL or other source revealing licenses is likely to be acceptable.


  • Burak Akkaş

    Thanks for tutorial.

    I updated the code for Xcode 7 & Swift 2.0

    Here it is: http://pastebin.com/3V4btFBE


    • Seb Hareng

      Seb HarengSeb Hareng

      Author Reply

      Thanks for doing and sharing this Burak. Very helpful!


  • Mark Lucking

    Just tried this code on IOS 9.0; two small changes needed… Xcode 7.0 did one of them. I needed to do the other…

    // input = try AVCaptureDeviceInput.deviceInputWithDevice(captureDevice)
    input = try AVCaptureDeviceInput(device: captureDevice)

    Change the first line to the second… and it than worked perfectly!! Thank you AppCoda!! Great Post


  • Chuck

    ChuckChuck

    Author Reply

    Is anyone having an issue with the following error being thrown? “This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes.” It appears to be coming from this line? captureMetadataOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())?


  • Alain

    AlainAlain

    Author Reply

    where is the link for download xcode project??


  • Alain

    AlainAlain

    Author Reply

    I have this error with :
    // Set the input device on the capture session.
    captureSession?.addInput(input as AVCaptureInput)


  • Steven Tan

    Steven TanSteven Tan

    Author Reply

    1. When a QR code is detected, the app will highlight the code using a green box.

    Hi, i have a issue. My camera can’t detect the QRCode, and doesn’t show the green box.
    i’ve follow your tutorial. but it doesn’t work only on that part.
    I’m using swift 2.0 on iOS 9.1.
    Any help will be great.
    thankyou


  • Steven Tan

    Steven TanSteven Tan

    Author Reply

    It’s a great tutorial, but is there any chance that after scan QRCode auto direct to website ?
    thanks !


    • zuyao

      zuyaozuyao

      Author Reply

      UIApplication.sharedApplication().openURL(NSURL(string: “https://www.google.com.sg”)!)


      • agantengik

        agantengikagantengik

        Author Reply

        could u explain more? what if i want to scan the qr code with url, example: http://abc[dot]com) then automatically direct to that url. thanks for your response..


        • zuyao

          zuyaozuyao

          Author Reply

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

          // Check if the metadataObjects array is not nil and it contains at least one object.

          if metadataObjects == nil || metadataObjects.count == 0 {

          qrCodeFrameView?.frame = CGRectZero

          messageLabel.text = “No QR code is detected”

          return

          }

          // Get the metadata object.

          let metadataObj = metadataObjects[0] as AVMetadataMachineReadableCodeObject

          if metadataObj.type == AVMetadataObjectTypeQRCode {

          // If the found metadata is equal to the QR code metadata then update the status label’s text and set the bounds

          let barCodeObject = videoPreviewLayer?.transformedMetadataObjectForMetadataObject(metadataObj as AVMetadataMachineReadableCodeObject) as AVMetadataMachineReadableCodeObject

          qrCodeFrameView?.frame = barCodeObject.bounds;

          if metadataObj.stringValue != nil {

          messageLabel.text = metadataObj.stringValue
          //if the result is url…
          UIApplication.sharedApplication().openURL(NSURL(string: metadataObj.stringValue)!)
          }

          }

          }


  • Barry

    BarryBarry

    Author Reply

    Any tips on how to trigger the scan action on button click?

    I have added a global

    var scanEnabled: Bool = false

    and

    func buttonScanAction(){
    print(“Scan”)
    scanEnabled = true
    self.cameraLayerWithBorder() // this is my function with the camera view
    }


    • zuyao

      zuyaozuyao

      Author Reply

      try google on IBAction…. it tie the button click to an function.


  • zuyao

    zuyaozuyao

    Author Reply

    any more website which teach / sample apps tutorial? thank!


  • Anirudh R Huilgol

    Hello guys I have just started to learn swift language aim trying to implement the above code and i am getting this error i looked for the solutions but i did not understand them

    here below i have screenshot of my Xcode please Some one help me out with the fix for this error


    • zuyao

      zuyaozuyao

      Author Reply

      var input: AnyObject!

      do {

      input = try AVCaptureDeviceInput(device: captureDevice)

      } catch let err as NSError {

      print(err)

      }


  • zuyao

    zuyaozuyao

    Author Reply

    hi….. the scanner built by using AVFOUNDATION cannot read the barcode of the left… (distorted)
    the right one with the thicker line is original image.

    **Some of the scanner manage to scan the left image….. anything i can do/ tweak so that i can scan by using AVFOUNDATION?


  • agantengik

    agantengikagantengik

    Author Reply

    could u explain more? what if i want to scan the QRCode with url, example: http://abc[dot]com) then automatically direct to that url. thanks for your response..


  • Ayhan Ayhan

    hi, how i can store the qr/barcode on my device maybe in a list?
    best regards


  • chad

    chadchad

    Author Reply

    After a successful scan of QR code with a URL, any suggestions on how you would stop scanning and perform a segue to a new view controller and display the URL in a webview?


  • Diego Garcia

    I’m getting a memory warning, sometimes even before I detect the QRCode, what can I do?


  • Jordan Holmer

    I tried this tutorial with a device running iOS 10.0.1 and it does not appear to be working properly. I even tried your completed Xcode project. The camera shows up but no green box or QR detection ever happens.


  • ppear

    ppearppear

    Author Reply

    why i still on lunch page, nothing appear ???


    • ppear

      ppearppear

      Author Reply

      it said

      2016-10-11 22:08:27.034847 QRReaderDemo[9775:2754269] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles

      2016-10-11 22:08:27.036230 QRReaderDemo[9775:2754269] [MC] Reading from public effective user settings.

      2016-10-11 22:08:27.069432 QRReaderDemo[9775:2754305] [access] This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.

      how can i fix this?


  • 徐振庭

    徐振庭徐振庭

    Author Reply

    Not sure if this post is still being maintained, but I guess I’ll give it a shot. Basically, if I want the QR reader to perform a segue after it reads something, it somehow gets executed “twice”. A more detailed description of my issues is posted in StackOverflow from this link: http://stackoverflow.com/questions/42550348/ios-qr-code-scanner-triggering-actions-multiple-times

    Greatly appreciate it if anyone provide some assistance


  • Roman Nikitiuk

    Thanks


  • Vusala

    VusalaVusala

    Author Reply

    Very nice explanation,thank you !


  • Jacques

    JacquesJacques

    Author Reply

    Please update code for swift4. Thanks

    // Check if the metadataObjects array is not nil and it contains at least one object.
    if metadataObjects == nil || metadataObjects.count == 0 {Comparing non-optional value of type ‘[AVMetadataObject]’ to nil always returns false


  • Mitesh

    MiteshMitesh

    Author Reply

    It is continuously increasing memory each time when i get back to this scan view controller. I have 2 view controllers, on is this scan view controller and after scan i am navigating to second view controller but when i get back to scan view controller then each time it increase memory. I tried with stop capture session but nothing happen to memory. Any suggestion ?


  • Mitesh

    MiteshMitesh

    Author Reply

    It is continuously increasing memory each time when i get back to this scan view controller. I have 2 view controllers, one is this scan view controller and after scan i am navigating to second view controller but when i again get back to this scan view controller then each time it increase memory. I tried with stop capture session but nothing happen to memory. Any suggestion ?


  • Bernard

    BernardBernard

    Author Reply

    Thank you for this tutorial.
    How can open the email app and generate a pre-formated email email from a QRCode ?
    I test it on a QRcode coding a “mailto….” it doesn’t work
    Thanks


Leave a Reply to 俨 小良
Cancel Reply

Shares