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

[image_picker] add requestFullMetadata for iOS (optional permissions) #4638

Closed

Conversation

PiotrMitkowski
Copy link
Contributor

Description

Originally #3264 made by @cpboyd

Make iOS 11+ permissions requests optional per https://developer.apple.com/forums/thread/653414.
PHAssets aren't actually required and there was already some fallback code in place.

Related issues

Fixes flutter/flutter#65995

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.

@godofredoc godofredoc changed the base branch from master to main January 6, 2022 22:42
…-permissions

� Conflicts:
�	packages/image_picker/image_picker_platform_interface/CHANGELOG.md
�	packages/image_picker/image_picker_platform_interface/pubspec.yaml
@stuartmorgan stuartmorgan self-requested a review January 20, 2022 21:04
Copy link
Contributor

@stuartmorgan stuartmorgan left a comment

Choose a reason for hiding this comment

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

Where are the iOS changes? This PR adds all the plumbing, but has no effect on any platform.

packages/image_picker/image_picker/lib/image_picker.dart Outdated Show resolved Hide resolved
@@ -191,6 +191,13 @@ class ImagePicker {
/// the front or rear camera should be opened, this function is not guaranteed
/// to work on an Android device.
///
/// Use `forceFullMetadata` (defaults to `true`) to control, how much additional information the plugin tries to get on iOS.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: remove the comma after "control".

@@ -191,6 +191,13 @@ class ImagePicker {
/// the front or rear camera should be opened, this function is not guaranteed
/// to work on an Android device.
///
/// Use `forceFullMetadata` (defaults to `true`) to control, how much additional information the plugin tries to get on iOS.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: This shouldn't specifically say it's for iOS, as it might apply to other platforms in the future. I would remove iOS here, and add it to the sentence below (e.g., "... which may require extra permission requests on some platforms, such as iOS").

@@ -1,3 +1,8 @@
## 2.5.0

* Re-adding `forceFullMetadata` option, but as a parameter of a new `pickImageFromSource`
Copy link
Contributor

Choose a reason for hiding this comment

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

Re-adds

(Per style guide)

## 2.5.0

* Re-adding `forceFullMetadata` option, but as a parameter of a new `pickImageFromSource`
method (non-breaking change approach).
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need to explain in the changelog that it's non-breaking; that's indicated by the version.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I only wanted to explicitly show the difference from previous implementation

@@ -270,4 +272,24 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
files: pickedFileList,
);
}

