iOS 8 Photos framework. Access photo metadata
I thought I'd share some code to read the metadata using the ImageIO framework in conjunction with Photos framework. You must request the image data using a PHCachingImageManager.
@property (strong) PHCachingImageManager *imageManager;
Request the image and use it's data to create a metadata dictionary
-(void)metadataReader{
PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:self.myAssetCollection options:nil];
[result enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndex:myIndex] options:NSEnumerationConcurrent usingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
[self.imageManager requestImageDataForAsset:asset options:nil resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
NSDictionary *metadata = [self metadataFromImageData:imageData];
NSLog(@"Metadata: %@", metadata.description);
NSDictionary *gpsDictionary = metadata[(NSString*)kCGImagePropertyGPSDictionary];
if(gpsDictionary){
NSLog(@"GPS: %@", gpsDictionary.description);
}
NSDictionary *exifDictionary = metadata[(NSString*)kCGImagePropertyExifDictionary];
if(exifDictionary){
NSLog(@"EXIF: %@", exifDictionary.description);
}
UIImage *image = [UIImage imageWithData:imageData scale:[UIScreen mainScreen].scale];
// assign image where ever you need...
}];
}];
}
Convert NSData to metadata
-(NSDictionary*)metadataFromImageData:(NSData*)imageData{
CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL);
if (imageSource) {
NSDictionary *options = @{(NSString *)kCGImageSourceShouldCache : [NSNumber numberWithBool:NO]};
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
if (imageProperties) {
NSDictionary *metadata = (__bridge NSDictionary *)imageProperties;
CFRelease(imageProperties);
CFRelease(imageSource);
return metadata;
}
CFRelease(imageSource);
}
NSLog(@"Can't read metadata");
return nil;
}
This has the overhead of grabbing the image, so it's not nearly as fast as enumerating your assets or collections, but it's something at least.
I prefer not to CIImage solution, but to ImageIO solution:
func imageAndMetadataFromImageData(data: NSData)-> (UIImage?,[String: Any]?) {
let options = [kCGImageSourceShouldCache as String: kCFBooleanFalse]
if let imgSrc = CGImageSourceCreateWithData(data, options as CFDictionary) {
let metadata = CGImageSourceCopyPropertiesAtIndex(imgSrc, 0, options as CFDictionary) as! [String: Any]
//print(metadata)
// let image = UIImage(cgImage: imgSrc as! CGImage)
let image = UIImage(data: data as Data)
return (image, metadata)
}
return (nil, nil)
}
below is code to get data from PHAseet
func getImageAndMeta(asset: PHAsset){
let options = PHImageRequestOptions()
options.isSynchronous = true
options.resizeMode = .none
options.isNetworkAccessAllowed = false
options.version = .current
var image: UIImage? = nil
var meta:[String:Any]? = nil
_ = PHCachingImageManager().requestImageData(for: asset, options: options) { (imageData, dataUTI, orientation, info) in
if let data = imageData {
(image, meta) = imageAndMetadataFromImageData(data: data as NSData)
//image = UIImage(data: data)
}
}
// here to return image and meta
}
If you request a content editing input, you can get the full image as a CIImage
, and CIImage
has a property titled properties
which is a dictionary containing the image metadata.
Sample Swift Code:
let options = PHContentEditingInputRequestOptions()
options.networkAccessAllowed = true //download asset metadata from iCloud if needed
asset.requestContentEditingInputWithOptions(options) { (contentEditingInput: PHContentEditingInput?, _) -> Void in
let fullImage = CIImage(contentsOfURL: contentEditingInput!.fullSizeImageURL)
print(fullImage.properties)
}
Sample Objective-C Code:
PHContentEditingInputRequestOptions *options = [[PHContentEditingInputRequestOptions alloc] init];
options.networkAccessAllowed = YES; //download asset metadata from iCloud if needed
[asset requestContentEditingInputWithOptions:options completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
CIImage *fullImage = [CIImage imageWithContentsOfURL:contentEditingInput.fullSizeImageURL];
NSLog(@"%@", fullImage.properties.description);
}];
You'll get the desired {Exif}, {TIFF}, {GPS}, etc dictionaries.
PhotoKit limits the access to metadata to the properties of PHAsset (location, creationDate, favorite, hidden, modificatonDate, pixelWidth, pixelHeight...). The reason (I suspect) is that due to the introduction of iCloud PhotoLibrary the images may not be on the device. Therefore the whole metadata is not available. The only way to get full EXIF/IPTC metadata is to first download the original image (if not available) from iCloud and then use ImageIO to extract its metadata.