Skip to content

Commit 8859021

Browse files
raju-muliyashiyaRaju
authored andcommitted
Address gemini comments
1 parent f6c1c9e commit 8859021

File tree

2 files changed

+43
-41
lines changed

2 files changed

+43
-41
lines changed

packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -704,47 +704,40 @@ - (void)testPickMultiVideoWithoutLimit {
704704
- (void)testUIImagePickerImmediateCloseReturnsEmptyArray {
705705
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
706706

707-
XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result"];
708-
709-
FLTImagePickerMethodCallContext *context = [[FLTImagePickerMethodCallContext alloc]
710-
initWithResult:^void(NSArray<NSString *> *paths, FlutterError *error) {
711-
if (paths == nil || paths.count == 0) {
712-
XCTAssertNil(error);
713-
[resultExpectation fulfill];
714-
}
715-
}];
716-
context.includeImages = YES;
717-
context.maxSize = [[FLTMaxSize alloc] init];
718-
context.maxItemCount = 1;
719-
context.requestFullMetadata = NO;
720-
721-
plugin.callContext = context;
722-
723707
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
724-
UIView *controllerView = controller.view;
725-
726-
UIView *observerView = [[UIView alloc] init];
727-
[controllerView addSubview:observerView];
728-
729-
void (^removeCallback)(void) = ^{
730-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
731-
dispatch_get_main_queue(), ^{
732-
if (plugin && plugin.callContext == context && !plugin.isProcessingSelection) {
733-
[plugin sendCallResultWithSavedPathList:nil];
734-
}
735-
});
736-
};
708+
[plugin setImagePickerControllerOverrides:@[ controller ]];
737709

738-
UIWindow *testWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
739-
testWindow.hidden = NO;
740-
[testWindow addSubview:controllerView];
710+
// Mock camera access to avoid permission dialogs and device-specific logic.
711+
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
712+
OCMStub(ClassMethod([mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
713+
.andReturn(YES);
714+
OCMStub(ClassMethod([mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]))
715+
.andReturn(YES);
716+
id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
717+
OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
718+
.andReturn(AVAuthorizationStatusAuthorized);
741719

742-
[testWindow setNeedsLayout];
743-
[testWindow layoutIfNeeded];
720+
XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result"];
744721

745-
[controllerView removeFromSuperview];
722+
FLTSourceSpecification *source = [FLTSourceSpecification makeWithType:FLTSourceTypeCamera
723+
camera:FLTSourceCameraRear];
724+
[plugin pickImageWithSource:source
725+
maxSize:[[FLTMaxSize alloc] init]
726+
quality:nil
727+
fullMetadata:NO
728+
completion:^(NSString *_Nullable result, FlutterError *_Nullable error) {
729+
XCTAssertNil(result);
730+
XCTAssertNil(error);
731+
[resultExpectation fulfill];
732+
}];
746733

747-
removeCallback();
734+
// The `pickImage` call will attach the observer. Now, simulate dismissal.
735+
// This needs to happen on the next run loop to ensure the observer is attached.
736+
dispatch_async(dispatch_get_main_queue(), ^{
737+
UIWindow *testWindow = [[UIWindow alloc] init];
738+
[testWindow addSubview:controller.view];
739+
[controller.view removeFromSuperview];
740+
});
748741

749742
[self waitForExpectationsWithTimeout:1.0 handler:nil];
750743
}

packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,22 @@ - (instancetype)initWithResult:(nonnull FlutterResultAdapter)result {
2828
@end
2929

3030
/**
31-
* a callback function what the PickerViewController remove from window.
31+
* A callback function that is invoked when the PickerViewController is removed from the window.
32+
* This callback is used to handle cleanup operations and notify the plugin when the picker
33+
* interface has been dismissed, either through user interaction or programmatically.
3234
*/
3335
typedef void (^FLTImagePickerRemoveCallback)(void);
3436

3537
/**
36-
* Add the view to the PickerViewController's view, observing its window to observe the window of
37-
* PickerViewController. This is to prevent PickerViewController from being removed from the screen
38-
* without receiving callback information under other circumstances, such as being interactively
39-
* dismissed before PickerViewController has fully popped up.
38+
* A UIView subclass that monitors the removal of a PickerViewController from the window hierarchy.
39+
*
40+
* This observer view is added to the PickerViewController's view and monitors changes to its window
41+
* property. When the PickerViewController is removed from the screen (either through user dismissal
42+
* or programmatic dismissal), this view detects the change and triggers the associated callback.
43+
*
44+
* This mechanism ensures that the plugin receives notification when the picker is dismissed under
45+
* various circumstances, including interactive dismissal gestures that occur before the
46+
* PickerViewController has fully appeared on screen.
4047
*/
4148
@interface FLTImagePickerRemoveObserverView : UIView
4249

@@ -200,6 +207,8 @@ - (void)bindRemoveObserver:(nonnull UIViewController *)controller
200207
FLTImagePickerRemoveObserverView *removeObserverView =
201208
[[FLTImagePickerRemoveObserverView alloc] initWithRemoveCallback:^{
202209
__strong typeof(weakSelf) strongSelf = weakSelf;
210+
// Add a small delay to ensure delegate methods have a chance to run first
211+
// This prevents the observer from firing during normal selection flow
203212
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
204213
dispatch_get_main_queue(), ^{
205214
if (strongSelf && strongSelf.callContext == context &&

0 commit comments

Comments
 (0)