I am writing a custom photography app for iOS. I’m pretty new to coding and I’ve never used swift before.
Right now, I just want to get the app to take a photo and save it to the user’s photo library. Apple’s documentation says I use capturePhoto() to do this, and that I need to give it a AVCapturePhotoCaptureSettings object as well as a AVCapturePhotoCaptureDelegate object. I do exactly this, and yet, when the app runs capturePhoto(), none of the methods in my delegate object are called, and I mean absolutely none.
Here is my CameraSession code:
import Foundation
import AVFoundation
import UIKit
import Photos
class CameraSession: UIViewController {
let captureSession = AVCaptureSession()
let photoOutput = AVCapturePhotoOutput()
private let sessionQueue = DispatchQueue(label: "session queue")
var previewView = CameraPreview()
func configureSession() {
captureSession.beginConfiguration()
let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera,
for: .video, position: .unspecified)
// Configure inputs
guard
let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice!),
captureSession.canAddInput(videoDeviceInput)
else { return }
captureSession.addInput(videoDeviceInput)
print("Inputs configured")
// Configure outputs
guard captureSession.canAddOutput(photoOutput) else {
print("Could not add photo output to session")
return
}
captureSession.sessionPreset = .photo
captureSession.addOutput(photoOutput)
print("Outputs configured")
// End and committ configuration
captureSession.commitConfiguration()
// Connect session to preview
self.previewView.videoPreviewLayer.session = self.captureSession
print("Preview connected")
// Run the session
sessionQueue.async {
self.captureSession.startRunning()
}
print("Session running")
}
func configurePhotoSettings() -> AVCapturePhotoSettings {
print("photoSettings configured")
var photoSettings = AVCapturePhotoSettings()
if self.photoOutput.availablePhotoCodecTypes.contains(AVVideoCodecType.hevc) {
photoSettings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.hevc])
print(photoSettings)
}
photoSettings.flashMode = .auto
return photoSettings
}
func capturePhoto() {
sessionQueue.async {
print("Taking photo")
let photoSettings = self.configurePhotoSettings()
let photoDelegate = CapturePhotoDelegate()
print("sessionQueue.async running capturePhoto")
self.photoOutput.capturePhoto(with: photoSettings, delegate: photoDelegate)
}
}
}
And here is my CapturePhotoDelegate code:
import Foundation
import Photos
class CapturePhotoDelegate: NSObject {
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
override init() {
super.init()
print("New delegate")
}
func isPhotoLibraryReadWriteAccessGranted() async -> Bool {
//let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
var isAuthorized = (status == .authorized)
print("Requesting access to library")
if status == .notDetermined {
isAuthorized = await PHPhotoLibrary.requestAuthorization(for: .readWrite) == .authorized
}
return isAuthorized
}
}
//TODO: Why is the delegate never called when running capturePhoto()?
extension CapturePhotoDelegate: AVCapturePhotoCaptureDelegate {
// Monitors capture progress
func photoOutput(_ output: AVCapturePhotoOutput, willBeginCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
print("willBeginCaptureFor")
print(resolvedSettings)
}
func photoOutput(_ output: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
print("willCapturePhotoFor")
}
func photoOutput(_ output: AVCapturePhotoOutput, didCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
print("didCapturePhotoFor")
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {
print("didFinishCaptureFor")
}
// Receives capture results
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
print("didFinishProcessingPhoto")
if let error {
print("Error processing photo: \(error.localizedDescription)")
return
}
Task {
await savePhotoToLibrary(photo: photo)
}
}
func savePhotoToLibrary(photo: AVCapturePhoto) async {
print("Attempting to save photo")
guard await isPhotoLibraryReadWriteAccessGranted() else { return }
print("Library access granted")
if let photoData = photo.fileDataRepresentation() {
PHPhotoLibrary.shared().performChanges {
let creationRequest = PHAssetCreationRequest.forAsset()
creationRequest.addResource(with: .photo, data: photoData, options: nil)
} completionHandler: { success, error in
if let error {
print("Error saving photo: \(error.localizedDescription)")
return
}
}
}
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishRecordingLivePhotoMovieForEventualFileAt outputFileURL: URL, resolvedSettings: AVCaptureResolvedPhotoSettings) {
print("didFinishRecordingLivePhotoMovie")
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingLivePhotoToMovieFileAt outputFileURL: URL, duration: CMTime, photoDisplayTime: CMTime, resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {
print("didFinishProcessingLivePhotoMovie")
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishCapturingDeferredPhotoProxy deferredPhotoProxy: AVCaptureDeferredPhotoProxy?, error: Error?) {
print("didFinishCapturingDefferredPhotoProxy")
}
}
I’ve tried changing what I request in my photoSettings configuration, and I’ve put a lot of print statements in the delegate to see if anything is called.
I’ve tried to use xcode’s debugger, but whenever I want to step into the capturePhoto() method and see why it does not call what it should, the debugger does not let me. So now, I have turned to stack overflow.




