From ba37e3725ed45fe82c030af0c7465d6018e1bc48 Mon Sep 17 00:00:00 2001 From: Wojciech Lewicki Date: Thu, 9 Jan 2025 15:39:28 +0100 Subject: [PATCH] fix: dispatch callback after dismissing modal --- ...1.2+002+callback-in-completion-block.patch | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 patches/react-native-image-picker+7.1.2+002+callback-in-completion-block.patch diff --git a/patches/react-native-image-picker+7.1.2+002+callback-in-completion-block.patch b/patches/react-native-image-picker+7.1.2+002+callback-in-completion-block.patch new file mode 100644 index 000000000000..6011a582dcf5 --- /dev/null +++ b/patches/react-native-image-picker+7.1.2+002+callback-in-completion-block.patch @@ -0,0 +1,150 @@ +diff --git a/node_modules/react-native-image-picker/ios/ImagePickerManager.mm b/node_modules/react-native-image-picker/ios/ImagePickerManager.mm +index 93e99be..0ef2a8a 100644 +--- a/node_modules/react-native-image-picker/ios/ImagePickerManager.mm ++++ b/node_modules/react-native-image-picker/ios/ImagePickerManager.mm +@@ -506,81 +506,85 @@ @implementation ImagePickerManager (PHPickerViewControllerDelegate) + + - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray *)results API_AVAILABLE(ios(14)) + { +- [picker dismissViewControllerAnimated:YES completion:nil]; +- +- if (photoSelected == YES) { +- return; +- } +- photoSelected = YES; +- +- if (results.count == 0) { +- dispatch_async(dispatch_get_main_queue(), ^{ +- self.callback(@[@{@"didCancel": @YES}]); +- }); +- return; +- } ++ dispatch_block_t dismissCompletionBlock = ^{ + +- dispatch_group_t completionGroup = dispatch_group_create(); +- NSMutableArray *assets = [[NSMutableArray alloc] initWithCapacity:results.count]; +- for (int i = 0; i < results.count; i++) { +- [assets addObject:(NSDictionary *)[NSNull null]]; +- } ++ if (photoSelected == YES) { ++ return; ++ } ++ photoSelected = YES; + +- [results enumerateObjectsUsingBlock:^(PHPickerResult *result, NSUInteger index, BOOL *stop) { +- PHAsset *asset = nil; +- NSItemProvider *provider = result.itemProvider; ++ if (results.count == 0) { ++ dispatch_async(dispatch_get_main_queue(), ^{ ++ self.callback(@[@{@"didCancel": @YES}]); ++ }); ++ return; ++ } + +- // If include extra, we fetch the PHAsset, this required library permissions +- if([self.options[@"includeExtra"] boolValue] && result.assetIdentifier != nil) { +- PHFetchResult* fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[result.assetIdentifier] options:nil]; +- asset = fetchResult.firstObject; ++ dispatch_group_t completionGroup = dispatch_group_create(); ++ NSMutableArray *assets = [[NSMutableArray alloc] initWithCapacity:results.count]; ++ for (int i = 0; i < results.count; i++) { ++ [assets addObject:(NSDictionary *)[NSNull null]]; + } + +- dispatch_group_enter(completionGroup); ++ [results enumerateObjectsUsingBlock:^(PHPickerResult *result, NSUInteger index, BOOL *stop) { ++ PHAsset *asset = nil; ++ NSItemProvider *provider = result.itemProvider; + +- if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]) { +- 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"; ++ // If include extra, we fetch the PHAsset, this required library permissions ++ if([self.options[@"includeExtra"] boolValue] && result.assetIdentifier != nil) { ++ PHFetchResult* fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[result.assetIdentifier] options:nil]; ++ asset = fetchResult.firstObject; + } + +- [provider loadFileRepresentationForTypeIdentifier:identifier completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) { +- NSData *data = [[NSData alloc] initWithContentsOfURL:url]; +- UIImage *image = [[UIImage alloc] initWithData:data]; ++ dispatch_group_enter(completionGroup); + +- assets[index] = [self mapImageToAsset:image data:data phAsset:asset]; +- dispatch_group_leave(completionGroup); +- }]; +- } else if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeMovie]) { +- [provider loadFileRepresentationForTypeIdentifier:(NSString *)kUTTypeMovie completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) { +- NSDictionary *mappedAsset = [self mapVideoToAsset:url phAsset:asset error:nil]; +- if (nil != mappedAsset) { +- assets[index] = mappedAsset; ++ if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]) { ++ 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"; + } ++ ++ [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]; ++ dispatch_group_leave(completionGroup); ++ }]; ++ } else if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeMovie]) { ++ [provider loadFileRepresentationForTypeIdentifier:(NSString *)kUTTypeMovie completionHandler:^(NSURL * _Nullable url, NSError * _Nullable error) { ++ NSDictionary *mappedAsset = [self mapVideoToAsset:url phAsset:asset error:nil]; ++ if (nil != mappedAsset) { ++ assets[index] = mappedAsset; ++ } ++ dispatch_group_leave(completionGroup); ++ }]; ++ } else { ++ // The provider didn't have an item matching photo or video (fails on M1 Mac Simulator) + dispatch_group_leave(completionGroup); +- }]; +- } else { +- // The provider didn't have an item matching photo or video (fails on M1 Mac Simulator) +- dispatch_group_leave(completionGroup); +- } +- }]; ++ } ++ }]; + +- dispatch_group_notify(completionGroup, dispatch_get_main_queue(), ^{ +- // mapVideoToAsset can fail and return nil, leaving asset NSNull. +- for (NSDictionary *asset in assets) { +- if ([asset isEqual:[NSNull null]]) { +- self.callback(@[@{@"errorCode": errOthers}]); +- return; ++ dispatch_group_notify(completionGroup, dispatch_get_main_queue(), ^{ ++ // mapVideoToAsset can fail and return nil, leaving asset NSNull. ++ for (NSDictionary *asset in assets) { ++ if ([asset isEqual:[NSNull null]]) { ++ self.callback(@[@{@"errorCode": errOthers}]); ++ return; ++ } + } +- } + +- NSMutableDictionary *response = [[NSMutableDictionary alloc] init]; +- [response setObject:assets forKey:@"assets"]; ++ NSMutableDictionary *response = [[NSMutableDictionary alloc] init]; ++ [response setObject:assets forKey:@"assets"]; ++ ++ self.callback(@[response]); ++ }); ++ }; ++ ++ [picker dismissViewControllerAnimated:YES completion:dismissCompletionBlock]; + +- self.callback(@[response]); +- }); + } + + @end