swift - How can I scan barcodes on iOS?

ID : 20315

viewed : 15

Tags : iosswiftiphonecocoa-touchbarcodeios

Top 5 Answer for swift - How can I scan barcodes on iOS?

vote vote

100

We produced the 'Barcodes' application for the iPhone. It can decode QR Codes. The source code is available from the zxing project; specifically, you want to take a look at the iPhone client and the partial C++ port of the core library. The port is a little old, from circa the 0.9 release of the Java code, but should still work reasonably well.

If you need to scan other formats, like 1D formats, you could continue the port of the Java code within this project to C++.

EDIT: Barcodes and the iphone code in the project were retired around the start of 2014.

vote vote

84

As with the release of iOS7 you no longer need to use an external framework or library. The iOS ecosystem with AVFoundation now fully supports scanning almost every code from QR over EAN to UPC.

Just have a look at the Tech Note and the AVFoundation programming guide. AVMetadataObjectTypeQRCode is your friend.

Here is a nice tutorial which shows it step by step: iPhone QR code scan library iOS7

Just a little example on how to set it up:

#pragma mark - #pragma mark AVFoundationScanSetup  - (void) setupScanner; {     self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];      self.input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];      self.session = [[AVCaptureSession alloc] init];      self.output = [[AVCaptureMetadataOutput alloc] init];     [self.session addOutput:self.output];     [self.session addInput:self.input];      [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];     self.output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode];      self.preview = [AVCaptureVideoPreviewLayer layerWithSession:self.session];     self.preview.videoGravity = AVLayerVideoGravityResizeAspectFill;     self.preview.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);      AVCaptureConnection *con = self.preview.connection;      con.videoOrientation = AVCaptureVideoOrientationLandscapeLeft;      [self.view.layer insertSublayer:self.preview atIndex:0]; } 
vote vote

71

There are two major libraries:

  • ZXing a library written in Java and then ported to Objective C / C++ (QR code only). And an other port to ObjC has been done, by TheLevelUp: ZXingObjC

  • ZBar an open source software for reading bar codes, C based.

According to my experiments, ZBar is far more accurate and fast than ZXing, at least on iPhone.

vote vote

69

You can find another native iOS solution using Swift 4 and Xcode 9 at below. Native AVFoundation framework used with in this solution.

First part is the a subclass of UIViewController which have related setup and handler functions for AVCaptureSession.

