Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for PHPickerViewController #1654

Merged
merged 17 commits into from May 8, 2021
Merged

Support for PHPickerViewController #1654

merged 17 commits into from May 8, 2021

Conversation

JanGorman
Copy link
Contributor

Motivation

This PR adds support for the iOS 14 PHPickerViewController to address Issue #1428

Test Plan

Tested using the included example app to load images/videos and verify that the output matches that of the original.


@import MobileCoreServices;

@interface ImagePickerManager ()

@property (nonatomic, strong) UIImagePickerController *picker;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The presenting view controller retains the picker so there's no requirement to hold on to it

@@ -198,12 +215,14 @@ - (void)checkCameraPermissions:(void(^)(BOOL granted))callback
if (status == AVAuthorizationStatusAuthorized) {
callback(YES);
return;
} else if (status == AVAuthorizationStatusNotDetermined){
}
else if (status == AVAuthorizationStatusNotDetermined){
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There was a mix of formatting

@Johan-dutoit
Copy link
Collaborator

Thanks for the PR, I'll see if I can squeeze in some testing soon and revert back.

@ravirajn22
Copy link
Contributor

@JanGorman I have added few comments, can you check it and revert with changes or your POV

@JanGorman
Copy link
Contributor Author

@ravirajn22 Thanks for the comments. Agreed! I factored out the common pieces

ios/ImagePickerManager.m Outdated Show resolved Hide resolved
@ravirajn22
Copy link
Contributor

Need to check the PR again, will update and push before this weekend.

@Johan-dutoit Johan-dutoit linked an issue Mar 25, 2021 that may be closed by this pull request
@Johan-dutoit
Copy link
Collaborator

I did some testing and mostly works okay.

I get the following error when quickly tapping an image twice

image

@JanGorman
Copy link
Contributor Author

@Johan-dutoit Thanks for the feedback. I'll see how to work around that

}

// Immediately dismiss the controller to prevent calling the completion handler twice
[picker dismissViewControllerAnimated:YES completion:nil];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Johan-dutoit This change here fixes the double tap issue in my tests

Copy link
Collaborator

Choose a reason for hiding this comment

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

It does a better job, but still fails if you click really fast.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You really want to try to break it 😅 I would suggest you make a call on how realistic this is (double tap speed vs. mouse clicks…). I don't see what other way there is to prevent this.

What's interesting is that it's reproducible (at least for me) only on the first double click. Subsequent attempts fail until you relaunch it. I'm able to reproduce this in a bare bones example and will report it as a bug to Apple.

class ViewController: UIViewController {
    @IBAction func pick(_ sender: Any) {
        let picker = PHPickerViewController(configuration: .init())
        picker.delegate = self
        present(picker, animated: true)
    }
}

extension ViewController: PHPickerViewControllerDelegate {
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true)
        // Prints twice the first time you double click really quickly
        print("Called")
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reported to Apple as FB9059292

Copy link
Collaborator

Choose a reason for hiding this comment

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

I totally agree with your sentiments above. At first I didn't even want to mention it, but decided that it's good to have a trail.

Choose a reason for hiding this comment

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

Shouldn't the call to dismissViewControllerAnimated be made before we check for results? That's what's suggested in the WWDC video about PHPicker, I'm wondering if that has incidence in the bug you're facing with double/triple taps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@flochtililoch Thanks for the comment. I made this change specifically to attempt to work around the mentioned bug. In my tests it didn't matter whether you call the completion block in the dismiss completion block or outside. It was always reproducible the first time that you present the picker but not on the second call which to me suggest that there is an issue.

Choose a reason for hiding this comment

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

Oh, what I meant was:

    // First, dismiss picker
    [picker dismissViewControllerAnimated:YES completion:nil];

    // Then, check results
    if (results.count == 0) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [picker dismissViewControllerAnimated:YES completion:^{
                self.callback(@[@{@"didCancel": @YES}]);
            }];
        });
        return;
    }
    ...

Another thing worthwhile to explore, is around setting the flag preferredAssetRepresentationMode on the configuration object to PHPickerConfigurationAssetRepresentationModeCurrent. This blog article suggests that not setting it can have a performance impact on the import process.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's give this a go, would love to not face this :D

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I moved it to the first line of the delegate callback. The double tap problem persists but in terms of consistency I guess this is better (only one dismiss instead of separate handling for the "cancel" case).
I also applied the suggestion to set the preferredAssetRepresentationMode

@Johan-dutoit
Copy link
Collaborator

if @ravirajn22 is happy with the code, I'm happy to give the thumbs up.

@ravirajn22
Copy link
Contributor

