How to let user select photos from iOS Photo Library

Much better experience. No permissions required and multi-select!

Published: June 25, 2020
See books

In iOS 14 there is a brand new way to let users select a photo(s) or video(s) from their library to use in your app and it has some cool features and benefits. Let's see what these are and how basic PHPickerViewController usage looks like.

Benefits

No permissions required - you don't need to provide usage description in Info.plist to present the new picker and system does not show the allow access dialog. That is because users have full control over what media they select and subsequently send to your app.

Modern UI consistent with Photos - the new picker is much easier to use because the UI is very similar to what you get in Photos and there is also a search. This should be much better selection experience for the users than with the UIImagePickerViewController

Multi-selection - what else to say? 🙂

Usage overview

Using the new PHPickerViewController is pretty straightforward. It uses the traditional delegate model that will alert you when user finishes her selection. There is just a single delegate method to support and a bit of configuration.

Start by importing the PhotosUI framework:

import PhotosUI

Then continue with creating the configuration object PHPickerConfiguration. So far this allows to customize how many media files user can select and what kind of:

var configuration = PHPickerConfiguration()
configuration.selectionLimit = 3
configuration.filter = .images

In this example we are setting the selectionLimit to 3 (default is 1). You can also set 0 to mean unlimited. And next with filter we are saying that we want just images.

You can also ask for videos or livePhotos and combine these with the any(of: method like so:

configuration.filter = .any(of: [.livePhotos, .images])

Next create the picker itself and set yourself as its delegate:

let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self

We have two things left. Present the picker:

present(picker, animated: true, completion: nil)

The UI is different depending if multi-selection is enabled or not.

The new PHPhotoPickerViewController

And implement the delegate:

extension ViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        dismiss(animated: true, completion: nil)
        guard !results.isEmpty else { return }
     }
}

So far we are just dismissing the picker and checking that we have results. Unlike with UIImagePickerViewController we won't get the URL path to the file but instead a NSItemProvider that we can ask to load a specific object. For images this may look like this:

We will use a loop and get the itemProvider for each result:

for result in results {
    let provider = result.itemProvider
}

And then we can load the UIImage.

if provider.canLoadObject(ofClass: UIImage.self) {
     provider.loadObject(ofClass: UIImage.self) { (image, error) in
             DispatchQueue.main.async {
                 if let image = image as? UIImage {
                      self.imageView.image = image
                 }
             }
    }
 }

Just to be safe we first ask if the object can be loaded and then load it. To be honest I am not that well versed with NSItemProvider.

And to recap. Here is entire code from above:

extension ViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
    dismiss(animated: true, completion: nil)
        guard !results.isEmpty else { return }
    }

    for result in results {
        let provider = result.itemProvider

        if provider.canLoadObject(ofClass: UIImage.self) {
             provider.loadObject(ofClass: UIImage.self) { (image, error) in
                 DispatchQueue.main.async {
                     if let image = image as? UIImage {
                          self.imageView.image = image
                     }
                 }
            }
         }
    }
}

With UIImagePickerViewController there was a settings to specify the resolution for exported media which here does not appear to be a case. I plan to investigate this further and either update this post or write new one detailing how to get video or image with specific resolution.

EDIT: Indeed there is no support for automatic compression. The picker will always pass the original video/image and it is up to the app to do the necessary compressions or edits.

In the WWDC 2020 session about this new photo picker Apple specifically mentions that we should be using this new one instead of the older UIImagePickerViewController. This is not deprecated yet but presets for image/video quality are as per new documentation:

Uses: Xcode 12 & Swift 5.3

Filip Němeček profile photo

WRITTEN BY

Filip Němeček @nemecek_f@iosdev.space

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