ios – Why are my delegate methods never called when running AVCapturePhotoOutput.capturePhoto(with:delegate:)?


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.

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img