Skip to content
intermediate Phase 8 · Media & Hardware

Camera & Photos

Access the camera, capture photos/videos, and integrate with the Photos library.

50m
3 problems
Topic Progress 0%

Camera Access

Requesting Camera Permission

Before accessing the camera, you must request permission. Add NSCameraUsageDescription to your Info.plist with a clear description.

import AVFoundation

class CameraPermissionManager: ObservableObject {
    @Published var hasPermission = false
    
    func checkPermission() {
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized:
            hasPermission = true
        case .notDetermined:
            AVCaptureDevice.requestAccess(for: .video) { granted in
                DispatchQueue.main.async { self.hasPermission = granted }
            }
        case .denied, .restricted:
            hasPermission = false
        @unknown default:
            hasPermission = false
        }
    }
}

Setting Up AVCaptureSession

AVCaptureSession manages the flow of data from camera input to output.

import AVFoundation
import UIKit

class CameraManager: NSObject, ObservableObject {
    var captureSession: AVCaptureSession?
    var photoOutput: AVCapturePhotoOutput?
    var previewLayer: AVCaptureVideoPreviewLayer?
    @Published var lastPhoto: UIImage?
    
    func setupCamera() {
        captureSession = AVCaptureSession()
        captureSession?.sessionPreset = .photo
        
        guard let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back),
              let input = try? AVCaptureDeviceInput(device: camera) else { return }
        
        if captureSession?.canAddInput(input) == true {
            captureSession?.addInput(input)
        }
        
        photoOutput = AVCapturePhotoOutput()
        if captureSession?.canAddOutput(photoOutput!) == true {
            captureSession?.addOutput(photoOutput!)
        }
        
        captureSession?.startRunning()
    }
}

Camera Device Configuration

Configure camera properties like focus, exposure, and white balance.

func configureCamera() throws {
    guard let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
        throw CameraError.deviceNotFound
    }
    
    try camera.lockForConfiguration()
    
    // Auto focus
    if camera.isFocusModeSupported(.continuousAutoFocus) {
        camera.focusMode = .continuousAutoFocus
    }
    
    // Auto exposure
    if camera.isExposureModeSupported(.continuousAutoExposure) {
        camera.exposureMode = .continuousAutoExposure
    }
    
    // Auto white balance
    if camera.isWhiteBalanceModeSupported(.continuousAutoWhiteBalance) {
        camera.whiteBalanceMode = .continuousAutoWhiteBalance
    }
    
    camera.unlockForConfiguration()
}

Photo Capture

Capturing Photos

Use AVCapturePhotoOutput to capture photos from the camera session.

extension CameraManager: AVCapturePhotoCaptureDelegate {
    func capturePhoto() {
        let settings = AVCapturePhotoSettings()
        settings.flashMode = .auto
        settings.isHighResolutionPhotoEnabled = true
        photoOutput?.capturePhoto(with: settings, delegate: self)
    }
    
    func photoOutput(_ output: AVCapturePhotoOutput,
                     didFinishProcessingPhoto photo: AVCapturePhoto,
                     error: Error?) {
        guard let data = photo.fileDataRepresentation(),
              let image = UIImage(data: data) else { return }
        DispatchQueue.main.async {
            self.lastPhoto = image
        }
    }
}

SwiftUI Camera Interface

Build a custom camera view using UIViewRepresentable.

struct CameraPreviewView: UIViewRepresentable {
    let session: AVCaptureSession
    
    func makeUIView(context: Context) -> UIView {
        let view = UIView(frame: UIScreen.main.bounds)
        let previewLayer = AVCaptureVideoPreviewLayer(session: session)
        previewLayer.frame = view.bounds
        previewLayer.videoGravity = .resizeAspectFill
        view.layer.addSublayer(previewLayer)
        return view
    }
    func updateUIView(_ uiView: UIView, context: Context) { }
}

struct CameraScreen: View {
    @StateObject private var camera = CameraManager()
    
    var body: some View {
        ZStack {
            if let session = camera.captureSession {
                CameraPreviewView(session: session)
                    .ignoresSafeArea()
            }
            
            VStack {
                Spacer()
                Button(action: { camera.capturePhoto() }) {
                    Circle()
                        .fill(.white)
                        .frame(width: 80, height: 80)
                        .overlay(Circle().stroke(.gray, lineWidth: 4))
                }.padding(.bottom, 30)
            }
        }
        .onAppear { camera.setupCamera() }
    }
}

Photos Library Integration

