Building a Barcode and a QR Code Reader Application for iOS using Swift 4: PT3

This is the third part on building barcode and QRcode reader for iOS using Swift 4. In this article we will add a layer outlining detected barcode. We will also add a sound notification using iOS built in system sound and using our custom sound file. And finally we will navigate to the new ViewController.

Adding rectangle

Let’s start adding an outlining green box around the detected barcode. First we need to define the box itself. We will be using UIView that will have a transparent background and green border. Here’s the sample:

To place the box in the position of the detected Barcode we will use its frame property which takes four parameters: x and y coordinates, width and height. We will get those values from videoPreviewLayer transformed Metadata Object's bounds. transformedMetadataObject(for:) converts a metadata object’s visual properties to layer coordinates.

TParameter that we have to pass is metadataObject. It is the metadata object whose visual properties we want to convert. And in our case it’s the metadata object that we already detected and took the string value out of it. The sample looks like this:

If you take a look at the code that we previously wrote you will notice that we set codeFrame .frame parameter to CGRect.zero. We did this because we didn’t want it to show up anywhere on the screen. Now that we grabbed the coordinates and the size of the detected object we set coordinates and dimensions accordingly for our rectangle.

That’s it! Couple of lines of code and now we are able to outline the detected object. Whole code inside metadataOutput function looks like this:

Playing notification sound

Now it’s time to play some sound as soon as the the code is detected. In this section we will first play Apple’s built in system sound and then we’ll add our custom sound. In my case it will be just a beep sound.

There have been several ways to approach this case - some use AVFoundation and some use AudioToolbox. Well we could use AVFoundation but we only play a very short sound, we don’t need to control it. You might also want to make the device to vibrate. All of this can be simply done by AudioToolbox. All of the code goes into metadataOutput function, right after we display the outlining rectangle.

First you need to import AudioToolbox. We need AudioServicesPlaySystemSound function that plays a short sound (30 seconds or less in duration). Because sound might play for several seconds, this function is executed asynchronously. To know when a sound has finished playing, call the AudioServicesAddSystemSoundCompletion(_:_:_:_:_:) function to register a callback function. Please take a look at the docs for more detailed information.

Apple has assigned numbers to specific notification sounds in the system. Here you can find the list of all system sounds available in iOS: iPhoneDevWiki or iOSSystemSoundsLibrary.

With the first line we assign systemSoundId a number from the list of available sounds and then we play the sound.

NOTE: You need to note that there are two functions that can play system sound. Depending on the particular iOS device, AudioServicesPlayAlertSound function plays a short sound and may invoke vibration. AudioServicesPlaySystemSound will only play system sound without vibration.

Now it’s time to load our custom sound. Simply drag and drop the file into Project Navigator window and mark copy if needed. In my case I created a folder assets and dropped the file in it.

In order to play our sound we first need to create it by using AudioServicesCreateSystemSoundID(_:_:) function. It creates a system sound object. We need to pass two parameters inFileURL that will take the location of our file and outSystemSoundID is the number we have to assign to our sound.

Apple docs say that we need to dispose a system sound object and associated resources after we finished playing it and create again next time. We need to use AudioServicesDisposeSystemSoundID function and pass the ID of system sound object to dispose.

In order to find out when playing the sound has finished we are going to use AudioServicesAddSystemSoundCompletion function. It registers a callback function that is invoked when a specified system sound finishes playing. Take a look at documentation for this function to get a better understanding of the parameters that are being passed.

The completion callback function is a closure. It takes two parameters. We need only one parameter SystemSoundID to pass the ID to AudioServicesDisposeSystemSoundID. We don’t need the other parameter. Hence we place underscore _.

The other use case for this function might be: Because a system sound may play for several seconds, you might want to know when it has finished playing. For example, you may want to wait until a system sound has finished playing before you play another sound.

Now our application detects barcode, outlines it with the green box and plays an alert sound. You might have noticed that though the sound file is only two seconds long it beeps non-stop. This is happening because the capture session is still running. You might want it to stop as soon as you have detected a barcode. At the end of metadataOutput function simply add captureSession?.stopRunning().

Programmatically performing Segue

Now that you have performed all of the detection and alerts in our ScannerViewController it’s time to perform some logic that your application might have. The best way is to navigate to a new view controller where one might add a label to display the code that has been read and add a scan button to the view to trigger scanning again by going back to ScannerViewController.

I have used two methods for navigating to a new view controller. present(detailsViewController, animated: true, completion: nil) and navigationController?.pushViewController(detailsViewController, animated: true).

They both take user from one to another view controller but in two different ways. pushViewConrtoller function adds target view controller into the stack of the navigation view controller.Whereas present puts the target view controller on the top. You can simply just experiment with those two and see the result yourself.

To pass data to the next view controller you simply declare it in the new view controller as the similar type that you are expecting. In our case it’s a string so in the detailsViewController I declared var scannedCode:String? as an optional. In the ScannerViewController I added the code below. It’s pretty self explanatory:

At the end of metadataOutput function, right after captureSession?.stopRunning() simply add displayDetailsViewController(scannedCode: stringCodeValue). I’m sure there’s no need to explain what we have done here.

You can find the whole project on my Github. It includes DetailedViewController.

Conclusion

So that’s it! We have fully working Barcode and QRcode scanning application with sound alerts and outlining rectangle. I hope you found it helpful. In case of any questions, wishes, criticism please don’t hesitate to send me an email and follow me on Twitter. Cheers!