Sorry for the delay, haven't looked into the code. Give me few more days.

@Sven65
Copy link

Sven65 commented Apr 7, 2021

Does this PR allow users of the library to set the selection limit, in order to select multiple images at once?

@JanGorman
Copy link
Contributor Author

@Sven65 It does not. The library currently doesn't offer this option either. It's straightforward to add that option but IMO better done in a separate PR

@JanGorman
Copy link
Contributor Author

@ravirajn22 Have you had chance to review? If there's anything I can do to help unblock the PR please let me know 🙂

@Johan-dutoit
Copy link
Collaborator

Johan-dutoit commented Apr 26, 2021

@ravirajn22 last chances for a review, going to merge this today, unless there are any objections.

@ravirajn22
Copy link
Contributor

@Johan-dutoit I will review and tell before tomorrow. This change affects many lines including styling and refactor of unrelated lines. I don't know if its the right way to add feature + refactor together. Just give some time.

@ravirajn22
Copy link
Contributor

@JanGorman I have refactored your code, it now uses one function to process image (same for video), reducing duplicate codes and fixed few issues. Your code did not handle image types other than jpg and png. Please review this.

@Johan-dutoit Test this code and release a beta version if everything ok.

@ravirajn22
Copy link
Contributor

I did some testing and mostly works okay.

I get the following error when quickly tapping an image twice

image

Regarding this error, we can handle this in another PR. Just setting the callback to nil after sending response and checking if callback != nil before sending the response should fix this issue. This issue exist even in current production build I think.


typedef NS_ENUM(NSInteger, RNImagePickerTarget) {
camera = 1,
library
};

@interface ImagePickerManager : NSObject <RCTBridgeModule, UINavigationControllerDelegate, UIActionSheetDelegate, UIImagePickerControllerDelegate>
@interface ImagePickerManager : NSObject <RCTBridgeModule, UINavigationControllerDelegate, UIActionSheetDelegate, UIImagePickerControllerDelegate, PHPickerViewControllerDelegate>
Copy link
Contributor Author

@JanGorman JanGorman Apr 27, 2021

Choose a reason for hiding this comment

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

@ravirajn22 Out of curiosity, why do you prefer declaring this on the public interface? For one, the outside world doesn't need to know that it conforms to the interface (personally I'd move UIActionSheetDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate into the .m file too). This was one of the benefits that Obj-C has over Swift, that you can move the #import into the .m file making the link phase faster

Copy link
Contributor

Choose a reason for hiding this comment

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

I am no expert in objective c, so l like consistency over others. Since all previous protocols have been done on the public interface as you said, I thought its better if we can declare PHPickerViewControllerDelegate the same way.

Refactors can go in another PR or with smaller PR, not with big PR like this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Understood! Then I'll gladly change this in a follow-up PR 👍


- (void)onImageObtained:(UIImage*)image data:(NSData*)data
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Come to think of it, we don't necessarily need to pass the UIImage here. We could

UIImage *image = [[UIImage alloc] initWithData:data];

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fine to put this into a follow-up PR as well imo

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes ideally it should be done as you said. But in UIImagePickerController there is something called edited UIIImage and original UIImage. But the data we obtain is only from original image, you cannot get edited image data. So if the user edited the image then we need to process further using the edited UIImage and use the data from original image to just obtain image type and save it directly for GIF type. I am not sure if I am explaining properly.

This is how previous code has been working so I just used it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll look into it. Should be possible to figure out the type and do the editing as you said

@JanGorman
Copy link
Contributor Author

JanGorman commented Apr 30, 2021

LGTM. As discussed in the PR I'll follow up with a refactoring to move protocol conformance into categories akin to class extensions in Swift

@Johan-dutoit
Copy link
Collaborator

Thanks @JanGorman, I'll do some basic testing locally and get this merged.

@JanGorman
Copy link
Contributor Author

@Johan-dutoit Is there anything left that I can help with to merge? :)

@Johan-dutoit Johan-dutoit merged commit 26eee5d into react-native-image-picker:main May 8, 2021
Johan-dutoit pushed a commit that referenced this pull request May 8, 2021
# [3.6.0](v3.5.0...v3.6.0) (2021-05-08)

### Features

* add support for PHPickerViewController  ([#1654](#1654)) ([26eee5d](26eee5d))
@Johan-dutoit
Copy link
Collaborator

🎉 This PR is included in version 3.6.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@gabrieldonadel
Copy link
Contributor

Does this support the durationLimit param?

MattDennyir pushed a commit to MattDennyir/react-native-image-picker that referenced this pull request Oct 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for PHPicker in iOS 14
6 participants