I have this simple code for saving an image:
@IBAction private func saveImageButtonTouch(sender:UIButton) {
let image = areaForImageCaptureView.asImage()
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
@objc func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
if let error = error {
let alertVC = UIAlertController(title: "Save Error", message: "Could not save image to camera roll", preferredStyle: .alert)
alertVC.addAction(UIAlertAction(title: "Okay", style: .default))
present(alertVC, animated: true)
} else {
// Saved
}
}
If the user does not allow access, clicking the button again does not re-prompt for access, just shows the same error.
How would I open the Settings for my app to allow the user to change their permissions?
The best solution is to check the current permission for adding an image to the photo library before you attempt to save the image. If the permission has been denied you can show an alert informing the user of this condition and offer them the option to open the app settings so they can change the permission if they wish.
@IBAction private func saveImageButtonTouch(sender:UIButton) {
let status = PHPhotoLibrary.authorizationStatus(for: .addOnly)
switch status {
case .notDetermined:
PHPhotoLibrary.requestAuthorization(for: .addOnly) { [unowned self] status in
if status != .denied {
saveImage()
}
}
case .denied:
let alertVC = UIAlertController(title: "Save Denied", message: "You have denied the ability to save images to your photo library.", preferredStyle: .alert)
alertVC.addAction(UIAlertAction(title: "OK", style: .default))
alertVC.addAction(UIAlertAction(title: "Settings", style: .default) { action in
let url = URL(string: UIApplication.openSettingsURLString)!
UIApplication.shared.open(url)
})
present(alertVC, animated: true)
case .restricted, .authorized, .limited:
saveImage()
@unknown default:
break
}
}
private func saveImage() {
let image = areaForImageCaptureView.asImage()
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
@objc func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
if error != nil {
let alertVC = UIAlertController(title: "Save Error", message: "Could not save image to camera roll", preferredStyle: .alert)
alertVC.addAction(UIAlertAction(title: "Okay", style: .default))
present(alertVC, animated: true)
} else {
// Saved
}
}
In theory you could simply call requestAuthorization(for:)
, without first calling authorizationStatus(for:)
, and handle all of the status
values. But then the user experience isn't as good. This is because the first time the user runs your app and taps the button the user will be prompted for their permission choice. If they choose "Deny" then the code would immediately prompt them about having denied permission. But they know they just denied it. By making both authorization calls, you have better control over the user experience.
One other possible solution would be to attempt to write the image with no authorization check (as you are doing now). If the user denies permission, the write will fail and there will be an error in the completion handler (as you are seeing now). But unfortunately the error that you get does not give you an error that clearly indicates that the user has denied permission. If the error did give such an indication, you could handle that specific error to prompt the user to go to settings.
Keep in mind that if the user taps the Settings button then they will be taken to the app's settings page. If the user changes the Photos permission (or any other privacy permission your app happens to use) then the app will be terminated. This is normal for any iOS app that has a permission change. If the user then returns to the app, the app will be fully restarted just as if the user had force-quit the app. This same behavior will be seen even if the user runs the Settings app directly and changes a privacy setting for an app. This behavior is not specific to launching the app's settings from within the app.