0

This is the barcode scanning tutorial I used in my program, so that you have a lot more context when you read my code: Link

Here is what my program does so far: Essentially, when I scan an item's barcode with my phone, the UIAlert pops up with the barcode ID displayed and a button prompting the user to open the "Results" page. This is all fine and good, but how do I pass that same scanned barcode ID into a label on the Result's page? I have been stuck on this for 2 days now, even though it seems like such an easy task.

Any help is much appreciated <3

Here is my relevant code:

ProductCatalog.plist -> Link to Image

Scanner_ViewController.swift (first View Controller) ->

import UIKit
import AVFoundation

class Scanner_ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate, ScannerDelegate
{
    private var scanner: Scanner?

    override func viewDidLoad()
    {
        super.viewDidLoad()
    
        self.scanner = Scanner(withDelegate: self)
    
        guard let scanner = self.scanner else
        {
            return
        }
    
        scanner.requestCaptureSessionStartRunning()
    }

    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // Mark - AVFoundation delegate methods
    public func metadataOutput(_ output: AVCaptureMetadataOutput,
                               didOutput metadataObjects: [AVMetadataObject],
                               from connection: AVCaptureConnection)
    {
        guard let scanner = self.scanner else
        {
            return
        }
        scanner.metadataOutput(output,
                               didOutput: metadataObjects,
                               from: connection)
    }

    // Mark - Scanner delegate methods
    func cameraView() -> UIView
    {
        return self.view
    } 

    func delegateViewController() -> UIViewController
    {
        return self
    }

    func scanCompleted(withCode code: String)
    {
        print(code)
        showAlert_Success(withTitle: (code))
    }
 
    private func showAlert_Success(withTitle title: String)
    {
        let alertController = UIAlertController(title: title, message: "Product has been successfully scanned", preferredStyle: .alert)

        // programatically segue to the next view controller when the UIAlert pops up
        alertController.addAction(UIAlertAction(title:"Get Results", style: .default, handler:{ action in self.performSegue(withIdentifier: "toAnalysisPage", sender: self) }))
    
        present(alertController, animated: true)
    }
}

Scanner.Swift (accompanies Scanner_ViewController.swift)->

import Foundation
import UIKit
import AVFoundation

protocol ScannerDelegate: class
{
    func cameraView() -> UIView
    func delegateViewController() -> UIViewController
    func scanCompleted(withCode code: String)
}

class Scanner: NSObject
{
    public weak var delegate: ScannerDelegate?
    private var captureSession : AVCaptureSession?

    init(withDelegate delegate: ScannerDelegate)
    {
        self.delegate = delegate
        super.init()
        self.scannerSetup()
    }

    private func scannerSetup()
    {
        guard let captureSession = self.createCaptureSession()
    
        else
        {
            return
        }
    
        self.captureSession = captureSession
    
        guard let delegate = self.delegate
    
        else
        {
            return
        }
    
        let cameraView = delegate.cameraView()
        let previewLayer = self.createPreviewLayer(withCaptureSession: captureSession,
                                                   view: cameraView)
        cameraView.layer.addSublayer(previewLayer)
    }

    private func createCaptureSession() -> AVCaptureSession?
    {
        do
        {
            let captureSession = AVCaptureSession()
            guard let captureDevice = AVCaptureDevice.default(for: .video) else
            {
            return nil
            }
        
            let deviceInput = try AVCaptureDeviceInput(device: captureDevice)
            let metaDataOutput = AVCaptureMetadataOutput()
        
            // add device input
            if captureSession.canAddInput(deviceInput) && captureSession.canAddOutput(metaDataOutput)
            {
                captureSession.addInput(deviceInput)
                captureSession.addOutput(metaDataOutput)
            
                guard let delegate = self.delegate,
                    let viewController = delegate.delegateViewController() as? AVCaptureMetadataOutputObjectsDelegate else
                {
                        return nil
                }
            
                metaDataOutput.setMetadataObjectsDelegate(viewController,
                                                          queue: DispatchQueue.main)
                metaDataOutput.metadataObjectTypes = self.metaObjectTypes()
             
                return captureSession
            }
        }
    
        catch
        {
            // handle error
        }
    
        return nil
    }

