Barcode on swift 4

14,793

Solution 1

I figured it out but Apple didn't make it so obvious. The callback function from the delegate AVCaptureMetadataOutputObjectsDelegate has been renamed and the parameter names are different!

So, replace

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)

to

func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)

My view controller is now scanning QR Codes as before after this. It has the same parameters but the first parameter name is different. Change the function and parameter names and build/run.

Hope this helps!

Solution 2

After changing the delegate call back :

From

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)

To

func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)

I need to set all available types for metadataObjectTypes too as below-

output.metadataObjectTypes=output.availableMetadataObjectTypes

Solution 3

After changing your code from:

func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {}

to:

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

everything works again.

Share:
14,793

Related videos on Youtube

Dx_
Author by

Dx_

Updated on December 04, 2020

Comments

  • Dx_
    Dx_ over 3 years

    I'm trying to upgrade mi app to swift 4, but the barcode reader is not working.

    I have isolated the barcode reader code, and still not working. The camera works but it does not detect the barcode.

    The code worked just fine on swift 3 iOS 10.

    This is the complete code

    import AVFoundation
    import UIKit
    
    class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
    var captureSession: AVCaptureSession!
    var previewLayer: AVCaptureVideoPreviewLayer!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        view.backgroundColor = UIColor.black
        captureSession = AVCaptureSession()
    
        let videoCaptureDevice = AVCaptureDevice.default(for: AVMediaType.video)
        let videoInput: AVCaptureDeviceInput
    
        do {
            videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice!)
        } catch {
            return
        }
    
        if (captureSession.canAddInput(videoInput)) {
            captureSession.addInput(videoInput)
        } else {
            failed();
            return;
        }
    
        let metadataOutput = AVCaptureMetadataOutput()
    
        if (captureSession.canAddOutput(metadataOutput)) {
            captureSession.addOutput(metadataOutput)
    
            metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
            metadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.ean8, AVMetadataObject.ObjectType.ean13, AVMetadataObject.ObjectType.pdf417]
        } else {
            failed()
            return
        }
    
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession);
        previewLayer.frame = view.layer.bounds;
        previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill;
        view.layer.addSublayer(previewLayer);
    
        captureSession.startRunning();
    }
    
    func failed() {
        let ac = UIAlertController(title: "Scanning not supported", message: "Your device does not support scanning a code from an item. Please use a device with a camera.", preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "OK", style: .default))
        present(ac, animated: true)
        captureSession = nil
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    
        if (captureSession?.isRunning == false) {
            captureSession.startRunning();
        }
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
    
        if (captureSession?.isRunning == true) {
            captureSession.stopRunning();
        }
    }
    
    func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
        captureSession.stopRunning()
    
        if let metadataObject = metadataObjects.first {
            let readableObject = metadataObject as! AVMetadataMachineReadableCodeObject;
    
            AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
            found(code: readableObject.stringValue!);
        }
    
        dismiss(animated: true)
    }
    
    func found(code: String) {
        print(code)
    }
    
    override var prefersStatusBarHidden: Bool {
        return true
    }
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
    }
    

    I am using iOS 11 on my iPhone, upgraded to beta 9.

    Any idea? Thank you.

    • Mario A Guzman
      Mario A Guzman almost 7 years
      So it's nice to know that this problem isn't just happening to me after updating to iOS 11 and Swift 4 for my project. I have a very basic QR code reader as well in my app using an AVCaptureMetadataOutput object and the AVCaptureMetadataOutputObjectsDelegate delegate. I have verified that everything is constantly and consistently running and not interrupted. I think at this point its time to submit a bug to Apple (both of use should). Only thing that changed were the names of properties/functions in Swift 4 but nothing else. Weird that we're not getting any delegate callbacks.
    • Mario A Guzman
      Mario A Guzman almost 7 years
      Also, looking at your code, you need to create a Serial Queue for your AVCaptureMetadataOutputObjectsDelegate callback. metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main). Instead of using the main queue, create a serial queue as a property in your view controller and use it here rather than the main queue.
    • Mahendra
      Mahendra about 6 years
      Just for reference, can be used third party github.com/mahendragp/MGPBarcodeScanner
  • Dx_
    Dx_ almost 7 years
    That was the solution! The function is different, thank you!
  • Mario A Guzman
    Mario A Guzman almost 7 years
    I submitted an enhancement request to Apple so that Xcode will give developers a heads up if a function changes like that. I spent so long not finding the solution until I got to the API and read it line by line because no way was my code wrong. And It wasn't! That's when I realized the function name was different.
  • Andrew Bennet
    Andrew Bennet almost 7 years
    I additionally had to use a non-Main DispatchQueue when setting the metadataObjectsDelegate, in order to get the callback to occur: output.setMetadataObjectsDelegate(self, queue: DispatchQueue.global(qos: .userInteractive))
  • Mario A Guzman
    Mario A Guzman almost 7 years
    @AndrewBennet Right, I mentioned this to him also in comment attached to his question above. You have to create and pass in a Serial Queue as a param. Define one globally and pass it in.
  • Leon
    Leon over 6 years
    Why did you answer with the same info as Mario?
  • Emil Korngold
    Emil Korngold over 6 years
    Affirmation of correct answer by Mario, please next time simply up-vote the correct answer. Thanks!
  • Jason Moore
    Jason Moore over 6 years
    I can confirm that the updated delegate works on iOS9 and iOS10.
  • mbonness
    mbonness over 6 years
    This answer worked better for me since it is more obvious that the function signature was changed, not just the function name.
  • mbonness
    mbonness over 6 years
    Just renaming the function is insufficient, you also need to change the first parameter name from captureOutput to output for it to be called.
  • Mario A Guzman
    Mario A Guzman over 6 years
    @mbonness - yes, that is shown in my sample code above.
  • mbonness
    mbonness over 6 years
    @MarioAGuzman Got it thanks. It looks like actually the method signature is changed, the first parameter type is different, in case you want to call that out in your answer.
  • btrballin
    btrballin about 5 years
    This answer worked for me over the accepted answer. In addition to changing the function signature, updating the line to output.metadataObjectTypes=output.availableMetadataObjectTyp‌​es was ultimately what allowed my scanner to return a value
  • Ben Butzer
    Ben Butzer over 4 years
    In iOS 13.0 and 13.1 with Xamarin I ran into an issue where I had to explicitly set the bar code types again, as the line of code would crash: output.metadataObjectTypes=output.availableMetadataObjectTyp‌​es
  • Ben Butzer
    Ben Butzer over 4 years
    Add to my comment above, it was only on the iPhone 11 pro, where this happened.