The following function downloads a photo, edits exif metadata attached to it, and saves it back. The function works only for photos that already have PHAdjustmentData binding, i.e. Photos that were previously edited by another application. If the photo has not been edited, it does not work in the performChanges () block and prints
Failed to save. Error: Optional(Error Domain=NSCocoaErrorDomain Code=-1 "(null)").
Why is this happening in this situation? Looking through Stack Overflow, I saw a number of other options for this question, but none of them seemed to be resolved. I know this fails if the saved image is PNG, but my original image is JPEG, so the saved image is also JPEG.
func editPhotoProperties(_ asset: PHAsset) { let options = PHContentEditingInputRequestOptions() options.canHandleAdjustmentData = { data in return false } asset.requestContentEditingInput(with: options) { input, info in if let input = input { let adjustmentData = PHAdjustmentData(formatIdentifier:"viewfinder", formatVersion:"1.0", data:"viewfinder".data(using:.utf8)!) let output = PHContentEditingOutput(contentEditingInput:input) output.adjustmentData = adjustmentData do { let imageData = try Data(contentsOf:input.fullSizeImageURL!) } catch { print("Failed to load data") return } let properties = getImageDataProperties(imageData)! let properties2 = properties.mutableCopy() as! NSMutableDictionary // edit properties2 ... let newImageData = addImageProperties(imageData: imageData, properties: properties2) do { try newImageData!.write(to: output.renderedContentURL, options: .atomic) } catch { print("Failed to write to disk") return } PHPhotoLibrary.shared().performChanges({ let changeRequest = PHAssetChangeRequest(for:asset) changeRequest.contentEditingOutput = output }) { success, error in if !success { print("Failed to save. Error: \(String(describing:error))") } } } } } func getImageDataProperties(_ data: Data) -> NSDictionary? { if let imageSource = CGImageSourceCreateWithData(data as CFData, nil) { if let dictionary = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) { return dictionary } } return nil } // add image properties (exif, gps etc) to image func addImageProperties(imageData: Data, properties: NSDictionary?) -> Data? { // create an imagesourceref if let source = CGImageSourceCreateWithData(imageData as CFData, nil) { // this is of type image if let uti = CGImageSourceGetType(source) { // create a new data object and write the new image into it let destinationData = NSMutableData() if let destination = CGImageDestinationCreateWithData(destinationData, uti, 1, nil) { // add the image contained in the image source to the destination, overidding the old metadata with our modified metadata CGImageDestinationAddImageFromSource(destination, source, 0, properties) if CGImageDestinationFinalize(destination) == false { return nil } return destinationData as Data } } } return nil }
source share