Jake
Jake

Reputation: 861

How to limit number of API calls?

I have an app that reads barcodes. If a barcode is detected, it uses an API to get information about the metadata detected. The problem is that on each scan, it actually reads the barcode 30-40 times, which can be a problem as I am only allowed to make 1000 API calls per hour.

Is there an easy way to 'pause' the scanning or API calls when one is being processed?

override func viewDidLoad() {
    super.viewDidLoad()

  // Get an instance of the AVCaptureDevice class to initialize a device object and provide the video as the media type parameter.
  let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)

  let yellow1 = UIColor(red: 0.427, green: 0.429, blue: 0.144, alpha: 1.0)

  do {
    // Get an instance of the AVCaptureDeviceInput class using the previous device object.
    let input = try AVCaptureDeviceInput(device: captureDevice)

    // Initialize the captureSession object.
    captureSession = AVCaptureSession()

    // Set the input device on the capture session.
    captureSession?.addInput(input)

    // Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
    let captureMetadataOutput = AVCaptureMetadataOutput()
    captureSession?.addOutput(captureMetadataOutput)

    // Set delegate and use the default dispatch queue to execute the call back
    captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
    captureMetadataOutput.metadataObjectTypes = supportedCodeTypes

    // Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
    videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
    videoPreviewLayer?.frame = view.layer.bounds
    view.layer.addSublayer(videoPreviewLayer!)

    // Start video capture.
    captureSession?.startRunning()

    view.bringSubview(toFront: messageLabel)
    view.bringSubview(toFront: leftBracket)
    view.bringSubview(toFront: rightBracket)


    // Initialize QR Code Frame to highlight the QR code
    qrCodeFrameView = UIView()

    if let qrCodeFrameView = qrCodeFrameView {
      qrCodeFrameView.layer.borderColor = yellow1.cgColor
      qrCodeFrameView.layer.borderWidth = 2
      view.addSubview(qrCodeFrameView)
      view.bringSubview(toFront: qrCodeFrameView)
    }

  } catch {

    // If any error occurs, simply print it out and don't continue any more.
    print(error)
    return
  }

    // Do any additional setup after loading the view.
}


func captureOutput(_ captureOutput: AVCaptureOutput!,          didOutputMetadataObjects metadataObjects: [Any]!, from connection:    AVCaptureConnection!) {
    // Check if the metadataObjects array is not nil and it contains at least one object.
    if metadataObjects == nil || metadataObjects.count == 0 {
      qrCodeFrameView?.frame = CGRect.zero
      messageLabel.text = "No barcode detected"
      return
    }

    // Get the metadata object.
    let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject

    if supportedCodeTypes.contains(metadataObj.type) {
      // If the found metadata is equal to the QR code metadata then update the status label's text and set the bounds
      let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
      qrCodeFrameView?.frame = barCodeObject!.bounds

      if metadataObj.stringValue != nil {
        messageLabel.text = metadataObj.stringValue
        barcodeString = (metadataObj.stringValue)
        barcodeString.remove(at: barcodeString.startIndex)
        self.readCode(barcodeString: barcodeString)

      }
    }
}

func readCode(barcodeString: String){

    let string = "https://api.nal.usda.gov/ndb/search/?format=json&q=" + barcodeString + "&sort=n&max=25&offset=0&api_key=NY4LT5Gtc9X4eOOm40UBuSqfaO2eUgcwz20jIQLn"
    let url = URL(string: string)
    URLSession.shared.dataTask(with: url!, completionHandler: {
      (data, response, error) in
      if(error != nil){
        print("object not in database")
      }else{
        do{

          let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String : AnyObject]
          let array = json as NSDictionary

          if array["list"] != nil{

            let list = array["list"] as! [String : AnyObject]
            let item = list["item"] as! NSArray
            let dict = item[0] as! [String : AnyObject]
            let num = dict["ndbno"] as! String
            // Second API call
            self.secondAPICall(number: num)

          }
          else{

            // DO THIS IF ITEM NOT IN DATABASE
            self.itemNotInDatabase()

          }

        }catch let error as NSError{
          print(error)
        }
      }
    }).resume()
}

Upvotes: 1

Views: 340

Answers (2)

Jake
Jake

Reputation: 861

The answer above is correct, I stopped the capture session when a barcode was detected, and this seemed to limit one call per barcode scan.

    if metadataObj.stringValue != nil {
    messageLabel.text = metadataObj.stringValue
    barcodeString = (metadataObj.stringValue)
    barcodeString.remove(at: barcodeString.startIndex)
    self.readCode(barcodeString: barcodeString)
    captureSession?.stopRunning()
    }

Upvotes: 0

Pang Ho Ming
Pang Ho Ming

Reputation: 1319

After you successfully captured the barcode, in the if metadataObj.stringValue != nil block, you should either set the delegate to nil or captureSession to nil, that will stop the scanning temperately(otherwise, it keeps scanning). After you called the api and it returned, you can start the session again, if you want to have another scan.

Upvotes: 6

Related Questions