import UIKit import AVFoundation  class BarCodeScannerViewController: UIViewController {      let captureSession = AVCaptureSession()     var videoPreviewLayer: AVCaptureVideoPreviewLayer!     var initialized = false      let barCodeTypes = [AVMetadataObject.ObjectType.upce,                         AVMetadataObject.ObjectType.code39,                         AVMetadataObject.ObjectType.code39Mod43,                         AVMetadataObject.ObjectType.code93,                         AVMetadataObject.ObjectType.code128,                         AVMetadataObject.ObjectType.ean8,                         AVMetadataObject.ObjectType.ean13,                         AVMetadataObject.ObjectType.aztec,                         AVMetadataObject.ObjectType.pdf417,                         AVMetadataObject.ObjectType.itf14,                         AVMetadataObject.ObjectType.dataMatrix,                         AVMetadataObject.ObjectType.interleaved2of5,                         AVMetadataObject.ObjectType.qr]      override func viewDidAppear(_ animated: Bool) {         super.viewDidAppear(animated)         setupCapture()         // set observer for UIApplicationWillEnterForeground, so we know when to start the capture session again         NotificationCenter.default.addObserver(self,                                            selector: #selector(willEnterForeground),                                            name: .UIApplicationWillEnterForeground,                                            object: nil)     }      override func viewWillDisappear(_ animated: Bool) {         super.viewWillDisappear(animated)         // this view is no longer topmost in the app, so we don't need a callback if we return to the app.         NotificationCenter.default.removeObserver(self,                                               name: .UIApplicationWillEnterForeground,                                               object: nil)     }      // This is called when we return from another app to the scanner view     @objc func willEnterForeground() {         setupCapture()     }      func setupCapture() {         var success = false         var accessDenied = false         var accessRequested = false          let authorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)         if authorizationStatus == .notDetermined {             // permission dialog not yet presented, request authorization             accessRequested = true             AVCaptureDevice.requestAccess(for: .video,                                       completionHandler: { (granted:Bool) -> Void in                                           self.setupCapture();             })             return         }         if authorizationStatus == .restricted || authorizationStatus == .denied {             accessDenied = true         }         if initialized {             success = true         } else {             let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,                                                                                         .builtInTelephotoCamera,                                                                                         .builtInDualCamera],                                                                           mediaType: .video,                                                                           position: .unspecified)              if let captureDevice = deviceDiscoverySession.devices.first {                 do {                     let videoInput = try AVCaptureDeviceInput(device: captureDevice)                     captureSession.addInput(videoInput)                     success = true                 } catch {                     NSLog("Cannot construct capture device input")                 }             } else {                 NSLog("Cannot get capture device")             }         }         if success {             DispatchQueue.global().async {                 self.captureSession.startRunning()                 DispatchQueue.main.async {                     let captureMetadataOutput = AVCaptureMetadataOutput()                     self.captureSession.addOutput(captureMetadataOutput)                     let newSerialQueue = DispatchQueue(label: "barCodeScannerQueue") // in iOS 11 you can use main queue                     captureMetadataOutput.setMetadataObjectsDelegate(self, queue: newSerialQueue)                     captureMetadataOutput.metadataObjectTypes = self.barCodeTypes                     self.videoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)                     self.videoPreviewLayer.videoGravity = .resizeAspectFill                     self.videoPreviewLayer.frame = self.view.layer.bounds                     self.view.layer.addSublayer(self.videoPreviewLayer)                 }              }             initialized = true         } else {             // Only show a dialog if we have not just asked the user for permission to use the camera.  Asking permission             // sends its own dialog to th user             if !accessRequested {                 // Generic message if we cannot figure out why we cannot establish a camera session                 var message = "Cannot access camera to scan bar codes"                 #if (arch(i386) || arch(x86_64)) && (!os(macOS))                     message = "You are running on the simulator, which does not hae a camera device.  Try this on a real iOS device."                 #endif                 if accessDenied {                     message = "You have denied this app permission to access to the camera.  Please go to settings and enable camera access permission to be able to scan bar codes"                 }                 let alertPrompt = UIAlertController(title: "Cannot access camera", message: message, preferredStyle: .alert)                 let confirmAction = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in                     self.navigationController?.popViewController(animated: true)                 })                 alertPrompt.addAction(confirmAction)                 self.present(alertPrompt, animated: true, completion: nil)             }         }     }      func handleCapturedOutput(metadataObjects: [AVMetadataObject]) {         if metadataObjects.count == 0 {             return         }          guard let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject else {             return         }          if barCodeTypes.contains(metadataObject.type) {             if let metaDataString = metadataObject.stringValue {                 captureSession.stopRunning()                 displayResult(code: metaDataString)                 return             }         }     }      func displayResult(code: String) {         let alertPrompt = UIAlertController(title: "Bar code detected", message: code, preferredStyle: .alert)         if let url = URL(string: code) {             let confirmAction = UIAlertAction(title: "Launch URL", style: .default, handler: { (action) -> Void in                 UIApplication.shared.open(url, options: [:], completionHandler: { (result) in                     if result {                         NSLog("opened url")                     } else {                         let alertPrompt = UIAlertController(title: "Cannot open url", message: nil, preferredStyle: .alert)                         let confirmAction = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in                         })                         alertPrompt.addAction(confirmAction)                         self.present(alertPrompt, animated: true, completion: {                             self.setupCapture()                         })                     }                 })                     })             alertPrompt.addAction(confirmAction)         }         let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) -> Void in             self.setupCapture()         })         alertPrompt.addAction(cancelAction)         present(alertPrompt, animated: true, completion: nil)     }  } 

Second part is the extension of our UIViewController subclass for AVCaptureMetadataOutputObjectsDelegate where we catch the captured outputs.

extension BarCodeScannerViewController: AVCaptureMetadataOutputObjectsDelegate {      func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {         handleCapturedOutput(metadataObjects: metadataObjects)     }  } 

Update for Swift 4.2

.UIApplicationWillEnterForegroundchanges as UIApplication.willEnterForegroundNotification.

vote vote

50

If support for the iPad 2 or iPod Touch is important for your application, I'd choose a barcode scanner SDK that can decode barcodes in blurry images, such as our Scandit barcode scanner SDK for iOS and Android. Decoding blurry barcode images is also helpful on phones with autofocus cameras because the user does not have to wait for the autofocus to kick in.

Scandit comes with a free community price plan and also has a product API that makes it easy to convert barcode numbers into product names.

(Disclaimer: I'm a co-founder of Scandit)

Top 3 video Explaining swift - How can I scan barcodes on iOS?

Related QUESTION?