Suran Suran - 4 months ago 112
Objective-C Question

Sharing images/videos to other apps through UIActivityViewController shows shared items twice in iOS

I'm trying to share some images and videos to other apps(like FB, WhatsApp) using

UIActivityViewController
. I have sub-classed
UIActivityItemProvider
, and on calling
-(id)item
methods, I'm processing the images/videos and saving in documents directory. Then I am returning the file paths as
NSURLs
. My problem is that I'm not able to find a way to send multiple file URLs at the same time.

Below are the approaches I took to return urls from -(id)item method;


  1. As an
    NSArray
    of
    NSURL
    objects. DOES NOT WORK. When the target app popup comes, it is empty always.

  2. As a
    NSDictionary
    , in which
    NSURL
    objects are the values and keys could be anything. PROBLEMATIC: The target app popup shows all items, but TWICE! I experimented with the dictionary a lot, but couldn't find a way to solve this.



Simply returning an
NSURL
object from
-(id)item
method works fine for single file. But, I have to share multiple items. Array doesn't work, Dictionary is duplicating shared items.

Can someone please tell me what I'm doing wrong here?




UPDATE 1:

This is how I show the UIActivityViewController.

CustomItemProvider *provider = [[CustomItemProvider alloc] initWithPlaceholderItem:[UIImage imageNamed:@"ios-59.png"]];

UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[provider] applicationActivities:nil];

activityViewController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError)
{
if(completed)
{
NSLog(@"Activity Completed");
}
else
{
NSLog(@"Activity Cancelled");
}
};
[self presentViewController:activityViewController animated:YES completion:^{}];


The UIActivityItemProvider implementation is as given below; The URLs are valid and there are images/videos at those locations.

@interface CustomItemProvider : UIActivityItemProvider

@end

@implementation CustomItemProvider
- (id)item
{
NSURL *url1 = [NSURL fileURLWithPath:@"file one url"];
NSURL *url2 = [NSURL fileURLWithPath:@"file two url"];
NSURL *url3 = [NSURL fileURLWithPath:@"file three url"];

return @{@"item1":url1, @"item2":url2, @"item3":url3}; //As NSDictionary. This causes 6 items to be shared; all files twice.

//return @[url1, url2, url3]; //As NSArray
}
@end





UPDATE 2:

The linked question is different.
I don't want to send the files directly to the UIActivityViewController as parameter to
initWithActivityItems:
. The reason is that there could be multiple video files, which will cause memory warning and a crash. Also, I will be manipulating the files before sending it to target app(in the
-(id)item
method, which I have not shown here), hence I need
UIActivityItemProvider
to process the files in background.

Answer

Okay, finally I got it. This has been driving me crazy and I couldn't find any proper documentation/answers anywhere.

The solution was to return a dictionary back with NSItemProvider objects as values. The keys for the same could be anything that is not same as other keys.

If anyone is after the same problem, find the solution below;

@interface CustomItemProvider : UIActivityItemProvider
{
     NSCondition *_condition;
     NSArray *_filesToShare;
}
@end

@implementation CustomItemProvider
- (id)item
{
    _fetchInProgress = YES; 
    //Here create start fetching/processing videos/photos in ANOTHER THREAD. Once the fetching/processing is complete, set the value of _fetchInProgress to NO.

    //If you want to show progress, display progress view in MAIN THREAD here.

    [_condition lock];
    while(_fetchInProgress)
    {
        [_condition wait];
    }
    [_condition unlock];

    //Here I am assuming that the files are processed and are saved to documents/cache directory and their file-paths are in _filesToShare array.

     NSMutableDictionary *itemsDict = [NSMutableDictionary dictionaryWithCapacity:[_filesToShare count]];
     for (int i = 0; i < [_filesToShare count]; i++)
     {
         NSURL *url = [NSURL fileURLWithPath:[_filesToShare objectAtIndex:i]];
         NSItemProvider *item = [[NSItemProvider alloc] initWithContentsOfURL:url];
         [itemsDict setObject:item forKey:@(i)];
     }

     return itemsDict;
}
@end

Cheers!!!