This demo adds a permissions key to the info.plist
for camera permissions. It also demonstrates how a UI behaves differently if the user denies permission or grants permission.
This is the app you can find in the starter folder of the materials repo. When you open it, you’ll see that there’s already a lot of code in the app. That’s because this module isn’t about how to import images into an app, it’s about how to use the Vision Framework. There are extra comments in the code in case you aren’t familiar with importing images and want to explore further.
When you run the app, you can see that it can access the user’s File library and their Photo library without any issues or permission requests. This is because both are configured for read-only access. However, when the app attempts to open the camera, it crashes. That’s because it was built to the simulator. The simulator doesn’t support any camera, yet. It seems like soon, there will be some kind of camera support in the simulator, but for now, when you want to work with the camera, you need to build to device.
So, now the app is building to a device. Now when the app attempts to open the camera, it still crashes. The reason for this crash is different, though. You can see the crash message in the console telling you that it needs a permissions key.
This is actually a better developer experience than older versions of iOS. In those versions, it’d work fine when building from Xcode but then crash on real builds. How embarrassing to finally be ready to demo on device to your friends only to have the app crash.
To fix this, you stop the app running by clicking the stop button in Xcode. Now, use the File Navigator to find the main project file and then open the Info tab.
Working with the Keys in the Target Properties can be a little tricky. To add your permissions key, hover over the end of one of the rows and then click the little plus button that appears. This usually works, but sometimes it doesn’t, especially if you click the last entry. If you misclick, instead of adding a new entry, you might remove one or change the key name of an existing entry. A more reliable way to add a row is to move the mouse over where you want to add the row and then right-click or Control-click to get the context menu.
Now, you can select the Add Row option from the menu, opening a dropdown of the human-readable versions of all the keys. Scroll down to the Privacy section and select Privacy - Camera Usage Description. Unfortunately, the console didn’t tell you that this is the human-readable version of the key. So you have a few choices here to confirm you’ve entered the right key. You can again show the context menu and select the Raw Keys and Values option. Now, the dropdown shows the key names, and you can find one that matches the key in the console.
You can also copy and paste from the console into the list like this. First highlight the key in the console. Now, use Command-C to copy it to the clipboard. Find the row you’ve already added, or add a new row to the info plist and double-click the name to highlight it. Paste in the key now that there’s a row for it. The last step is to enter a value. This should be one sentence about why the user should give your app camera permission. You can’t leave it blank. For the demo, add something like this:
“Let the app use the camera to import images to our app.”
Now, make sure there aren’t any extra blank entries you accidentally made while working with the file. You can remove any extras by hovering over the end and clicking the minus button. You can also highlight the row and use Command-X or the context menu to cut it out.
Build and run. When you click the button to access the camera for the first time, it shows a modal like this one:
The name of your app appears in quotation marks. That matches the product name in Xcode here.
The rest of the bold text comes from Apple and you can’t change it. You also can’t change the names of the buttons. But you can see that the text you entered in info.plist appears on the modal. Once the user clicks OK, the camera viewfinder appears.
So now, delete the app and run it again. Deleting the app is the only way to clear the user’s permission choices. when you run the app again, deny permissions. Notice that there’s no feedback when I click the button subsequent times. The viewfinder stays covered. iOS asks only once.
You can improve your user’s experience by paying attention to the permissions and letting them know to go to the settings app to change them.
Here in the CameraView
, there’s already some code to communicate to the user the permission status. .notDetermined
means you’ve never asked for permission. .denied
is for when they have denied permission. .restricted
is when some system policy doesn’t let apps use the camera. You’ll usually only see this in large organizations that supply locked down phones to their employees. Then, there’s .authorized
- that means the user has granted permission.
Here in the view model, there’s a function to check the current value of the camera permissions, checkCameraPermissions()
. You can add the guts by adding this code.
let status = AVCaptureDevice.authorizationStatus(for: .video)
DispatchQueue.main.async {
self.cameraPermissionStatus = status
}
AVCaptureDevice
is any capture device on the phone, like the camera. .authorizationStatus(for:)
returns the current authorization status for the camera. Then the code is assigning the value of status to a local variable .cameraPermissionStatus
Although the app only uses still images, you request the authorization status for .video
. .video
is for using the camera regardless of whether you’re working with still images or video. Also, the results of access requests don’t always come back on the main thread, so it’s safest to use a dispatch queue before you do anything that will affect the UI.
Now, down in the requestCameraPermissions()
, add some code to request permissions.
AVCaptureDevice.requestAccess(for: .video) { _ in
self.checkCameraPermissions()
}
The response from .requestAccess(for:)
is a Boolean, but because you want to determine the actual authorizationStatus
, you can ignore the response by using the _
.
Scroll down to the button that shows the camera. Replace showCameraPicker = true
with some code that changes the button action depending on the permission status.
switch viewModel.cameraPermissionStatus {
case .authorized:
showCameraPicker = true
case .notDetermined:
viewModel.requestCameraPermissions()
default:
break
}
Now, when the user clicks the button, the app displays the camera, asks for permissions, or does nothing. Scroll down further. Here is a Text
that shows those permissionMessage
values you saw earlier. Delete the app to reset the permissions and build and run the app on a real device.
When you tab over to the camera tab and tap the button, the permissions dialog appears. After accepting or denying the request, you can see that the permission text has updated to show the current status. Now when you tap Capture Photo you see the view finder correctly.
Apple moves things around in Settings pretty regularly, but in iOS 17 you can navigate over to the Settings and scroll down to find the Permissions Demo app. This is where the user can toggle the camera permissions for the app.
In the next lessons, you’ll apply Vision requests to imported images. The lessons and demos will usually use PhotosUI to get images from the Photo Library. However, if you want to work with the camera or use the Files app instead, this code will be in the Starter project in the next lessons but will be disabled. It should be pretty straightforward to wire it up though.