Skip to content

Commit

Permalink
Update iOS image picker
Browse files Browse the repository at this point in the history
* Preserve metadata for images when targeting JPEG output
* Fixes react-native-image-picker#2220 - continue picker if only available format is HEIF or HEIC
  • Loading branch information
jefflewis committed Sep 29, 2023
1 parent 1c7d59c commit 298e956
Showing 1 changed file with 69 additions and 6 deletions.
75 changes: 69 additions & 6 deletions ios/ImagePickerManager.mm
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#import "UniformTypeIdentifiers/UniformTypeIdentifiers.h"
#import "ImagePickerManager.h"
#import "ImagePickerUtils.h"
#import <React/RCTConvert.h>
Expand Down Expand Up @@ -173,7 +174,45 @@ -(NSMutableDictionary *)mapImageToAsset:(UIImage *)image data:(NSData *)data phA
float quality = [self.options[@"quality"] floatValue];
if (![image isEqual:newImage] || (quality >= 0 && quality < 1)) {
if ([fileType isEqualToString:@"jpg"]) {
// Get image source data before mutating it
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)data, nil);
CGMutableImageMetadataRef metadata = nil;
// Attempt to extract the existing image metadata
if (imageSource != nil) {
metadata = CGImageMetadataCreateMutableCopy(CGImageSourceCopyMetadataAtIndex(imageSource, 0, nil));
}

data = UIImageJPEGRepresentation(newImage, quality);

// Set the image source to be the newly resized image
imageSource = CGImageSourceCreateWithData((CFDataRef)data, nil);
if (imageSource != nil && metadata != nil) {
// If we have existing metadata, merge that into new imageSource
CFMutableDataRef newData = CFDataCreateMutable(nil, 0);
CGImageDestinationRef destination = CGImageDestinationCreateWithData(newData, kUTTypeJPEG, 1, nil);
CGImageDestinationCopyImageSource(
destination,
imageSource,
(CFDictionaryRef)@{
(__bridge NSString*)kCGImageDestinationMetadata : (__bridge NSDictionary*)metadata,
(__bridge NSString*)kCGImageDestinationMergeMetadata : @YES
},
nil);
// Set the underlying data to the data created by the CGImageDestination
// Use __bridge_transfer to transfer ownership and release newData
data = (__bridge_transfer NSData*)newData;

// Release the CGImageDestination
if (destination != nil) {
CFRelease(destination);
}
}

// Release the CGImageSource
if (imageSource != nil) {
CFRelease(imageSource);
}

} else if ([fileType isEqualToString:@"png"]) {
data = UIImagePNGRepresentation(newImage);
}
Expand Down Expand Up @@ -434,6 +473,10 @@ + (NSURL *)getNSURLFromInfo:(NSDictionary *)info {
}
}

+ (NSDictionary *)getMetadataFromInfo:(NSDictionary *)info {
return info[UIImagePickerControllerMediaMetadata];
}

@end

@implementation ImagePickerManager (UIImagePickerControllerDelegate)
Expand All @@ -457,7 +500,11 @@ - (void)imagePickerController:(UIImagePickerController *)picker didFinishPicking
if ([info[UIImagePickerControllerMediaType] isEqualToString:(NSString *) kUTTypeImage]) {
UIImage *image = [ImagePickerManager getUIImageFromInfo:info];

[assets addObject:[self mapImageToAsset:image data:[NSData dataWithContentsOfURL:[ImagePickerManager getNSURLFromInfo:info]] phAsset:asset]];
[assets addObject: [self mapImageToAsset: image
data: [NSData dataWithContentsOfURL: [ImagePickerManager getNSURLFromInfo:info]]
phAsset: asset
metadata: [ImagePickerManager getMetadataFromInfo: info]]];
[ImagePickerManager getNSURLFromInfo:info]] phAsset:asset]];
} else {
NSError *error;
NSDictionary *videoAsset = [self mapVideoToAsset:info[UIImagePickerControllerMediaURL] phAsset:asset error:&error];
Expand Down Expand Up @@ -538,23 +585,39 @@ - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPick

dispatch_group_enter(completionGroup);

if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]) {
if ([provider canLoadObjectOfClass:[UIImage class]]){
NSString *identifier = provider.registeredTypeIdentifiers.firstObject;
// Matches both com.apple.live-photo-bundle and com.apple.private.live-photo-bundle
if ([identifier containsString:@"live-photo-bundle"]) {
// Handle live photos
identifier = @"public.jpeg";
identifier = UTTypeImage.identifier;
}

[provider loadFileRepresentationForTypeIdentifier:identifier completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) {
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];

assets[index] = [self mapImageToAsset:image data:data phAsset:asset];
assets[index] = [self mapImageToAsset:image data:data phAsset:asset metadata: nil];
dispatch_group_leave(completionGroup);
}];
} else if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeMovie]) {
[provider loadFileRepresentationForTypeIdentifier:(NSString *)kUTTypeMovie completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) {
} else if ([provider hasItemConformingToTypeIdentifier: UTTypeHEIC.identifier]) {
// No jpeg is present, but a heic is -- this is still an image, so we'll convert it
[provider loadFileRepresentationForTypeIdentifier: UTTypeHEIC.identifier completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) {
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];
[assets addObject:[self mapImageToAsset:image data:data phAsset:asset metadata: nil]];
dispatch_group_leave(completionGroup);
}];
} else if ([provider hasItemConformingToTypeIdentifier: UTTypeHEIF.identifier]) {
// No jpeg is present, but a heif is -- this is still an image, so we'll convert it
[provider loadFileRepresentationForTypeIdentifier: UTTypeHEIF.identifier completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) {
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];
[assets addObject:[self mapImageToAsset:image data:data phAsset:asset metadata: nil]];
dispatch_group_leave(completionGroup);
}];
} else if ([provider hasItemConformingToTypeIdentifier: UTTypeMovie.identifier]) {
[provider loadFileRepresentationForTypeIdentifier: UTTypeMovie.identifier completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) {
NSDictionary *mappedAsset = [self mapVideoToAsset:url phAsset:asset error:nil];
if (nil != mappedAsset) {
assets[index] = mappedAsset;
Expand Down

0 comments on commit 298e956

Please sign in to comment.