Photo Library Access with PhotoKit
Guides you through photo picking, limited library handling, and saving photos to the camera roll using privacy-forward patterns.
When to Use This Skill
Use when you need to:
- β Let users select photos from their library
- β Handle limited photo library access
- β Save photos/videos to the camera roll
- β Choose between PHPicker and PhotosPicker
- β Load images from PhotosPickerItem
- β Observe photo library changes
- β Request appropriate permission level
Example Prompts
"How do I let users pick photos in SwiftUI?"
"User says they can't see their photos"
"How do I save a photo to the camera roll?"
"What's the difference between PHPicker and PhotosPicker?"
"How do I handle limited photo access?"
"User granted limited access but can't see photos"
"How do I load an image from PhotosPickerItem?"
Red Flags
Signs you're making this harder than it needs to be:
- β Using UIImagePickerController (deprecated for photo selection)
- β Requesting full library access when picker suffices (privacy violation)
- β Ignoring
.limited authorization status (users can't expand selection)
- β Not handling Transferable loading failures (crashes on large photos)
- β Synchronously loading images from picker results (blocks UI)
- β Using PhotoKit APIs when you only need to pick photos (over-engineering)
- β Assuming
.authorized after user grants access (could be .limited)
Mandatory First Steps
Before implementing photo library features:
1. Choose Your Approach
What do you need?
ββ User picks photos (no library browsing)?
β ββ SwiftUI app β PhotosPicker (iOS 16+)
β ββ UIKit app β PHPickerViewController (iOS 14+)
β ββ NO library permission needed! Picker handles it.
β
ββ Display user's full photo library (gallery UI)?
β ββ Requires PHPhotoLibrary authorization
β ββ Request .readWrite for browsing
β ββ Handle .limited status with presentLimitedLibraryPicker
β
ββ Save photos to camera roll?
β ββ Requires PHPhotoLibrary authorization
β ββ Request .addOnly (minimal) or .readWrite
β
ββ Just capture with camera?
ββ Don't use PhotoKit - see camera-capture skill
2. Understand Permission Levels
| Level |
What It Allows |
Request Method |
| No permission |
User picks via system picker |
PHPicker/PhotosPicker (automatic) |
.addOnly |
Save to camera roll only |
requestAuthorization(for: .addOnly) |
.limited |
User-selected subset only |
User chooses in system UI |
.authorized |
Full library access |
requestAuthorization(for: .readWrite) |
Key insight: PHPicker and PhotosPicker require NO permission. The system handles privacy.
3. Info.plist Keys
<key>NSPhotoLibraryUsageDescription</key>
<string>Access your photos to share them</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Save photos to your library</string>
Core Patterns
Pattern 1: SwiftUI PhotosPicker (iOS 16+)
Use case: Let users select photos in a SwiftUI app.
import SwiftUI
import PhotosUI
struct ContentView: View {
@State private var selectedItem: PhotosPickerItem?
@State private var selectedImage: Image?
var body: some View {
VStack {
PhotosPicker(
selection: $selectedItem,
matching: .images
) {
Label("Select Photo", systemImage: "photo")
}
if let image = selectedImage {
image
.resizable()
.scaledToFit()
}
}
.onChange(of: selectedItem) { _, newItem in
Task {
await loadImage(from: newItem)
}
}
}
private func loadImage(from item: PhotosPickerItem?) async {
guard let item else {
selectedImage = nil
return
}
if let data = try? await item.loadTransferable(type: Data.self),
let uiImage = UIImage(data: data) {
selectedImage = Image(uiImage: uiImage)
}
}
}
Multi-selection:
@State private var selectedItems: [PhotosPickerItem] = []
PhotosPicker(
selection: $selectedItems,
maxSelectionCount: 5,
matching: .images
) {
Text("Select Photos")
}
Advanced Filters (iOS 15+/16+)
matching: .screenshots
matching: .screenRecordings
matching: .sloMoVideos
matching: .cinematicVideos
matching: .depthEffectPhotos
matching: .bursts
matching: .any(of: [.videos, .livePhotos])
matching: .all(of: [.images, .not(.screenshots)])
matching: .all(of: [.images, .not(.any(of: [.screenshots, .panoramas]))])
Cost: 15 min implementation, no permissions required
Pattern 1b: Embedded PhotosPicker (iOS 17+)
Use case: Embed picker inline in your UI instead of presenting as sheet.
import SwiftUI
import PhotosUI
struct EmbeddedPickerView: View {
@State private var selectedItems: [PhotosPickerItem] = []
var body: some View {
VStack {
SelectedPhotosGrid(items: selectedItems)
PhotosPicker(
selection: $selectedItems,
maxSelectionCount: 10,
selectionBehavior: .continuous,
matching: .images
) {
Text("Select")
}
.photosPickerStyle(.inline)
.photosPickerDisabledCapabilities([.selectionActions])
.photosPickerAccessoryVisibility(.hidden, edges: .all)