skip to Main Content

I have a view controller where I’m trying to open a camera to scan a QR code. Every time I open the VC it crashes my app with an error message:

This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.

I have double-checked my info.plist and in Target as well. I have permitted the camera usage already, but still facing the same error every time. What is the missing dot? Here is my VC code:

enter image description here

import UIKit


import AVFoundation

class Scanner__VC: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!




override func viewDidLoad() {
    super.viewDidLoad()

    
    view.backgroundColor = UIColor.black
            captureSession = AVCaptureSession()

            guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
            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 = [.qr]
        
    } else {
        failed()
        return
        
    }

    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    previewLayer.frame = view.layer.bounds
    previewLayer.videoGravity = .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 metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
    captureSession.stopRunning()
    
    if let metadataObject = metadataObjects.first {
        guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
        guard let stringValue = readableObject.stringValue else { return }
        AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
        found(code: stringValue)
    }
    
    dismiss(animated: true)
    
}

func found(code: String) {
    print(code)
    
}

override var prefersStatusBarHidden: Bool {
    return true
    
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .portrait
    
}


func showAlert(with message: String) {
    let alert = UIAlertController(title: "QR Code Scanned", message: message, preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "OK", style: .default))
    present(alert, animated: true)
    
}

3

Answers


  1. You have:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>UIApplicationSceneManifest</key>
        <dict>
            <key>NSCameraUsageDescription</key>
            <string>Camera usage reason</string>
            <key>UIApplicationSupportsMultipleScenes</key>
            <false/>
            <key>UISceneConfigurations</key>
            <dict>
                <key>UIWindowSceneSessionRoleApplication</key>
                <array>
                    <dict>
                        <key>UISceneConfigurationName</key>
                        <string>Default Configuration</string>
                        <key>UISceneDelegateClassName</key>
                        <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
                        <key>UISceneStoryboardFile</key>
                        <string>Main</string>
                    </dict>
                </array>
            </dict>
        </dict>
    </dict>
    </plist>
    
    

    For the next use of NSCameraUsageDescription in my answer, it can be applied to the PhotoLibrary & the two Location permissions too.

    NSCameraUsageDescription is inside UIApplicationSceneManifest (in a hierarchy level speaking), while it should be at the top hierarchy of the plist, on the same level as UIApplicationSceneManifest.

    You should have something like that:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>NSCameraUsageDescription</key>
        <string>Camera usage reason</string>
        <key>UIApplicationSceneManifest</key>
        <dict>
            <key>UIApplicationSupportsMultipleScenes</key>
            <false/>
            <key>UISceneConfigurations</key>
            <dict>
                <key>UIWindowSceneSessionRoleApplication</key>
                <array>
                    <dict>
                        <key>UISceneConfigurationName</key>
                        <string>Default Configuration</string>
                        <key>UISceneDelegateClassName</key>
                        <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
                        <key>UISceneStoryboardFile</key>
                        <string>Main</string>
                    </dict>
                </array>
            </dict>
        </dict>
    </dict>
    </plist>
    
    

    I put it before the UIApplicationSceneManifest to highlight the fact that they are at the same hierarchy level, but it could be afterward, it doesn’t matter, it’s a key access (dictionary), not index access (array)

    Login or Signup to reply.
  2. As the screenshot shows, you added the permission declaration to the wrong location so you got the error.
    enter image description here

    Login or Signup to reply.
  3. If any of such crash come you should have to check the Info.plist file or the Info tab in the Target setting. There you have to look at the error key e.g in your case its NSCameraUsageDescription.

    If its there then it wont be crash but if its not there you should add that key with proper description.

    <key>NSCameraUsageDescription</key>
    <string>Camera Usage Description</string>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search