    private func createPreviewLayer(withCaptureSession captureSession: AVCaptureSession,
                                    view: UIView) -> AVCaptureVideoPreviewLayer
    {
        let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.frame = view.layer.bounds
        previewLayer.videoGravity = .resizeAspectFill
    
        return previewLayer
    }

    private func metaObjectTypes() -> [AVMetadataObject.ObjectType]
    {
        return [.qr,
                .code128,
                .code39,
                .code39Mod43,
                .code93,
                .ean13,
                .ean8,
                .interleaved2of5,
                .itf14,
                .pdf417,
                .upce
        ]
    }

    public func metadataOutput(_ output: AVCaptureMetadataOutput,
                         didOutput metadataObjects: [AVMetadataObject],
                         from connection: AVCaptureConnection)
    {
        self.requestCaptureSessionStopRunning()
    
        guard let metadataObject = metadataObjects.first,
            let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject,
            let scannedValue = readableObject.stringValue,
            let delegate = self.delegate
    
        else
        {
                return
        }
        
        delegate.scanCompleted(withCode: scannedValue)
    }

    public func requestCaptureSessionStartRunning()
    {
        self.toggleCaptureSessionRunningState()
    }

    public func requestCaptureSessionStopRunning()
    {
        self.toggleCaptureSessionRunningState()
    }

    private func toggleCaptureSessionRunningState()
    {
        guard let captureSession = self.captureSession
    
        else
        {
            return
        }
    
        if !captureSession.isRunning
        {
            captureSession.startRunning()
        }
    
        else
        {
            captureSession.stopRunning()
        }
    }
}

Analysis_ViewController.swift (second view controller) ->

Right now, the forKey: has been hard-coded to item ID 8710908501708 because I have no idea how to actually pass camera-scanned ID's into the second View Controller :/

import UIKit

class Analysis_ViewController: UIViewController
{
    @IBOutlet weak var productTitle: UILabel!

    func getData()
    {
        let path = Bundle.main.path(forResource:"ProductCatalog", ofType: "plist")

        let dict:NSDictionary = NSDictionary(contentsOfFile: path!)!
    
        if (dict.object(forKey: "8710908501708" as Any) != nil)
        {
            if let levelDict:[String : Any] = dict.object(forKey: "8710908501708" as Any) as? [String : Any]
            {
                // use a for loop to iterate through all the keys and values in side the "Levels" dictionary
                for (key, value) in levelDict
                {
                    // if we find a key named whatever we care about, we can print out the value
                    if (key == "name")
                    {
                        productTitle.text = (value as! String)
                    }
                }
            }
        }
    }

    // listing the better options that are safer in comparison to the scanned product image
    override func viewDidLoad()
    {
        super.viewDidLoad()

        getData()
    }
}
chnken
  • 3
  • 1

1 Answers1

0

Do you have a variable to hold the scanned ID in your view controllers? If not, you can add var itemID: String? to both Scanner_ViewController and Analysis_ViewController.

Then in your func where you get the scanned code, you can set it to the variable.

func scanCompleted(withCode code: String) {
    print(code)
    itemID = code // Saves the scanned code to your var
    showAlert_Success(withTitle: (code))
}

For passing data to another view controller via segue, you might want to look into this UIViewController method for segues: documentation here. This answer also might help.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "toAnalysisPage" {
        if let viewController = segue.destination as? Analysis_ViewController {
            viewController.itemID = itemID    
        }
    }
}
angtlin
  • 192
  • 1
  • 9
  • Worked like an absolute charm, thank you so much. You saved my life <3 <3 <3 I can't express how grateful I am right now. I've been struggling on this for 2 days and turns out I neglected to define something like itemID in both view controllers, not just one. You're awesome, seriously :) – chnken Sep 22 '20 at 05:29
  • Glad it helped. Best of luck in your coding journey, sometimes it's just knowing what to search for to get unstuck (i.e. "passing data in segue" or "passing data to view controller"). Also I think the article you were referencing has non-standard code formatting. Ctrl + I in Xcode should help auto-indent your code the right way. – angtlin Sep 22 '20 at 15:36
  • Ooo I see what you mean, thanks so much for the tip ! :D – chnken Sep 23 '20 at 20:16