How to Read and Write Image File Metadata with CoreGraphics
How to read and write Exif, GPS, IPTC, JFIF, TIFF and other metadata in image files using the CoreGraphics APIs on iOS and macOS
UIImage allows you to very easily save the image to a file, with UIImagePNGRepresentation(_)
or UIImageJPEGRepresentation(_, _)
. However, those new images will be new, with the current date and time, and containing no location or camera metadata. If you want to edit an image without losing those things, that's a problem.
Fortunately, CoreGraphics includes several easy API's to access the image files raw metadata.
However, these are the raw values themselves. If you want a CLLocation object from the GPS metadata you'll have to parse the "{GPS}"
section of the metadata yourself.
Read Image Metadata
To read the image file metadata you only need a CoreGraphics image source and then to pass that image source to the CGImageSourceCopyPropertiesAtIndex(_, _, _)
function. This function then returns a CFDictionaryRef
, which can just be cast to an NSDictionary
, containing all the image file metadata.
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
NSLog(@"%@", (__bridge NSDictionary *)(imageProperties) );
let data = NSData(contentsOf: url)!
let source = CGImageSourceCreateWithData(data as CFData, nil)!
let metadata = CGImageSourceCopyPropertiesAtIndex(source, 0, nil)!
print(metadata)
Example Image Metadata
{
ColorModel = RGB;
Depth = 8;
PixelHeight = 5632;
PixelWidth = 11264;
ProfileName = "sRGB IEC61966-2.1";
"{Exif}" = {
ColorSpace = 1;
DateTimeDigitized = "2000:02:12 20:20:14";
DateTimeOriginal = "2000:02:12 20:20:14";
PixelXDimension = 11264;
PixelYDimension = 5632;
};
"{GPS}" = {
Altitude = "3.522659318754763";
AltitudeRef = 0;
Latitude = "50.79826333333333";
LatitudeRef = N;
Longitude = "0.5709116666666667";
LongitudeRef = W;
};
"{IPTC}" = {
DateCreated = 20000612;
DigitalCreationDate = 20000612;
DigitalCreationTime = 202014;
TimeCreated = 202014;
};
"{JFIF}" = {
DensityUnit = 0;
JFIFVersion = (
1,
0,
1
);
XDensity = 72;
YDensity = 72;
};
"{TIFF}" = {
Make = Apple;
Model = "iPhone11,1 - 13.1.1";
};
}
{
"{Exif}" = {
DateTimeDigitized = "2010:01:12 20:20:14";
DateTimeOriginal = "2010:01:12 20:20:14";
};
"{GPS}" = {
Altitude = "2.522659318754763";
AltitudeRef = 0;
Latitude = "53.79826333333333";
LatitudeRef = N;
Longitude = "-1.5709116666666667";
LongitudeRef = W;
};
}
Write Image Metadata
To write image file metadata is very similar. Provided with an image source, and the type of image file being written to, we can create a new CGImageDestinationRef
and then write in the image and metadata with CGImageDestinationAddImageFromSource(_, _, _, _)
.
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
CFStringRef uniformTypeIdentifier = CGImageSourceGetType(source);
CGImageDestinationRef destination = CGImageDestinationCreateWithURL( (__bridge CFURLRef)url, uniformTypeIdentifier, 1, NULL);
CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef)metadata);
CGImageDestinationFinalize(destination);
guard let source = CGImageSourceCreateWithURL(url as CFURL, nil),
let uniformTypeIdentifier = CGImageSourceGetType(source) else { return }
guard let destination = CGImageDestinationCreateWithURL(url as CFURL, uniformTypeIdentifier, 1, nil) else { return }
CGImageDestinationAddImageFromSource(destination, source, 0, metadata)
guard CGImageDestinationFinalize(destination) else { return }
Example Projects
The example code Xcode Projects demonstrating how to use the new API are available on Github:
The example projects support iOS and macOS and allow you to select an image file. The XML representation of the metadata dictionary is then loaded into an editable text view. You can then save the edited metadata and export the resulting file.