# Photo Picker in SwiftUI (PhotosPicker)

In this post, we’ll implement a SwiftUI view that allows the users to pick a photo from their photo library, with the SwiftUI `PhotosPicker`.

The source code of this post is available [here](https://github.com/xavier7t/DemoPhotoPicker20230216).

# Step 0 - Overview

Before getting started, let’s learn more about `PhotosPicker`. It is a public structure of type SwiftUI view; users can select one or multiple photos from their library via this picker. Note that this is a struct in `PhotosUI`and is available on devices with iOS 16, macOS 13, watchOS 9 or higher.

# Step 1 - Import `PhotosUI`

As mentioned in the overview, the `PhotosPicker` is part of the `PhotosUI` so let’s create a SwiftUI project and add the import expression above the `ContentView` structure.

```swift
import SwiftUI
import PhotosUI

struct ContentView: View {
    var body: some View {
    //...
    }
}
```

# Step 2 - Add a state property and the PhotosPicker

Add a `@State` property called `selected` of type `[PhotosPickerItem]`.

Now in the VStack of the `body` of the `ContentView`, let’s declare a `PhotoPicker` element. The parameters of the initializer are explained below:

* `selection: Binding<[PhotosPickerItem]>` Here we need to pass the state property selected declared as instructed above. Since this is a binding array of PhotosPickerItem, the item(s) selected in the picker will be updated to the `selected` array in our view.
    
* `maxSelectionCount: Int?` This is an optional of Integer, which sets the maximum number of items that the user can pick from the library.
    
* `matching filter: PHPickerFilter?` This parameter is a `PHPickerFilter`, which is a struct in `PhotosUI` that represents the type of elements (images or videos that can be stored and picked from a photo library) and acceptable values included: `.images`, `.videos`, `.screenshots` etc. In this demo, we’ll use `.images` to get a photo.
    
* `@ViewBuilder label: Label` In short this parameter is where we define the front end of the picker. For instance, in this demo, we passed in a VStack with a photo icon and a text message "Select a photo".
    

```swift
struct ContentView: View {
    @State var selected: [PhotosPickerItem] = []
    var body: some View {
        VStack {
            PhotosPicker(selection: $selected, maxSelectionCount: 1, matching: .images) {
                VStack {
                    Image(systemName: "photo")
                        .resizable()
                        .frame(width: 60, height: 45, alignment: .center)
                    Text("Select a photo")
                }
            }
        }
        .padding()
    }
}
```

Once this step is done, you should be able to see a picker and on tap gesture, a picker view will be presented, where you can select the default images in the simulator or your photos if you’re running this project on a physical device.

# Step 3 - Handle changes in the selection

Now you might be wondering, we have a state property to hold the selected item(s) from the photo library, how can the users see the images after selecting them?

We’re now addressing that. The state property `selected` is an array of `PhotosPickerItem` and need to fetch the data from it first. When do we need to perform this fetch? Every time the user changes the selection from the picker, right? Hence, we need an `onChange` modifier to observe the updates in `selected`.

First, add another state property to hold the data fetched from the selected items. We can make it an optional so that failure to parse the image data can be handled.

```swift
@State var data: Data?
```

Then add the on change modifier as shown below:

Every time when `selected` gets an update, the code will try to read the `selected` array to check if there is at least one item. If no item is found, no further action will be performed. (Logic of the `guard else` statement.)

If at least one item is found, it invokes the function `.loadTransferable` to parse the selected photo item and transfer the item to data. In the completion handler, we use a switch to handle both successful and failing cases. If the transfer fails, here only an error message gets printed. If the transfer goes through, we use `if let` to avoid nil value in data. If not nil, the data gets assigned to the state property `data` in the view.

```swift
PhotosPicker(selection: $selected, maxSelectionCount: 1, matching: .images) {
    VStack {
        //...
    }
}
//-------new lines below
.onChange(of: selected) { newValue in
    guard let selectedItem = selected.first else {
        return
    }
    selectedItem.loadTransferable(type: Data.self) { result in
        switch result {
        case .success(let data):
            if let data = data {
                self.data = data
            } else {
                print("Found nil in data")
            }
        case .failure(let error):
            print(error.localizedDescription)
        }
    }
}
```

# Step 4 - Present the selected image

Since we got the data of the image in the view now, we’re ready to present the selected image in the view.

Add the following code in the `ContentView` `VStack`, above the `PhotosPicker`.

The `if let` statement double checks if the data is non-nil, if not, an UIKit UIImage element will be initiated with the data.

Inside the `if` statement, we can add a text like "Selected photo:" as a hint of what this image is. The image below the text is declared with the UIImage element. You can use a `fit` `aspectRatio` to make the image size fit the view more.

```swift
if let data = data, let uiImage = UIImage(data: data) {
    VStack {
        HStack {
            Text("Selected photo: ")
                .bold()
            Spacer()
        }
        Image(uiImage: uiImage)
            .resizable()
            .aspectRatio(contentMode: .fit)
    }
    .padding()
}
Spacer()
    .frame(height: 50)
```
