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

Share PHAsset #634

Closed
RocKhalil opened this issue Nov 15, 2019 · 12 comments
Closed

Share PHAsset #634

RocKhalil opened this issue Nov 15, 2019 · 12 comments
Labels
help wanted os: ios question stale There has been a lack of activity on this issue and it may be closed soon.

Comments

@RocKhalil
Copy link

RocKhalil commented Nov 15, 2019

About

I'm trying to share images from my CameraRoll using @react-native-community/react-native-share and @react-native-community/cameraroll

Code Preview

import CameraRoll from '@react-native-community/cameraroll'
import Share from 'react-native-share'

const photos = await CameraRoll.getPhotos({
  first: 10,
  assetType: 'All',
})

const photo = photos[0]

const shareOptions = {
  url: photo.image.uri,
  type: photo.type,
  title: 'My Photo',
  subject: 'Check out what I found!'
}

Share.open(shareOptions)

This isn't the actual one, but works fine for demo purposes.

Question

On android, it's working perfectly. However, on iOS, we get from photo.image.uri a PHAsset url of the asset.
Right now, when the share dialog opens, we can't see the file that is being shared and we only see 001 which is the PHAsset's last file path.
Is there any way to share the ph://.... image or video directly ?

@jgcmarins
Copy link
Member

Hello @RocKhalil, it should be possible.
But maybe is there a way to tell react-native-cameraroll to return another type for photo.image.uri

@RocKhalil
Copy link
Author

RocKhalil commented Nov 15, 2019

@jgcmarins I wrote a small hack for it right now (within the cameraroll) in order to keep going with the development; but I'll try writing a fix to allow ph asset to be read directly.

Maybe you can get inspired by this for the react-native-share updates

if ([assetMediaTypeLabel isEqual:@"image"]) {
  [asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
    url = contentEditingInput.fullSizeImageURL.absoluteString;
  }];
} else {
  [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:nil resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
    AVURLAsset *urlAsset = (AVURLAsset*)asset;
    url = [[urlAsset URL] absoluteString];
  }];
}

do { } while( [url isEqual:@""] );

I know that the code is not performance oriented, but I need to move on from this task and will get back to this one as soon as possible.

@jgcmarins
Copy link
Member

@RocKhalil great to hear that.
Pull requests are welcome.
Feel free to open one with this improvement. I can help with code review.

@RocKhalil
Copy link
Author

@jgcmarins I tried adding the above piece of code to RNShare directly; here's where I currently stand:

#import <Photos/Photos.h>

RCT_EXPORT_METHOD(open:(NSDictionary *)options
                  failureCallback:(RCTResponseErrorBlock)failureCallback
                  successCallback:(RCTResponseSenderBlock)successCallback)
{
  ....
  if ([URL.scheme.lowercaseString isEqualToString:@"data"]) {
    ...
  } else if ([URL.scheme.lowercaseString isEqualToString:@"ph"]) {
    NSString *assetIdentifier = [urlsArray[i] stringByReplacingOccurrencesOfString: @"ph://" withString: @""];
    PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers: @[assetIdentifier] options:nil];
    PHAsset *asset = fetchResult.firstObject;
    NSString __block *url = @"";

    if (asset){
      switch (asset.mediaType) {
        case PHAssetMediaTypeVideo:
          [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:nil resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
            AVURLAsset *urlAsset = (AVURLAsset*)asset;
            url = [[urlAsset URL] absoluteString];
          }];
          break;
        case PHAssetMediaTypeImage:
           [asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
                 url = contentEditingInput.fullSizeImageURL.absoluteString;
               }
            ];
          break;
        default:
          RCTLogError(@"Asset type can't be shared");
          return;
      }

      do { } while( [url isEqual:@""] );
      [items addObject: url];
    }
  } else {
     [items addObject:URL];
  }

  ....
}

Now the issue is that do { } while( [url isEqual:@""] ) is executed on the main_queue and the app is freezing. I'm not that advanced is Objective-C and not sure how to await a block for completing instead of doing this method.

If you can help me with this, I'll test the solution and create a PR asap.

Thanks :-)

@RocKhalil
Copy link
Author

RocKhalil commented Dec 16, 2019

@jgcmarins the previous answer had some issues (not sharing correctly), so I wrote a better fix for PHAsset sharing:

#import <UIKit/UIWindow.h>
#import <UIKit/UIActivityViewController.h>

RCT_REMAP_METHOD(getShareModal, assetURI:(NSString *)assetURI)
{
  NSMutableArray *activityItems = [NSMutableArray array];

  // image Loader
  PHImageManager *im = [PHImageManager defaultManager];
  PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
  options.synchronous = YES;
  options.version = PHImageRequestOptionsVersionCurrent;
  options.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic;
  options.resizeMode = PHImageRequestOptionsResizeModeNone;
  
  PHFetchResult<PHAsset *> *fetchResult;
  NSString *const localIdentifier = [assetURI substringFromIndex:@"ph://".length];
  fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil];
  PHAsset *const _Nonnull asset = [fetchResult firstObject];

  // load image
  [im requestImageDataForAsset:asset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info)
   {
       [activityItems addObject:imageData];
   }];

  // generate the activity view
  UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
  // prevent sharing to gallery again
  activityVC.excludedActivityTypes = @[UIActivityTypeSaveToCameraRoll];
  
  // get the top root view controller
  UIViewController *topRootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
  while (topRootViewController.presentedViewController)
  {
      topRootViewController = topRootViewController.presentedViewController;
  }

  // present it
  [topRootViewController presentViewController:activityVC animated:YES completion:nil];
}

This way, whoever is calling the API will only do: getShareModal('ph://....') and the modal will show.

@jgcmarins
Copy link
Member

That's great. Does it makes sense to support PHAsset. I mean, does Apple is moving around to this type for URIs?

@RocKhalil
Copy link
Author

@jgcmarins mainly yes.

It will also give compatibility to share images retrieved from @react-native-community/cameraroll.
All the images that come from the system are PHAsset and should be handled this way..

@scgough
Copy link

scgough commented May 11, 2020

👍
This looks like a cleaner version of a fix I had to write to get the share working for image and video phasset urls on iOS. I used dispatch_semaphore... to signal when to continue after getting what I need from the asset.

I'm just in the process of extending the IG share (and stories) to also cater for ph:// urls too.

@MateusAndrade
Copy link
Collaborator

This looks like a cleaner version of a fix I had to write to get the share working for image and video phasset urls on iOS. I used dispatch_semaphore... to signal when to continue after getting when I need from the asset.

I'm just in the process of extending the IG share (and stories) to also cater for ph:// urls too.

This would be a great to rn-share. Do you need any help on that?

@scgough
Copy link

scgough commented May 12, 2020

This would be a great to rn-share. Do you need any help on that?

Cheers! 👍🏻 I might to perhaps tighten up the Obj-C code as I don’t have lots of experience in it. I’ll get it working for what I’m doing and then put a PR together. Just want to get stories with stickers working again!

@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. You may also mark this issue as a "discussion" and i will leave this open

@github-actions github-actions bot added the stale There has been a lack of activity on this issue and it may be closed soon. label May 19, 2021
@mrousavy
Copy link

Hey! Did this end up being merged into react-native-share? Can you share ph:// assets now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted os: ios question stale There has been a lack of activity on this issue and it may be closed soon.
Projects
None yet
Development

No branches or pull requests

5 participants