What’s New With PhotosPicker in iOS 16

PhotosPicker in iOS 16 has a robust set of features for users to view and select assets, such as images and videos. By Felipe Laso-Marsetti.

Leave a rating/review
Download materials
Save for later
Share

Working with photos is commonplace in today’s era of mobile apps, and PhotosPicker is usually the go-to choice, especially when working with SwiftUI.

Happy Swift Bird

iOS 16 brings some important and useful enhancements to improve your development workflow with PhotosPicker:

  • Improved SwiftUI implementation.
  • New filter options that can be back-ported to iOS 15.
  • Compound filters.
  • Availability for macOS and watchOS.
  • Updated design for iPadOS.

In this tutorial, you will learn how to work with the PhotosPicker API and also see what is new as of iOS 16.

For this tutorial you should have at least some beginner knowledge of Swift and SwiftUI.

Without further ado, you’ll jump right into an awesome tutorial that leverages PhotosPicker and iOS 16.

Understanding PhotosPicker

PhotosPicker is a SwiftUI view that allows you to select one or more assets from your photo library. It’s part of the PhotoKit framework for working with videos and images from your device’s photos app.

PhotosPicker

With as little code as the following:

@State private var photosPickerItem: PhotosPickerItem?
PhotosPicker(selection: $photosPickerItem, matching: .images) {
  Label("Select Photo", systemImage: "photo")
}

You can have a button that, when tapped, shows a robust, native view for your users to pick photo or video assets from their photo library.

PhotosPicker Button

Some of the customization options you have are changing the button’s image, label or tint color.

You can also add filters to control what types of assets can be selected. If you’re working on an app that relies exclusively on Live Photos, hiding all other asset types will save you and your users time.

Once your users have made their asset selection, you’ll work with PhotosPickerItem, a representation of your selections in PhotosPicker, to get information about the selection or the actual assets themselves.

How about seeing all of this in action?

Getting Started

Download the starter project by clicking Download Materials at the top or bottom of the tutorial.

You’ll see a project called Foodvorite, a journal app that keeps track of your favorite foods with images. The project uses sample, hard-coded data for meals, since this tutorial’s focus isn’t on adding or editing meals or on persisting them with your photo selections.

Open the project to see how it’s set up. Then, open Meal.swift to look at the model that represents a meal entry in the app. A meal consists of an ID, title, notes and data for the associated photo.

You’ll use four views in the project:

  • MealsView: Shows a list of all your meals.
  • MealRow: View that makes up a row for a meal entry. The row shows the meal’s title, notes and a thumbnail of the photo (if a selection has been made).
  • MealDetail: View to show all the details of a meal and a large image of the selected photo (if available).
  • PhotoView: Helper view to show either a placeholder image or a photo in two different sizes — one for the thumbnail and a larger one for the meal details.

Build and run the project to see the app in action and familiarize yourself with it.

Foodvorite App showing three entries

Implementing a Photo Picker

Time to implement a PhotosPicker view, so meals no longer show the placeholder image.

Open MealDetail.swift, and add the following property as part of the view:

@State private var photosPickerItem: PhotosPickerItem?

This property is used with PhotosPicker to handle any individual asset selections the user makes.

Next, at the bottom of the VStack but still inside it, add the following code:

PhotosPicker(selection: $photosPickerItem) {
  Label("Select Photo", systemImage: "photo")
}

Here, you add the PhotosPicker view with the photo SF Symbol as the button icon and Select Photo as the button text.

The initializer for the PhotosPicker view takes one parameter, a binding to an optional PhotosPickerItem. To use PhotosPicker, you need to add an import for PhotosUI, which was already done for you as part of the starter project.

Build and run. Select any meal from the list to go to the details. Notice the button that’s now showing.

PhotosPicker Basic Button with Pumpkin Pie selected

Tap the photos picker. The photos picker will show all the available photos and videos on your device.

PhotosPicker Asset Selection

Excellent!

You’ll handle selections in a little bit, but for now, how about styling the button so it looks better?

Still inside VStack in MealDetail.swift, add the button inside the horizontal stack with some spacing and padding:

HStack(spacing: 10) {
  PhotosPicker(selection: $photosPickerItem) {
    Label("Select Photo", systemImage: "photo")
  }
}
.padding()

This helps add some padding so the button isn’t squished with the rest of the view’s content.

Next, add these modifiers to PhotosPicker:

.tint(.accentColor)
.buttonStyle(.borderedProminent)

The two modifiers above help give the button the project’s accent color and a large, prominent visual style.

Build and run. Navigate to a meal’s details. Look at the select photo button.

PhotosPicker Improved Button

It definitely looks better than before!

Handling Selections

So, you’ve added the Select Photo and the button it shows. You can even select a photo from the picker. This action dismisses the picker even though nothing happens at the moment. Time to take care of that!

Also in MealDetail.swift, add the following function inside struct and below body to load the photo’s data that your user selects in the picker:

private func updatePhotosPickerItem(with item: PhotosPickerItem) async {
  photosPickerItem = item
  if let photoData = try? await item.loadTransferable(type: Data.self) {
    meal.photoData = photoData
  }
}

This is an async method that takes PhotosPickerItem as a parameter. Within it, you assign the item parameter to the view’s photosPickerItem property.

You then call loadTransferable on PhotosPickerItem to acquire the asset’s data. This method is asynchronous and can throw an error, so you mark it with try? await accordingly.

If the method returns valid data, then you store it in the meal’s photoData property.

So, where can you call this method from? Still within MealDetail.swift, add the following modifier to ScrollView, after the navigation modifiers:

.onChange(of: photosPickerItem) { selectedPhotosPickerItem in
  guard let selectedPhotosPickerItem else {
    return
  }
  Task {
    await updatePhotosPickerItem(with: selectedPhotosPickerItem)
  }
}

This code block will get called whenever the photosPickerItem changes. Inside, you have a guard statement to ensure you have a valid PhotosPickerItem; otherwise, you return.

If there’s a non-nil item, you call the method you just wrote within a Task — since it’s an async method — and mark it with await.

Build and run. Navigate to a meal’s details. Tap Select Photo. Select a photo for your meal.

Meal Details With Photo

Yay! How cool is PhotosPicker?

Next, navigate back to the list of meals. Notice the row corresponding to the meal you just selected a photo for.

Meal List With Photo