PHAssetCreationRequest fails with cocoa error -1

Hello,


I'm trying to save an image to the camera roll alongside an edited version. The goal is to replicate the behavior of the iOS Camera app, when a color filter is selected: when you take a photo, the original photo is saved, alongside an edit with the filtered photo.


I can't seem to get this to work using one PHAssetCreationRequest - as shown in the code below, the operation fails with "error The operation couldn’t be completed. (Cocoa error -1.)" and no further info as to what I'm doing wrong.


            PHPhotoLibrary.shared().performChanges({() -> Void in
                let creationRequest: PHAssetCreationRequest = PHAssetCreationRequest.forAsset()
                let creationOptions = PHAssetResourceCreationOptions()
                creationRequest.addResource(with: .photo, fileURL: editedImageURL, options: nil)
                creationRequest.addResource(with: .adjustmentBasePhoto, fileURL: imageURL, options: nil)
                creationRequest.addResource(with: .adjustmentData, data: "somedata".data(using: .ascii)!, options: creationOptions)
      
            }, completionHandler: { (success: Bool, error : Error?) -> Void in
                if !success {
                    NSLog("error \(error?.localizedDescription ?? "unknown error")")
                }
          
            })



Thanks,

Sebastien

Accepted Reply

Ok I found it in the documentation.. this use case is actually explicity documented at https://developer.apple.com/documentation/photos/phassetchangerequest/1624056-placeholderforcreatedasset

The trick is to create a PHContentEditingOutput from the PHObjectPlaceholder, in the same change block used for creating the asset, and assign it to the PHAssetCreationRequest's contentEditingOutput property.

Replies

bump

For what it's worth, I'm seeing the same with drag and drop implementations (dragging a video from Photos over to our app).

Hello sseb974,


Have you looked at the sample we have that shows it in action. What we are doing to the photo may be diffent, but the overall process is the same.


< https://developer.apple.com/library/content/samplecode/AVCamManual/Listings/AVCamManual_AVCamManualPhotoCaptureDelegate_m.html>

Thanks @3zs, yes I looked at that example - it also calls the addResource method on PHAssetCreationRequest twice:

- using PHAssetResourceType.alternatePhoto for the DNG file

- using PHAssetResourceType.photo for the jpeg file


I've tried to the same approach, but I get the same "Cocoa error -1 error".

Note if I call the addResource method on PHAssetCreationRequest only once with the original or edited image, it works.

Just to clarify: I tried the same approach with two JPEG files (the original photo, and the 'edited' version) and it fails with cocoa error -1. If I try with a DNG and a JPEG as per your example, it works of course.

Ok I found it in the documentation.. this use case is actually explicity documented at https://developer.apple.com/documentation/photos/phassetchangerequest/1624056-placeholderforcreatedasset

The trick is to create a PHContentEditingOutput from the PHObjectPlaceholder, in the same change block used for creating the asset, and assign it to the PHAssetCreationRequest's contentEditingOutput property.

Bump again. The example sited in https://developer.apple.com/library/content/samplecode/AVCamManual/Listings/AVCamManual_AVCamManualPhotoCaptureDelegate_m.html does not try to add editing data so does not seem relevant.

For those who might come across this thread in the future, here's a code example in Swift demonstrating what @sseb974 was referring to above:

PHPhotoLibrary.shared().performChanges {
      let creationRequest = PHAssetCreationRequest.forAsset()
      creationRequest.addResource(with: .photo, data: myOriginalImageData, options: nil)
      let editingOutput = PHContentEditingOutput(placeholderForCreatedAsset: creationRequest.placeholderForCreatedAsset!)
      try! myRenderedJpegData.write(to: editingOutput.renderedContentURL, options: .atomic)
      creationRequest.contentEditingOutput = editingOutput
    } completionHandler: { (success: Bool, error: Error?) in
    }