PHPickerViewController

Use the modern PHPickerViewController to let users select photos from their library.

import SwiftUI
import PhotosUI

struct PhotoPickerView: UIViewControllerRepresentable {
    @Binding var selectedImages: [UIImage]
    let selectionLimit: Int
    @Environment(\.dismiss) var dismiss
    
    func makeUIViewController(context: Context) -> PHPickerViewController {
        var config = PHPickerConfiguration()
        config.selectionLimit = selectionLimit
        config.filter = .images
        
        let picker = PHPickerViewController(configuration: config)
        picker.delegate = context.coordinator
        return picker
    }
    func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) { }
    
    func makeCoordinator() -> Coordinator { Coordinator(self) }
    
    class Coordinator: NSObject, PHPickerViewControllerDelegate {
        let parent: PhotoPickerView
        init(_ parent: PhotoPickerView) { self.parent = parent }
        
        func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
            let group = DispatchGroup()
            var images: [UIImage] = []
            
            for result in results {
                group.enter()
                result.itemProvider.loadObject(ofClass: UIImage.self) { image, _ in
                    if let image = image as? UIImage { images.append(image) }
                    group.leave()
                }
            }
            group.notify(queue: .main) {
                self.parent.selectedImages = images
                self.parent.dismiss()
            }
        }
    }
}

Saving to Photos Library

Save captured images to the user's photo library using PHPhotoLibrary.

import Photos

func saveToPhotosLibrary(image: UIImage) {
    PHPhotoLibrary.shared().performChanges {
        PHAssetChangeRequest.creationRequestForAsset(from: image)
    } completion: { success, error in
        if success {
            print("Photo saved successfully")
        } else if let error = error {
            print("Failed to save: \(error.localizedDescription)")
        }
    }
}

Fetching Photos from Library

Access photos programmatically using PHAsset and PHImageManager.

import Photos

func fetchRecentPhotos(limit: Int = 10) -> [UIImage] {
    var images: [UIImage] = []
    let fetchOptions = PHFetchOptions()
    fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
    fetchOptions.fetchLimit = limit
    
    let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions)
    let imageManager = PHImageManager.default()
    
    assets.enumerateObjects { asset, _, _ in
        let options = PHImageRequestOptions()
        options.synchronous = true
        options.deliveryMode = .highQualityFormat
        
        imageManager.requestImage(
            for: asset,
            targetSize: CGSize(width: 200, height: 200),
            contentMode: .aspectFill,
            options: options
        ) { image, _ in
            if let image = image { images.append(image) }
        }
    }
    return images
}

Quiz

1. What Info.plist key is required for camera access?

Question 1 options

2. What class manages the camera session flow?

Question 2 options

3. How do you capture a photo with AVCaptureSession?

Question 3 options

4. What is PHPickerViewController used for?

Question 4 options

Flashcards

Question

What permission is needed for camera access?

Answer

Add NSCameraUsageDescription to Info.plist and request AVCaptureDevice.requestAccess(for: .video).

Question

How does AVCaptureSession work?

Answer

It connects AVCaptureDeviceInput (camera) to AVCaptureOutput (photo/video) and manages the data flow.

Question

What is the modern way to pick photos in iOS?

Answer

PHPickerViewController provides a system photo picker with configurable filters and selection limits.

Question

How do you save an image to the photo library?

Answer

Use PHPhotoLibrary.shared().performChanges with PHAssetChangeRequest.creationRequestForAsset(from:).

Revision Notes

Key Takeaways

  • 1. Request camera permission before accessing AVCaptureSession
  • 2. AVCaptureSession connects camera input to output
  • 3. Use AVCapturePhotoOutput to capture still images
  • 4. PHPickerViewController is the modern photo picker
  • 5. PHPhotoLibrary handles saving and fetching photos

Interview Tips

  • Explain the AVCaptureSession pipeline: input -> session -> output
  • Discuss how to handle camera permission denial gracefully
  • Describe the difference between synchronous and asynchronous image loading

Cheat Sheet

Camera & Photos Quick Reference

  • NSCameraUsageDescription - Required Info.plist key
  • AVCaptureSession - Manages camera data flow
  • AVCapturePhotoOutput - Captures photos
  • AVCapturePhotoCaptureDelegate - Handles captured photos
  • PHPickerViewController - System photo picker
  • PHPhotoLibrary - Save and fetch photos
  • PHAsset - Represents a photo/video asset
  • PHImageManager - Load image data from assets