At this year’s WWDC Apple introduced PHPicker
, a view controller that lets users add photos and videos from their photo library to your app. It supports many new features which makes it a big step up from UIImagePickerController
, which is now deprecated. Besides that, it is designed with privacy in mind, which is always a good thing. Let’s have a look at what this new picker is about and how you can start using it in your app!
What’s new?
PHPickerViewController
is the new system provided picker that allows you to get access to images and videos from the user’s photo library. When you use the new picker for the first time you immediately notice that it feels very similar to the Photos app. It has a similar user interface which allows you to zoom in and out of the grid and it provides the same search functionality. It also allows for multi-select, something that UIImagePickerController
lacked and which was one of the main reasons why people decided to create their own picker.
User privacy
A lot of apps that work with photos and videos make use of a custom picker. My guess is that besides the need for customization most apps do this to get features like multi-select. As these pickers need to use PhotoKit
users explicitly have to grant photo library access to their entire library before they can use it. This is not really privacy friendly as apps should only get access to what they need. For example, if you only need to upload one profile picture there is no reason to give an app access to your full library. This is not an issue for PHPicker
because it runs in a separate process and renders its UI on top of your app. Therefore apps can only access the user-selected content. Therefore it is not needed to request photo library access anymore.
How to use PHPicker
?
Let’s have a look at how to use the new picker.
Import Photos UI framework
First, you’ll need to import the PhotosUI framework.
import PhotosUI
Create a new instance of PHPickerViewController.
You create a PHPickerViewController
by initializing it with a PHPickerConfiguration
, which allows you to customize the behavior of the picker:
The filter
property defines which type of content gets shown in the picker. When you don’t specify this property the picker by default will show all content (photos, live photos, and videos).
The selectionLimit
lets you specify the maximum amount of photos and videos that can be selected at once.
We’ll get to the preferredAssetRepresentationMode
later in this article as it is related to the way you retrieve content from the picker.
var configuration = PHPickerConfiguration()
configuration.selectionLimit = 0
let pickerViewController = PHPickerViewController(configuration: configuration)
Set up the delegate
The picker reports its result through a delegate protocol that contains a single method picker(_:didFinishPicking:)
.
Therefore you need to implement the delegate protocol.
extension MyViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
guard !results.isEmpty else {
/// There are no results meaning the picker was cancelled.
dismiss(animated: true, completion: nil)
return
}
// TODO: - Grab the images from the results
dismiss(animated: true, completion: nil)
}
}
And assign the delegate to the view controller.
pickerViewController.delegate = self
Present the picker
Present the picker from your view controller.
present(pickerViewController, animated: true, completion: nil)
Getting the selected content from the picker.
When a user selects some content in the picker, it will call the delegate method with an array of PHPickerResult
objects. Each result contains an NSItemProvider
and an assetIdentifier
with which you can retrieve the content. It is best to use the item provider as the asset identifier requires the use of PhotoKit which means it requires user permission and that you have to deal with the limited photo library which can lead to some strange flows.
Loading the data from the item provider
An item provider acts as a wrapper around data so that it can be sent from one process to another. You grab the data from an item provider by asking to load an object of a specific type, for example, a UIIMage
. However, loading several UIImages in memory can quickly lead to memory issues. Therefore, it is better to get a hold of the actual underlying file, which can be done with the loadFileRepresentation(forTypeIdentifier:completionHandler:)
method.
This method will write a copy of the file to a temporary location. If the user uses iCloud photo library and the content is not locally available it will be automatically downloaded. As this file will be deleted once the completion handler returns you will need to copy this file to a location in your app. According to Apple, the copy will create an APFS clone which should be really fast.
Let’s look at an example of loading an image
import UniformTypeIdentifiers
// Use UTIType.image to ask for image data.
itemProvider.loadFileRepresentation(forTypeIdentifier: UTIType.image) { url, error in
guard error == nil else {
// Handle error
return
}
// Copy the file to a folder in your app.
do {
let fileURL = "URL of the location where the file should be copied to"
try FileManager.default.copyItem(at: self as URL, to: fileURL)
} catch {
// handle error
}
// Do some further processing, for example, create previews images with Core Graphics.
// Display the content in the UI.
}
Preferred asset representation mode
In the example we use a UTIType
, Uniform Type Identifier, to specifically ask for image data. If the item provider contains an image it will give back one otherwise it will complete with an error. So if the item provider contains a JPEG
image it will give you back a JPEG file. It could also be that the item provider contains multiple file representations, for example, HEIC
and JPEG
. In that case, which file the item provider will give back is determined by the preferredAssetRepresentationMode
which is set on the configuration. The default, .automatic
, will give you the most compatible file. In the previous example that would be the jpeg. The default can be slow for videos as it will transcode them on the fly if they are in HEVC
. If that becomes an issue you can set the mode to current
which will always give you back the original file. Just make sure that you will be able to handle all the file formats that are supported by the photo library.
Conclusion
Because of the many new features, it is likely you want to replace UIMagePicker
or your custom picker with PHPicker
.
Now that you know how to use the new picker it is time to go and try it out in your app!
Contact me on Twitter @kairadiagne if you have any questions, comments, or feedback.