@override
Future<XFile?> getImageFromSource({
Copy link
Contributor

Choose a reason for hiding this comment

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

Please put this next to getImage

@@ -146,6 +146,8 @@ abstract class ImagePickerPlatform extends PlatformInterface {
throw UnimplementedError('retrieveLostData() has not been implemented.');
}

/// This method is deprecated in favor of [getImageFromSource] and will be removed in a future update
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Missing final period.

Copy link
Contributor

@stuartmorgan stuartmorgan left a comment

Choose a reason for hiding this comment

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

The iOS changes need unit tests that the parameter is actually honored (since this flow can't use integration tests). Right now all of the iOS code here could be broken or removed, and no tests would fail, which means there's no meaningful testing.

@@ -97,7 +97,12 @@ - (void)pickImageWithPHPicker:(int)maxImagesAllowed API_AVAILABLE(ios(14)) {

self.maxImagesAllowed = maxImagesAllowed;

[self checkPhotoAuthorizationForAccessLevel];
BOOL usePhaAsset = [[_arguments objectForKey:@"requestFullMetadata"] boolValue];
Copy link
Contributor

Choose a reason for hiding this comment

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

The type is PHAsset, not PHAAsset, and also ObjC style is to capitalize abbreviations even in variable names, so this, and the similar variables below, should be usePHAsset rather than usePhaAsset.

@@ -191,6 +191,10 @@ class ImagePicker {
/// the front or rear camera should be opened, this function is not guaranteed
/// to work on an Android device.
///
/// Use `requestFullMetadata` (defaults to `true`) to control how much additional information the plugin tries to get on iOS.
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove the "on iOS" here.

@PiotrMitkowski
Copy link
Contributor Author

@stuartmorgan can you give me a little hint on the iOS tests? I see two expected results of having the requestFullMetadata flag set to false:

  1. when running pickImage method call, plugin's method checkPhotoAuthorization should not be called
  2. after picking an image, plugin's method saveImageWithPickerInfo should be called

For now I don't see a way to check if any of those happened in the iOS unit tests.

@stuartmorgan
Copy link
Contributor

I see two expected results of having the requestFullMetadata flag set to false:

Those aren't expected results, they are internal implementation details. Expected results here would be expressed in terms of the iOS APIs that are or are not called, which would be checked by mocking them.

…ermissions

� Conflicts:
�	packages/image_picker/image_picker/CHANGELOG.md
�	packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m
�	packages/image_picker/image_picker/pubspec.yaml
@PiotrMitkowski PiotrMitkowski requested a review from cyanglaz April 28, 2022 09:43
@override
Future<XFile?> getImageFromSource({
required ImageSource source,
ImagePickerOptions? options,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can option parameter be non-null and defaults to default options? (const ImagePickerOptions())

/// If no images were picked, the return value is null.
Future<XFile?> getImageFromSource({
required ImageSource source,
ImagePickerOptions? options,
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto: can we make options non-null?

///
/// If no images were picked, the return value is null.
Future<XFile?> getImageFromSource({
required ImageSource source,
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the reason behind making source not a part of ImagePickerOptions? Can we also move source to ImagePickerOptions?

Copy link
Contributor Author

@PiotrMitkowski PiotrMitkowski Apr 29, 2022

Choose a reason for hiding this comment

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

This parameter is needed whenever the picking method is called. All parameters inside ImagePickerOptions are more optional, so the whole options object can be skipped if a caller doesn't need any specific adjustments. It was @stuartmorgan's suggestion in this PR that I've agreed with, as calling the picking method doesn't require creating the options object for every use

@PiotrMitkowski PiotrMitkowski requested a review from cyanglaz April 29, 2022 07:43
Copy link
Contributor

@cyanglaz cyanglaz left a comment

Choose a reason for hiding this comment

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

LGTM, @stuartmorgan Do you mind taking another look before we update the path dependency?

Copy link
Contributor

@stuartmorgan stuartmorgan left a comment

Choose a reason for hiding this comment

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

I have some documentation feedback and minor nits, but otherwise this looks good. @PiotrMitkowski please go ahead and open the platform interface PR (with the platform interface parts of my comments below addressed) and we can get that a quick final review and land it to start unwinding the full PR.

this.requestFullMetadata = true,
});

/// If specified, the image will be at most `maxWidth` wide. Otherwise,
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need to include the name of a field in the field comment name (as you do in the context of a method comment, where this was taken from). Also, the second sentence isn't true once you split it apart into two different comments for the two fields, because if one maximum is specified, it'll be scaled to fit that max which changes the other dimension. So it should be:

The maximum width of the image, in pixels. If null, the image will only be resized if maxHeight is specified.

final double? maxWidth;

/// If specified, the image will be at most`maxHeight` tall.
/// Otherwise the image will be returned at its original height.
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here.

final double? maxHeight;

/// Modifies the quality of the image, ranging from 0-100 where 100 is the original/max
/// quality. If `imageQuality` is null, the image with the originalquality will
Copy link
Contributor

Choose a reason for hiding this comment

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

"If null, the image will be returned at its original quality."

Copy link
Contributor

Choose a reason for hiding this comment

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

/// Specifies options for picking a single image from the device's camera or gallery.
class ImagePickerOptions {
/// Creates an instance with the given [maxHeight], [maxWidth], [imageQuality],
/// [referredCameraDevice] and [requestFullMetadata]. Any of the params may be null.
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove "Any of the params may be null"; nullability is part of types now, and doesn't need to be commented.

/// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery].
/// It is also ignored if the chosen camera is not supported on the device.
/// Defaults to [CameraDevice.rear]. Note that Android has no documented parameter
/// for an intent to specify if the front or rear camera should be opened, this
Copy link
Contributor

Choose a reason for hiding this comment

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

We try to avoid commenting about specific platforms in the platform interface (other than as illustrative examples). You can just remove this whole sentence since it's a minor detail that callers at the platform interface level aren't going to need to know.

final int? imageQuality;

/// Used to specify the camera to use when the `source` is [ImageSource.camera].
/// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery].
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove this sentence, and add a blank line.


/// Used to specify the camera to use when the `source` is [ImageSource.camera].
/// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery].
/// It is also ignored if the chosen camera is not supported on the device.
Copy link
Contributor

Choose a reason for hiding this comment

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

"Ignored if the source is not [ImageSource.camera], or the chosen camera is not supported on the device."

/// function is not guaranteed to work on an Android device.
final CameraDevice preferredCameraDevice;

/// `requestFullMetadata` defaults to `true`, so the plugin tries to get the
Copy link
Contributor

Choose a reason for hiding this comment

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

A field comment should lead with what it's for, not what the default is. How about:

If true, requests full image metadata, which may require extra permissions on some platforms, (e.g., NSPhotoLibraryUsageDescription on iOS).

Defaults to true.

@ObjCSelector('pickImageWithSource:maxSize:quality:')
String? pickImage(
SourceSpecification source, MaxSize maxSize, int? imageQuality);
@ObjCSelector('pickImageWithSource:maxSize:quality:requestFullMetadata:')
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: this param would be more idiomatic in ObjC as just fullMetadata:; the "with" is implied to go with following arguments, and "with full metadata" reads much better than "with request full metadata".

…ermissions

� Conflicts:
�	packages/image_picker/image_picker/CHANGELOG.md
�	packages/image_picker/image_picker/pubspec.yaml
�	packages/image_picker/image_picker_ios/CHANGELOG.md
�	packages/image_picker/image_picker_ios/pubspec.yaml
�	packages/image_picker/image_picker_platform_interface/CHANGELOG.md
�	packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart
�	packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart
�	packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart
}) {
return platform.getImage(
return platform.getImageFromSource(
Copy link
Contributor

Choose a reason for hiding this comment

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

You need to require the version of the platform interface that has this method in pubspec.yaml.

maxHeight: maxHeight,
imageQuality: imageQuality,
preferredCameraDevice: preferredCameraDevice,
requestFullMetadata: requestFullMetadata,
Copy link
Contributor

Choose a reason for hiding this comment

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

It would also be better to pull the iOS changes into a separate PR and land it first so that you can require that version, otherwise someone who only updates this package will not actually get a working implementation of the new parameter.

@@ -57,14 +57,16 @@ void main() {
'maxWidth': null,
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'requestFullMetadata': true,
Copy link
Contributor

Choose a reason for hiding this comment

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

You'll need to reconcile the test changes against #5706 once that lands (which is needed to resolve the current tree breakage, so will be before this PR can land).

@@ -2,7 +2,7 @@ name: image_picker_ios
description: iOS implementation of the video_picker plugin.
repository: https://github.com/flutter/plugins/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.5+2
version: 0.8.6

Copy link
Contributor

Choose a reason for hiding this comment

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

This also needs to require the right version of the platform interface.

…ermissions

� Conflicts:
�	packages/image_picker/image_picker/CHANGELOG.md
�	packages/image_picker/image_picker/lib/image_picker.dart
�	packages/image_picker/image_picker/pubspec.yaml
�	packages/image_picker/image_picker/test/image_picker_deprecated_test.dart
�	packages/image_picker/image_picker/test/image_picker_test.dart
@PiotrMitkowski
Copy link
Contributor Author

I closed this PR by a mistake today, so I've created a new one: #5915

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[image_picker] choosing Select Photos permission shows all photos
4 participants