Reputation: 33
here is my code that lets users pick a profile photo:
Earlier I only let users take a photo with their camera, for their profile image, but I decided to change it so a user can now pick a photo from the photo library.
The issue is that my users are able to access the photo library without getting a request alert to access their photo library. "App so and so would like access to your photo library". I have Info.plist for Photo Library Usage and Photo Library Additions Usage. Also while my app is running in the simulator, when I go into 'settings' then to 'Privacy' I go to Photos to see if my app has access but their is nothing there that mentions my app having access to the photo library.
If anyone can help me figure out what I am doing wrong and how I can get the request to access the 'photo library' to work, it would be greatly appreciated!! Thank you.
class ProfileImageVC: UIViewController {
@IBOutlet weak var AddAProfilePictureLabel: UILabel!
@IBOutlet weak var ProfilePictureImageView: UIImageView!
@IBOutlet weak var TapToChangeButton: UIButton!
@IBOutlet var ErrorLabel: UILabel!
@IBOutlet var FinishButton: UIButton!
var ImagePicker:UIImagePickerController!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let imageTap = UITapGestureRecognizer(target: self, action: #selector(openImagePicker))
ProfilePictureImageView.isUserInteractionEnabled = true
ProfilePictureImageView.addGestureRecognizer(imageTap)
ProfilePictureImageView.layer.cornerRadius = ProfilePictureImageView.bounds.height / 2
ProfilePictureImageView.clipsToBounds = true
TapToChangeButton.addTarget(self, action: #selector(openImagePicker), for: .touchUpInside)
//when user clicks they can choose a photo from library
//instantiate image picker
ImagePicker = UIImagePickerController()
ImagePicker.allowsEditing = true
ImagePicker.delegate = self
}
// taping to add a photo
@objc func openImagePicker(_ sender:Any) {
// Open Image Picker
#if targetEnvironment(simulator)
// simulator
ImagePicker.sourceType = .photoLibrary
self.present(ImagePicker, animated: true, completion: nil)
#else
// real device
ImagePicker.sourceType = .photoLibrary
self.present(ImagePicker, animated: true, completion: nil)
#endif
}
func checkCameraAccess() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .denied:
print("Denied, request permission from settings")
presentCameraSettings()
case .restricted:
print("Restricted, device owner must approve")
case .authorized:
self.present(ImagePicker, animated: true, completion: nil)
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { success in
DispatchQueue.main.async {
if success {
self.present(self.ImagePicker, animated: true, completion: nil)
} else {
self.presentCameraSettings()
}
}
}
@unknown default:
break
}
}
func presentCameraSettings() {
let alertController = UIAlertController(title: "Error",
message: "Photo library access is denied",
preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Cancel", style: .default))
alertController.addAction(UIAlertAction(title: "Settings", style: .cancel) { _ in
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
}
})
present(alertController, animated: true)
}
}
//extend the proper delagate method
extension ProfileImageVC: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
//cancel
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
//pick an image
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
//get the image the selected
if let pickedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
self.ProfilePictureImageView.image = pickedImage
picker.dismiss(animated: true, completion: nil)
startLoading(self.view)
//upload to firbase
PhotoService.savePhoto(image: pickedImage) { error in
stopLoading(self.view)
let resturantslocation =
self.storyboard?.instantiateViewController(identifier: Constants.Storyboard.userslocation) as? ResturantsUsersLocationVC
self.view.window?.rootViewController = resturantslocation
self.view.window?.makeKeyAndVisible()
}
}
}
}
Upvotes: 2
Views: 3259
Reputation: 39
The things you have to consider for user permission
When you need user's permission?
If you need to access photos metadata you need user's permission. MetaData includes geoLocation, Created TimeStamp and Camera information etc
How it is working?
It looks like the app have access to photo library but the picker runs out of process, so your app will only receive the content that the user selects.
If you need to ask permission from user. add the below code snippet.
@objc func showAttachmentAction(sender _: UIBarButtonItem) {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .notDetermined:
PHPhotoLibrary.requestAuthorization { [weak self] status in
guard let self else { return }
if status == .authorized {
DispatchQueue.main.async {
self.present(self.imagePicker, animated: true)
}
}
}
case .restricted, .denied:
let alert = UIAlertController(title: "Permisstion Denied", message: "APPName does not have access to photos. To enable access please go to the device's Settings > Privacy > Photos > AppName", preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default)
alert.addAction(okAction)
present(alert, animated: true)
case .authorized:
present(imagePicker, animated: true)
default:
break
}
}
But make sure to add in info.plist Privacy - Photo Library Usage Description before asking permission. If not the app will crash.
Upvotes: 1
Reputation: 535140
The issue is that my users are able to access the photo library without getting a request alert to access their photo library
That is not an "issue". That is correct behavior.
The UIImagePickerController (or, in iOS 14, the PHPickerViewController) does not require user authorization for the photo library. That's because your app is not accessing the photo library! The runtime is accessing it, and the user is willingly handing you a photo. You receive the photo, not as a full-fledged photo in the library — that would be a PHAsset — but as a mere UIImage. So all you are getting is disembodied images.
If you wanted to turn around and fetch the PHAsset and look into the library, you would need user authorization. But you are just asking for the .editedImage
, so you don't need user authorization.
If the .editedImage
is nil
, that's because it's the wrong key; if the user didn't edit the image, what you want is the .originalImage
. And you still won't need authorization.
If you want to ask the system to present the alert that requests authorization, go ahead and ask it to do that. But the user won't get the alert by magic merely because you use the picker.
Upvotes: 6