Tutorial

Building a Barcode and QR Code Reader in Swift 4 and Xcode 9


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 above image – that’s a QR code.

QR (short for Quick Response) code is a kind of two-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 the URL of a landing page or marketing information. Unlike the basic barcode that you’re familiar with, a QR code contains information in both the horizontal and vertical direction. Thus this contributes to its capability of storing a larger amount of data in both numeric and letter form. I don’t want to go into the technical details of the QR code here. If you’re interested in learning more, you can check out the official website of QR code.

Editor’s note: This is the sample chapter of our Intermediate iOS Programming with Swift book.

With the rising prevalence of iPhone and Android phones, the use of QR codes has been increased dramatically. In some countries, QR codes can be found nearly everywhere. They appear in magazines, newspapers, advertisements, billboards, name cards and even food menu. As an iOS developer, you may wonder how you can empower your app to read a QR code. Prior to iOS 7, you had to rely on third-party libraries to implement the scanning feature. Now, you can use the built-in AVFoundation framework to discover and read barcodes in real-time.

Creating an app for scanning and translating QR codes has never been so easy.

Quick tip: You can generate your own QR code. Simply go to http://www.qrcode-monkey.com
Note: This tutorial has been updated for Swift 4 and Xcode 9.

Creating a QR Code Reader App

The demo app that we’re going to build is fairly simple and straightforward. Before we proceed to build the demo app, however, it’s important to understand that any barcode scanning in iOS, including QR code scanning, is totally based on video capture. 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. The app works pretty much like a video capturing app but without the 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.

QR Reader App Demo

It’s that simple.

To build the app, you can start by downloading the project template from here. I have pre-built the storyboard and linked up the message label for you. The main screen is associated with the QRCodeViewController class, while the scanner screen is associated with the QRScannerController class.

QR Reader Starter Project

You can run the starter project to have a look. After launching the app, you can tap the scan button to bring up the scan view. Later we will implement this view controller for QR code scanning.

Now that you understand how the starter project works, let’s get started and develop the QR scanning feature in the app.

Import AVFoundation Framework

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

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

Later, we need to implement the AVCaptureMetadataOutputObjectsDelegate protocol. We’ll talk about that in a while. For now, adopt the protocol with an extension:

Before moving on, declare the following variables in the QRScannerController 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:

  1. Look up the back camera device.
  2. Set the input of the AVCaptureSession object to the appropriate AVCaptureDevice for video capturing.

Insert the following code in the viewDidLoad method of the QRScannerController class:

Assuming you’ve read the previous chapter, you should know that the AVCaptureDevice.DiscoverySession class is designed to find all available capture devices matching a specific device type. In the code above, we specify to retrieve the device that supports the media type AVMediaType.video.

