SwiftUI.cc
Snippets
ControlsMediaIntermediate

SwiftUI PhotosPicker and Loading Selected Images

PhotosPicker presents the photo library without permission prompts and returns PhotosPickerItem values you load asynchronously via Transferable into Image or Data.

4 min readUpdated 2026-06
Avatar picker with async load
import SwiftUI
import PhotosUI

struct AvatarPicker: View {
    @State private var item: PhotosPickerItem?
    @State private var avatar: Image?

    var body: some View {
        VStack(spacing: 16) {
            (avatar ?? Image(systemName: "person.crop.circle"))
                .resizable()
                .scaledToFill()
                .frame(width: 96, height: 96)
                .clipShape(.circle)

            PhotosPicker("Choose Photo", selection: $item,
                         matching: .images)
        }
        .onChange(of: item) {
            Task {
                avatar = try? await item?.loadTransferable(type: Image.self)
            }
        }
    }
}

PhotosPicker preview

Out-of-process privacy

PhotosPicker runs in a separate process: the user browses their full library, but your app receives only what they explicitly choose — hence no permission alert. The result arrives as PhotosPickerItem handles, not images.

Loading is async by design

.onChange(of: item) {
    Task {
        if let data = try? await item?.loadTransferable(type: Data.self),
           let ui = UIImage(data: data) {
            avatar = Image(uiImage: ui)
        }
    }
}

Loading as Data is the robust route when you also upload or cache; loading as Image is fine for display-only. Either way the bytes may come from iCloud, so treat it as a network-grade operation: progress, cancellation on new selection, error states.

Filters and multi-select

matching: composes: .any(of: [.images, .livePhotos]), .not(.screenshots). For galleries, bind an array and set maxSelectionCount; iterate the items and load each one, updating UI incrementally rather than blocking on all.

Common mistakes

  • Loading on the main flow synchronously-in-spirit — always Task it.
  • Ignoring iCloud latency, freezing the avatar slot with no feedback.
  • Re-loading every previously picked item when only the newest changed.

Related reference