diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 58dae8e1b635..d049de0ffcdc 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.13+7 + +* Fixes early dismissal of image picker. + ## 0.8.13+6 * Replaces deprecated `kUTTypeGIF` with `UTTypeGIF` to fix iOS 15+ deprecation warnings. diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index 734c0df56bf6..66d45ebe48f8 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -323,6 +323,67 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { [plugin imagePickerControllerDidCancel:controller]; } +- (void)testCancelResultIsNotSentUntilDismissalCompletes { + FLTImagePickerPlugin *plugin = + [[FLTImagePickerPlugin alloc] initWithViewProvider:[[StubViewProvider alloc] init]]; + __block NSArray *pickResult = nil; + plugin.callContext = [[FLTImagePickerMethodCallContext alloc] + initWithResult:^(NSArray *_Nullable result, FlutterError *_Nullable error) { + pickResult = result; + }]; + + __block void (^dismissCompletion)(void) = nil; + id mockPicker = OCMClassMock([UIImagePickerController class]); + OCMStub([mockPicker + dismissViewControllerAnimated:YES + completion:[OCMArg checkWithBlock:^BOOL(void (^completion)(void)) { + dismissCompletion = [completion copy]; + return YES; + }]]); + + [plugin imagePickerControllerDidCancel:mockPicker]; + + XCTAssertNotNil(dismissCompletion); + XCTAssertNil(pickResult, @"The cancel result should not be sent before dismissal completes."); + + dismissCompletion(); + + XCTAssertEqualObjects(pickResult, @[]); +} + +- (void)testPickResultIsNotSentUntilDismissalCompletes { + FLTImagePickerPlugin *plugin = + [[FLTImagePickerPlugin alloc] initWithViewProvider:[[StubViewProvider alloc] init]]; + __block NSArray *pickResult = nil; + plugin.callContext = [[FLTImagePickerMethodCallContext alloc] + initWithResult:^(NSArray *_Nullable result, FlutterError *_Nullable error) { + pickResult = result; + }]; + + // Capture the dismissal completion instead of running it, so the test controls + // when dismissal "finishes". + __block void (^dismissCompletion)(void) = nil; + id mockPicker = OCMClassMock([UIImagePickerController class]); + OCMStub([mockPicker + dismissViewControllerAnimated:YES + completion:[OCMArg checkWithBlock:^BOOL(void (^completion)(void)) { + dismissCompletion = [completion copy]; + return YES; + }]]); + + UIImage *image = [UIImage imageWithData:ImagePickerTestImages.JPGTestData]; + [plugin imagePickerController:mockPicker + didFinishPickingMediaWithInfo:@{UIImagePickerControllerOriginalImage : image}]; + + XCTAssertNotNil(dismissCompletion); + XCTAssertNil(pickResult, @"The picked media should not be sent before dismissal completes."); + + dismissCompletion(); + + XCTAssertEqual(pickResult.count, 1); + XCTAssertGreaterThan(pickResult.firstObject.length, 0); +} + - (void)testCameraPickerInteractionBlockerWindowIsAddedAndRemoved { id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]); diff --git a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m index 82991980d0c7..04442a037574 100644 --- a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m @@ -533,12 +533,18 @@ - (void)picker:(PHPickerViewController *)picker - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { - NSURL *videoURL = info[UIImagePickerControllerMediaURL]; + FLTImagePickerMethodCallContext *context = self.callContext; __weak typeof(self) weakSelf = self; [picker dismissViewControllerAnimated:YES completion:^{ [weakSelf removeInteractionBlocker]; + if (weakSelf.callContext == context) { + [weakSelf sendCallResultWithPickerInfo:info]; + } }]; +} + +- (void)sendCallResultWithPickerInfo:(NSDictionary *)info { // The method dismissViewControllerAnimated does not immediately prevent // further didFinishPickingMediaWithInfo invocations. A nil check is necessary // to prevent below code to be unwantly executed multiple times and cause a @@ -546,6 +552,7 @@ - (void)imagePickerController:(UIImagePickerController *)picker if (!self.callContext) { return; } + NSURL *videoURL = info[UIImagePickerControllerMediaURL]; if (videoURL != nil) { if (@available(iOS 13.0, *)) { NSURL *destination = [FLTImagePickerPhotoAssetUtil saveVideoFromURL:videoURL]; @@ -624,12 +631,15 @@ - (void)imagePickerController:(UIImagePickerController *)picker } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { + FLTImagePickerMethodCallContext *context = self.callContext; __weak typeof(self) weakSelf = self; [picker dismissViewControllerAnimated:YES completion:^{ [weakSelf removeInteractionBlocker]; + if (weakSelf.callContext == context) { + [weakSelf sendCallResultWithSavedPathList:nil]; + } }]; - [self sendCallResultWithSavedPathList:nil]; } #pragma mark - diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 076169304097..11d0cd04e4f9 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the image_picker plugin. repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.13+6 +version: 0.8.13+7 environment: sdk: ^3.10.0