Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[image_picker] Default to gallery instead of camera when picking multiple images on pre-iOS 14 devices. #4718

Merged
merged 22 commits into from
Feb 10, 2022

Conversation

mvanbeusekom
Copy link
Contributor

@mvanbeusekom mvanbeusekom commented Feb 1, 2022

Solves two problems when using the ImagePicker.pickMultiImage method on pre-iOS 14 devices:

  1. Picking multiple images now defaults to selecting images from the gallery instead of opening the camera (this is expected behaviour as it is not possible to select multiple images from the camera, see also issue [image_picker] _picker.pickMultiImage launches camera instead of gallery on IOS 12 flutter#96903);
  2. Make sure the supplied arguments and result callback are made available to the UIImagePicker when picking images on devices running iOS 13 and lower.

Resolves flutter/flutter#96903
Replaces and closes #4706

EDIT: Converted this PR to draft as it depends on changes from PR #4722.

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [relevant style guides] and ran [the auto-formatter]. (Unlike the flutter/flutter repo, the flutter/plugins repo does use dart format.)
  • I signed the [CLA].
  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I listed at least one issue that this PR fixes in the description above.
  • I updated pubspec.yaml with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes].
  • I updated CHANGELOG.md to add a description of the change, [following repository CHANGELOG style].
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • All existing and new tests are passing.

@mvanbeusekom
Copy link
Contributor Author

Hi @stuartmorgan,

I fully agree with your comments and I have been struggling with this too. Ideally we'd separate the method channel implementation from the code that interacts directly with the UIImagePicker and PHPicker controls into separate classes. For example:

  • A MethodChannelHandler class, responsible for communicating with the Flutter method channel and parsing arguments into strongly typed structures;
  • A FLTImagePicker class, abstraction around the UIImagePicker and PHPicker controls exposing methods and callbacks accepting only strongly type and clearly named parameters.

This would make testing the API surface between Dart and Native a lot easier. However this is also a large refactor. My reasoning was to solve this bug now by not introducing new parameters, ivars or logic but instead simply fixing the existing implementation (and add some tests to ensure it works) and discuss a bigger refactor with you offline.

@stuartmorgan-g
Copy link
Contributor

Ideally we'd separate the method channel implementation from the code that interacts directly with the UIImagePicker and PHPicker controls into separate classes.

Sounds like a good way to go longer term, but it shouldn't actually be necessary for what I'm suggesting. Look at the local_auth iOS tests for instance, which have a single class but still do method-call-to-mock-system-API testing.

My reasoning was to solve this bug now by not introducing new parameters, ivars or logic

Well, there's obviously new logic here. But I'm fine with the short term fix itself being pretty minimal (the non-tests changes I asked for should be quite small), the issue is that the change you made to the tests make the code worse. They make fixing the root problem harder, because they enshrine and encourage an anti-pattern. Small incremental changes are fine but they need to take us in the right direction.

but instead simply fixing the existing implementation (and add some tests to ensure it works)

I think "ensure it works" is highly arguable. As an experiment I tried moving self.arguments = call.arguments; to the end of handleMethodCall:... (where it would do absolutely nothing), and 2/3 of your new tests still pass.

They ensure that part of the anti-pattern is being followed at some point, but not that it's the right point, or that it's used correctly.

Refactored the existing unit tests to expose private interfaces in a
separate test header instead of an inline interface. This was first
introduced in the google_sign_in plugin (flutter#4157) and after that also
applied in the camera plugin (flutter#4426) and the webview_flutter plugin
(flutter#4480).
@mvanbeusekom mvanbeusekom marked this pull request as draft February 3, 2022 16:26
@mvanbeusekom mvanbeusekom marked this pull request as ready for review February 9, 2022 10:58
@@ -146,6 +146,32 @@ - (void)testPluginPickVideoDeviceFront {
UIImagePickerControllerCameraDeviceFront);
}

- (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 {
if (@available(iOS 14, *)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmagman Relevant to our discussions about test matrixes in this repository: we won't ever actually test this in CI currently.

_imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
_imagePickerController.delegate = self;
_imagePickerController.mediaTypes = @[
UIImagePickerController *imagePickerController = [self createImagePickerController];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be self.imagePickerController?

If we need a stateful controller, I'm very concerned about our test coverage if this didn't break anything. Does it not actually need to be stateful?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With some refactoring it is not necessary to keep the imagePickerController as state. I did the refactoring necessary which involves passing the current copy of the imagePickerController around to some other methods. Please have a look if you agree with this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thanks! (Now we just need to do the same with arguments, and ideally result, but those are definitely out of scope.)

if (image == nil) {
image = [info objectForKey:UIImagePickerControllerOriginalImage];
image = info[UIImagePickerControllerOriginalImage];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this everywhere :)

Copy link
Contributor

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with nits, thanks for all the iteration and test work here!

_imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
_imagePickerController.delegate = self;
_imagePickerController.mediaTypes = @[
UIImagePickerController *imagePickerController = [self createImagePickerController];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thanks! (Now we just need to do the same with arguments, and ideally result, but those are definitely out of scope.)

@mvanbeusekom mvanbeusekom added the waiting for tree to go green (Use "autosubmit") This PR is approved and tested, but waiting for the tree to be green to land. label Feb 10, 2022
@fluttergithubbot fluttergithubbot merged commit aa36a8a into flutter:main Feb 10, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Feb 10, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Feb 11, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Feb 11, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Feb 14, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
p: image_picker platform-ios waiting for tree to go green (Use "autosubmit") This PR is approved and tested, but waiting for the tree to be green to land.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[image_picker] _picker.pickMultiImage launches camera instead of gallery on IOS 12
3 participants