To perform a real-time capture, we use the 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 our output.

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 the AVCaptureMetadataOutputObjectsDelegate protocol, is used to intercept any metadata found in the input device (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 it right now – everything will become clear in a while. For now, continue to add the following lines of code in the do block of the viewDidLoad method:

Next, proceed to add the lines of code shown below. 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. In the above code, we specify the dispatch queue on which to execute the delegate’s methods. A dispatch queue can be either serial or concurrent. According to Apple’s documentation, the queue must be a serial queue. So, we use DispatchQueue.main 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 AVMetadataObject.ObjectType.qr clearly indicates our purpose. We want to do QR code scanning.

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

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

If you compile and run the app on a real iOS device, it crashes unexpectedly with the following error when you tap the scan button:

Similar to what we have done in the audio recording chapter, iOS requires app developers to obtain the user’s permission before allowing to access the camera. To do so, you have to add a key named NSCameraUsageDescription in the Info.plist file. Open the file and right-click any blank area to add a new row. Set the key to Privacy – Camera Usage Description, and value to We need to access your camera for scanning QR code.

Info.plist for Camera Access

Once you finish the editing, deploy the app and run it on a real device again. Tapping the scan button should bring up the built-in camera and start capturing video. However, at this point the message label and the top bar are hidden. You can fix it by adding the following line of code. This will move the message label and top bar to appear on top of the video layer.

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

Implementing QR Code Reading

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

  • When a QR code is detected, the app will highlight the code using a green box
  • 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 a UIView object and set its border to green. Add the following code in the do block of the viewDidLoad method:

The qrCodeFrameView variable is invisible on screen because the size of the UIView 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; 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 that have been read. The very first thing we need to do is make sure that this array is not nil, and it contains at least one object. Otherwise, we reset the size of qrCodeFrameView to zero and set 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 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 transformedMetadataObject(for:) method of viewPreviewLayer, the metadata object’s visual properties are converted to layer coordinates. From that, we can find the bounds of the QR code for constructing the green box.

Lastly, we decode the QR code into human-readable information. This step should be fairly simple. The decoded information can be accessed by using the stringValue property of an AVMetadataMachineReadableCode object.

Now you’re ready to go! Hit the Run button to compile and run the app on a real device.

Sample QR Code

Once launched, tap the scan button and then point the device to the QR code in figure 11.4. The app immediately detects the code and decodes the information.

qrcode-reader-5

Your Exercise – Barcode Reader

The demo app is currently capable of scanning a QR code. Wouldn’t it be great if you could turn it into a general barcode reader? Other than the QR code, the AVFoundation framework supports the following types of barcodes:

  • UPC-E (AVMetadataObject.ObjectType.upce)
  • Code 39 (AVMetadataObject.ObjectType.code39)
  • Code 39 mod 43 (AVMetadataObject.ObjectType.code39Mod43)
  • Code 93 (AVMetadataObject.ObjectType.code93)
  • Code 128 (AVMetadataObject.ObjectType.code128)
  • EAN-8 (AVMetadataObject.ObjectType.ean8)
  • EAN-13 (AVMetadataObject.ObjectType.ean13)
  • Aztec (AVMetadataObject.ObjectType.aztec)
  • PDF417 (AVMetadataObject.ObjectType.pdf417)
  • ITF14 (AVMetadataObject.ObjectType.itf14)
  • Interleaved 2 of 5 codes (AVMetadataObject.ObjectType.interleaved2of5)
  • Data Matrix (AVMetadataObject.ObjectType.dataMatrix)
Scanning a barcode

Your task is to tweak the existing Xcode project and enable the demo to scan other types of barcodes. You’ll need to instruct captureMetadataOutput to identify an array of barcode types rather than just QR codes.

I’ll leave it for you to figure out the solution. While I include the solution in the Xcode project below, I encourage you to try to sort out the problem on your own before moving on. It’s gonna be fun and this is the best way to really understand how the code operates.

If you’ve given it your best shot and are still stumped, you can download the solution on GitHub.

Love this tutorial? You may love our Intermediate iOS Programming with Swift book, which discusses more iOS APIs.
Tutorial
Integrating Google ML Kit in iOS for Face Detection, Text Recognition and Many More
Tutorial
Working with Drag and Drop APIs in iOS 11
macOS
Implementing Drag And Drop Operations Using NSPasteboard on macOS
  • Scott Hodson

    Good tutorial. Question though; is there any way to make the qrCodeFrameView show as a box around an EAN Barcode rather than just a line?


    • Stefan

      StefanStefan

      Author Reply

      if it would recognize the ean-13, I would already be happy.


      • Scott Hodson

        You just need to pass AVMetadataObjectTypeEAN13Code to captureMetadataOutput.metadataObjectTypes e.g. captureMetadataOutput.metadataObjectTypes = [AVMetadataObjectTypeEAN13Code]


  • Jeremy Soles

    This doesnt work when put inside of a module and called form Objective-C. The video capture view shows but never recognizes the QR.

    [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles


  • zeemyself

    zeemyselfzeemyself

    Author Reply

    Camera Upside down on landscrape


    • zeemyself

      zeemyselfzeemyself

      Author Reply

      and when device transition between landscape, portrait it has white space in camera input


  • Sporteolgy

    SporteolgySporteolgy

    Author Reply

    Today, other mobile operating systems like Symbian and Windows are far from being anywhere near Android or iOS in the number of users or in terms of quality and features. Now the question arises that out of iOS and Android which is the best mobile OS. Well, if we look at the best iOS 10 features full list and the best features list of Android, we come to realize that both have many similarities. But as you would expect there are many features among the complete list of best iOS 10 features that are not available in Android. And similarly there are many features among the best 10 android features that are not available in iOS. However, there seems to be a general perception that the iOS 10 is much more user friendly than Android. According to 10 websites of best iOS 10 features, one main thing that differentiates iOS from Android is the fact that iOS is simply made for one smartphone, that is iPhone. http://bit.ly/2f9xdIF … And Android is an open source mobile operating system, that is made for different smartphones. So the user experience of Android on different smartphones is quite different. On the other hand, as iOS is tailor-made for one particular smartphone, it provides an excellent user experience that feels very natural. Now, you might be thinking, that what are the best features in the list of best iOS 10 features and uses. So the Apple’s latest operating system iOS’s 10 best iOS 10 features include highly smart personal assistant Siri, predictive typing in messages, smart maps app, and rich notifications.


  • AshBox

    AshBoxAshBox

    Author Reply

    Though there is certain online tools are available for QR Code generate but making app of QR Code is such an excellent idea. You presented great information. Detailed & Step by step coding is really helpful for all app beginners. Thanks for sharing.


  • coder9711

    coder9711coder9711

    Author Reply

    How to use the url of the Qr code to go directly on the website ?


  • Leo

    LeoLeo

    Author Reply

    Hi. Great Tutorial. I would like to go directly to the the website in a a new scene with a WebView Controller. Thanks


  • bella

    bellabella

    Author Reply

    excuse me . i got some trouble . i followed your code but when i simulated that on my iphone i just saw black screen .


    • Simon Ng

      Simon NgSimon Ng

      Author Reply

      Have you tried to download the sample project and see if it works for you?


  • Joseph Silmon-Clyde

    Hi, great tutorial – I have been using an earlier version of this code for a while now, and it has worked brilliantly.

    But I have come across a strange problem: after 70 or so scans, the reader stops working. The viewDidLoad completes, but the QR code is not recognised. The only way to get it going again is to close down the app and reopen. It seems like something is persisting in memory and eventually preventing the scanner from working. The app doesn’t crash.

    Any ideas on what might be causing this?

    Thanks


  • Andri Ari Pratama

    Hi Simon, great tutorial! I just replace the code with switch to another present view if qr code recognize. When I’m see the log, it’s still running even I’m not in the scanner view. Any advice how to stop reading?

    self.present()


    • Andi

      AndiAndi

      Author Reply

      Try this:
      captureSession?.stopRunning()


  • Hai Vien

    Hai VienHai Vien

    Author Reply

    This is an excellent article! I have just subscribed to your blog.
    Thanks for sharing your knowledge Simon!


  • hgh

    hghhgh

    Author Reply

    added the project code and ran the app everything is fine but the cam is not opening after the i gave it access permission, any ideas?


  • alyasi 2002

    Hi, can you please tell me how can I bring a list from storage using search tool


  • alyasi 2002

    Hi, can you please tell me how can I bring a list from storage using search tool


  • Kenny

    KennyKenny

    Author Reply

    Hi Simon, awesome tutorial. just one question. I’m trying to scan EAN13 Code plus add-on (so 13 digits + 2 or 13 + 5 digits).
    Any idea how I can modify the scanner?


    • Ethan

      EthanEthan

      Author Reply

      This is not possible with Apple’s current API. File a bug and request it +5 scanning. I have done the same.


  • Bojan Ursus

    Thanks for great tutorial!


  • Ronald Hofmann

    https://uploads.disquscdn.com/images/48b285ee3aca4da0583daa6cec10ffaa07663f142ec8f75d2dc96ed03f8e95d9.png https://uploads.disquscdn.com/images/ab9775b724700d9e4c5f1d2ff8956cb73839cc6e417ab5fab939b31b80ab8962.png This is a great tutorial.
    But I still got a problem. When I boot the app in potrait e.g. things are alright. When I tilt the iPhone the camera also rotates 45° and the view is only covering half the screen-See pictures.

    Any idea whats wrong?

    Greetings from Switzerland, Ronald


    • Marius

      MariusMarius

      Author Reply

      Hi, you may need to adjust the constraints. It should be the right constraint which you’re missing. And this should answer your question: “What are constraints?”: https://www.youtube.com/watch?v=pwkpyzn7EOM


      • Marius

        MariusMarius

        Author Reply

        *EDIT: “…the constraint on the right side…”


  • Sandesh Sardar

    why it is taking QR barcode only? why not other? is there any setting made for tutorial?


  • markith

    markithmarkith

    Author Reply

    In Swift 4 this line is no longer working:
    let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)

    the closest I could find is:
    let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)

    unfortunately this now prevents this func from being called (as far as I can tell):
    func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)

    I tried reading the Apple Documentation and Googling for the solution, but with Swift 4 being so new I’m having really hard time finding a solution.


    • Facu Alegre

      Make sure you update the delegate function signature. I used this same tutorial a year ago and have the same code. The Swift 4 migrator changed everything well except the function signature (i.e. the argument labels).

      With Swift 3, it was “func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)” but with Swift 4 it’s “func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)”. The change is the first argument, it went from captureOutput to _ output.

      That did the trick for me, after 30 minutes of going through the documentation.


  • dilip

    dilipdilip

    Author Reply

    @simonng:disqus where to display alert if view loads and customer denies camera in the above code.Code is working fine their is no issue with that


  • Derek Yap

    Derek YapDerek Yap

    Author Reply

    Thank you for this tutorial! It works on my iPhone 8, and this code is much simpler than other QR reading examples that I’ve found online.

    Question for you: Can you offer any suggestions on how I can append the program so that a pop up view appears when a supported code is detected? I’m new to Swift and I’m not sure where I would add this or a good way to do it. Any hints or tips would be greatly appreciated. Thanks!


  • Francesco Mariotti

    Hi Simon, wonderful tutorial! I Checked the code many times but I don’t know why it continuously tell me that it failed to find camera device even if i made the change in the info.plist


    • anup gupta

      anup guptaanup gupta

      Author Reply

      same issue with me too


      • Philip Cheung

        If you’re using iPhone 6 you need to switch the camera type to be .builtInWideAngleCamera

        let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back)

        in QRScannerController.swift. Iphone 6 (or earlier) doesn’t have the fancy dual cameras 🙂


        • Comment Guy

          Try this to take into account wide or dual angle camera

          var deviceDiscoverySession: AVCaptureDevice.DiscoverySession

          if AVCaptureDevice.default(.builtInDualCamera, for: AVMediaType.video, position: .back) != nil {

          deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera], mediaType: AVMediaType.video, position: .back)

          } else {

          deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back)

          }


  • Michal Mynarski

    Hi Simon!
    You have issue in tutorial.

    Capture session is defined as a optional meanwhile in codebase you have initialization in this line.
    Going through your tut users gonna get ugly crash at seeing metadata type because capturesession (and it’s input) will be nil 🙂


    • Comment Guy

      Ran into the same problem. Need to initialize the captureSession object. Place the follow code

      captureSession = AVCaptureSession()

      above

      captureSession?.addInput(input)


  • James

    JamesJames

    Author Reply

    Hi Simon,

    My apologies for my previous post. The sample codes did not work initially for no reasons, but it work perfectly well now.

    Thank you for the great tutorial.


  • Malleswari

    MalleswariMalleswari

    Author Reply

    How to add gallery button on camerscreen to select QRCode from gallery like in paytm


  • Ankur Verma

    Shows :
    Failed to get the camera device


  • Digvijay Gida

    How to generate EAN-8 (AVMetadataObject.ObjectType.ean8), Code 39 (AVMetadataObject.ObjectType.code39) etc..?


Leave a Reply to Ronald Hofmann
Cancel Reply

Shares