Home / Blog / iOS Camera and Photos: the permission flow that ships

iOS Camera and Photos: the permission flow that ships

Asking for camera and photo permissions the wrong way gets your app rejected or chases users away. The flow that works, and the mistakes that got me there.

I’ve been on the wrong end of an App Store rejection. The reason: asking for the camera permission unnecessarily. On our signup screen we asked for camera access for the profile photo. Even when the user said “I don’t want to pick a photo” and skipped, the app kept asking for permission. The reviewer rejected it. They were right.

Camera and Photos on iOS is one of the most commonly mis-wired permission flows. Here’s what the correct shape looks like.

Core rule: ask only when the user is trying to do the thing

Permission is requested the moment the user takes an action that needs it. “Let’s ask on app open so we’re ready” is a bad experience, and it’s explicitly forbidden in the App Store guidelines.

So:
– User taps on the profile photo.
– An action sheet pops up: “Camera”, “Gallery”, “Cancel”.
– User picks “Camera”.
– Now you request camera access.

Info.plist strings

You have to write three strings correctly, wrong ones come back from review:

  • NSCameraUsageDescription: “We need camera access so you can take your profile photo.”
  • NSPhotoLibraryUsageDescription: “We need photo library access so you can attach the photos you pick.”
  • NSPhotoLibraryAddUsageDescription: “We need permission to save the content you create to your photo library.”

Don’t confuse the three. If you’re only saving (add), ask for add permission, not full access. Less permission calms the user and gets you through review.

Strings have to be concrete and transparent. Vague wording like “to support functionality” is a rejection reason.

The new Photos permission model (iOS 14+)

Photos now has three states:
authorized: full access.
limited: user granted access to specific photos.
denied / notDetermined: no access.

Use limited carefully. If the user chose a subset, your app has to work with that subset. If you use PHPickerViewController, Apple handles this for you, the UI already behaves correctly. If you use PHPhotoLibrary directly:

PHPhotoLibrary.shared().register(observer)
PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
  switch status {
  case .authorized, .limited: showPicker()
  case .denied, .restricted: showPermissionDeniedGuide()
  case .notDetermined: break
  @unknown default: break
  }
}

PHPickerViewController: the no-permission alternative

Since iOS 14, PHPickerViewController lets you present a photo picker without ever asking for Photos permission. The user picks a photo inside the picker, your app only gets what they selected. No permission dialog at all.

If all you need is “let the user pick a photo”, switch to PHPickerViewController. Review goes more smoothly too, because Apple prefers apps that ask for fewer permissions.

var config = PHPickerConfiguration()
config.selectionLimit = 1
config.filter = .images
let picker = PHPickerViewController(configuration: config)
picker.delegate = self
present(picker, animated: true)

Camera permission flow

There’s no alternative for the camera, you need AVCaptureDevice.requestAccess(for: .video). A few things to watch for:

  1. Ask on first camera use. Not on app open.
  2. If the user denies, a second request won’t show the same dialog, iOS returns silently. Your app has to detect that: show a Settings redirect screen. “You can grant camera access from Settings” plus a UIApplication.openSettingsURLString link.
  3. Handle the permission result on the main thread. The requestAccess completion comes in on a background thread.

Pre-prompt (the soft ask)

After a denial you can’t ask again, so don’t waste the first ask. You can show a soft pre-prompt.

  • “We’d like camera access to add your profile photo. Shall we continue?”
  • Yes: the system dialog comes up.
  • No: we never open the system dialog, no state is burned.

That way a not-ready user doesn’t slide into “denied” and can try again later.

Review checklist

  • Are your Info.plist strings concrete and honest?
  • Is permission requested during the user’s action?
  • Are you asking for the minimum (like PHPicker)?
  • If denied, do you show the user a path forward?
  • Is limited mode tested end to end?
  • Did you test the camera-in-use indicator (the green dot since iOS 14)?

One more tip: there’s no Camera in the simulator. Test the camera flow on a real device. People who only test on simulator miss limited mode and ship bugs to prod.

Reading Apple’s Camera and Photos framework docs on an ongoing basis pays off, small changes land in every iOS version. iOS 17 tightened limited mode further, iOS 18 brought Photos picker optimisations. You have to keep current.

Have a project on this topic?

Leave a brief summary — I’ll get back to you within 24 hours.

Get in touch