Photo capture permission problems in iOS 11
Asked Answered
I

1

17

So here's my problem. I am trying to create a screen in which there is a UIImageView and a UIButton. When the user presses the button, the camera app opens, you take a photo and if you press "Use Photo" in the Camera app, you are returned to my app's screen and the photo is placed in the UIImageView I mentioned previously.

What happens so far is that when I press the "Use Photo" button, the image is correctly placed in my UIImageView but then the app crashes with the following error:

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

What I've done so far is:

  1. Placed the key "Privacy - Photo Library Usage Description" with the value "$(PRODUCT_NAME) uses Library in order to process the photos you captured." in the Info.plist file (also checked how it is written in Source form and it's correct according to the Apple Developer Documentation).

  2. Also placed the key "Privacy - Camera Usage Description" with the value "$(PRODUCT_NAME) uses Cameras" in the Info.plist file.

  3. Checked under "TARGETS->->Info->Custom iOS Target Properties" and the 2 key/value pairs that I mentioned in steps 1 and 2, exist.

I will provide you with my code so far:

import UIKit
import Vision
import MobileCoreServices
import AVFoundation
import Photos

class ViewController: UIViewController, UIImagePickerControllerDelegate, 
UINavigationControllerDelegate {

var newMedia: Bool?

@IBAction func captureImageButtonPressed(_ sender: Any) {
    //let imageName : String = "dolphin"
    //randomImageView.image = UIImage.init(named:imageName)

    if UIImagePickerController.isSourceTypeAvailable(
        UIImagePickerControllerSourceType.camera) {

        let imagePicker = UIImagePickerController()

        imagePicker.delegate = self
        imagePicker.sourceType =
            UIImagePickerControllerSourceType.camera
        imagePicker.mediaTypes = [kUTTypeImage as String]
        imagePicker.allowsEditing = false

        self.present(imagePicker, animated: true,
                     completion: nil)
        newMedia = true
    }
}

@IBAction func classifyButtonPressed(_ sender: UIButton) {
    performVisionRequest()
}
@IBOutlet weak var randomImageView: UIImageView!
@IBOutlet weak var classificationLabel: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
}

func performVisionRequest() {
    let start = DispatchTime.now()
    let model = Resnet50()
    let request = VNImageRequestHandler(cgImage: randomImageView.image!.cgImage!, options: [:])
    do {
        let m = try VNCoreMLModel(for: model.model)
        let coreMLRequest = VNCoreMLRequest(model: m) { (request, error) in
            guard let observation = request.results?.first as? VNClassificationObservation else { return }
            let stop = DispatchTime.now()
            let nanoTime = stop.uptimeNanoseconds - start.uptimeNanoseconds
            let timeInterval = Double(nanoTime)
            self.classificationLabel.text = "\(observation.identifier) (\(observation.confidence * 100)%) in \(timeInterval) seconds."
        }
        try request.perform([coreMLRequest])
    } catch {
        print(error)
    }
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    let mediaType = info[UIImagePickerControllerMediaType] as! NSString
    self.dismiss(animated: true, completion: nil)
    if mediaType.isEqual(to: kUTTypeImage as String) {
        let image = info[UIImagePickerControllerOriginalImage]
            as! UIImage
        randomImageView.image = image
        if (newMedia == true) {
            UIImageWriteToSavedPhotosAlbum(image, self,
                                           #selector(ViewController.image(image:didFinishSavingWithError:contextInfo:)), nil)
        } else if mediaType.isEqual(to: kUTTypeMovie as String) {
            // Code to support video here
        }
    }
}

@objc func image(image: UIImage, didFinishSavingWithError error: NSErrorPointer, contextInfo:UnsafeRawPointer) {
    if error != nil {
        let alert = UIAlertController(title: "Save Failed",
                                      message: "Failed to save image",
                                      preferredStyle: UIAlertControllerStyle.alert)
        let cancelAction = UIAlertAction(title: "OK",
                                         style: .cancel, handler: nil)
        alert.addAction(cancelAction)
        self.present(alert, animated: true,
                     completion: nil)
    }
}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
    self.dismiss(animated: true, completion: nil)
}
}

Any idea why I get the above error in bold? Thank you very much in advance for your time.

Incommodity answered 13/6, 2017 at 16:50 Comment(0)
N
46

NSPhotoLibraryAddUsageDescription was added in iOS 11.

Please add "Privacy - Photo Library Additions Usage Description" in info.plist with a usage description (string), like you did for the other privacy permissions.

Ref: https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html

Natatorium answered 13/6, 2017 at 17:2 Comment(5)
I have added this but it still crashes for me. I am using Version 9.0 beta 5.Nola
@RubenZilibowitz i'm guessing maybe you typed in the plist key wrong. Alternatively, you can input "NSPhotoLibraryAddUsageDescription" and it will change automatically to "Privacy - Photo Library Additions Usage Description".Natatorium
I am pretty sure the plist keys are right. But also my crash is different. *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSDictionaryM setObject:forKey:]: key cannot be nil'Nola
would be nice if Xcode caught some of this stuff as warnings / compile time errorsArgentite
Is there a way to set this programmatically?Colostrum

© 2022 - 2024 — McMap. All rights reserved.