I am having an issue updating an image's metadata and saving that back to the Photos library. Everything works except that the image metadata after it's altered has missing entries that were there before and I am not getting any errors manipulating the image or executing the photos library change block. Also, the dictionary before it's written back into the image looks like the original plus my dictionary in the debugger.
My questions are:
EDIT:
Before the save all the Exif and Tiff values are present. This is the entirety of the metadata after the save to photos using the code below:
["PixelHeight": 2448, "PixelWidth": 3264, "{Exif}": {
ColorSpace = 1;
PixelXDimension = 3264;
PixelYDimension = 2448;}, "Depth": 8, "ProfileName": sRGB IEC61966-2.1, "Orientation": 1, "{TIFF}": {
Orientation = 1;}, "ColorModel": RGB, "{JFIF}": {
DensityUnit = 0;
JFIFVersion = (
1,
0,
1
);
XDensity = 72;
YDensity = 72;}]
The code, all in Swift 3, testing on iOS 10.1
The basic workflow is:
// Get a mutable copy of the existing Exif meta
let mutableMetaData = getMutableMetadataFrom(imageData: data)
// Check to see if it has the {GPS} entry, if it does just exit.
if let _ = mutableMetaData[kCGImagePropertyGPSDictionary as String] {
callback(imageAsset, true, nil)
return
}
// Add the {GPS} tag to the existing metadata
let clLocation = media.location!.asCLLocation()
mutableMetaData[kCGImagePropertyGPSDictionary as String] =
clLocation.asGPSMetaData()
// Attach the new metadata to the existing image
guard let newImageData = attach(metadata: mutableMetaData, toImageData: data) else {
callback(imageAsset, false, nil)
return
}
let editingOptions = PHContentEditingInputRequestOptions()
imageAsset.requestContentEditingInput(with: editingOptions) { editingInput, info in
guard let editingInput = editingInput else { return }
let library = PHPhotoLibrary.shared()
let output = PHContentEditingOutput(contentEditingInput: editingInput)
output.adjustmentData = PHAdjustmentData(formatIdentifier: "Project", formatVersion: "0.1",
data: "Location Adjustment".data(using: .utf8)!)
do {
try newImageData.write(to: output.renderedContentURL, options: [.atomic])
} catch {
callback(imageAsset, false, error)
return
}
library.performChanges({
let changeRequest = PHAssetChangeRequest(for: imageAsset)
changeRequest.location = clLocation
changeRequest.contentEditingOutput = output
}, completionHandler: { success, error in ... ...
The helper methods to the workflow are:
func attach(metadata: NSDictionary, toImageData imageData:Data) -> Data? {
guard
let imageDataProvider = CGDataProvider(data: imageData as CFData),
let cgImage = CGImage(jpegDataProviderSource: imageDataProvider, decode: nil,
shouldInterpolate: true, intent: .defaultIntent),
let newImageData = CFDataCreateMutable(nil, 0),
let type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType,
"image/jpg" as CFString, kUTTypeImage),
let destination = CGImageDestinationCreateWithData(newImageData,
(type.takeRetainedValue()), 1, nil) else {
return nil
}
CGImageDestinationAddImage(destination, cgImage, metadata as CFDictionary)
CGImageDestinationFinalize(destination)
guard
let newProvider = CGDataProvider(data: newImageData),
let newCGImage = CGImage(jpegDataProviderSource: newProvider, decode: nil,
shouldInterpolate: false, intent: .defaultIntent) else {
return nil
}
return UIImageJPEGRepresentation(UIImage(cgImage: newCGImage), 1.0)
}
func getMutableMetadataFrom(imageData data : Data) -> NSMutableDictionary {
let imageSourceRef = CGImageSourceCreateWithData(data as CFData, nil)
let currentProperties = CGImageSourceCopyPropertiesAtIndex(imageSourceRef!, 0, nil)
let mutableDict = NSMutableDictionary(dictionary: currentProperties!)
return mutableDict
}
Also the asGPSMetaData
is an extension on CLLocation
than looks a Swift 3 version of this Gist
Adding Metadata in Adobe Photoshop In Photoshop, you can update metadata for any open image. Go to File > File Info. Update the Document Title, Description and Copyright Notice on the Description tab. Save the image.
File format forensics: metadataWhen an image is saved, the file typically contains data about the image, known as metadata.
It turns out that it was not the manipulation of the image or metadata with CoreGraphics that was the issue at all, it was a couple of things I overlooked:
PHAssetChangeRequest::creationRequestForAssetFromImage(atFileURL:)
or use PHAssetCreationRequest::forAsset
to create a creation request
and then use PHAssetCreationRequest::addResource(with:data:options:)
to add the data as a photo. I chose the latter as it had less moving
parts.So I guess all that replaces the nice, succinct ALAssetsLibrary::writeImage(toSavedPhotosAlbum:metadata:completionBlock:)
.
The final change block for the Photos library ended up being this:
var assetID: String?
PHPhotoLibrary.shared().performChanges({
let creationRequest = PHAssetCreationRequest.forAsset()
creationRequest.addResource(with: .photo, data: imageWithMetaData as Data, options: nil)
creationRequest.location = clLocation
assetID = creationRequest.placeholderForCreatedAsset?.localIdentifier
}) { success, error in ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With