How to get image form URL passed by share extension to an App

Muruganandham K picture Muruganandham K · Oct 16, 2015 · Viewed 7.5k times · Source

I working on sharing array of images from gallery using share extension. i can get image inside share extension using

- (void)getImageFromExtension:(NSExtensionItem *)extensionItem
{
    for(NSItemProvider *attachment in extensionItem.attachments)
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            if([attachment hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage])
            {
                [attachment loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
                    NSURL *image = (NSURL *)item;
                    if(error)
                    {
                        NSLog(@"Error retrieving image");
                    } else {
                        dispatch_async(dispatch_get_main_queue(), ^{
                            NSLog(@"*** ### %@",[UIImage imageWithData:[NSData dataWithContentsOfURL:image]]);
                        });
                    }
                }];
            }
        });
    }
}

What i actually need is, i need to pass image url to container applciation. But not able to get image from that URL. The url format is file:///var/mobile/Media/DCIM/101APPLE/IMG_1705.JPG

The data from this url is always null. how to get the image from this URL?

Answer

joern picture joern · Oct 16, 2015

You probably call getImageFromExtension from didSelectPost. At the end of didSelectPost you are supposed to call completeRequestReturningItems:completionHandler: to tell the host app to complete the app extension request with an array of result items. When that method is called the whole ShareViewController is dismissed and deallocated. When you call this at the end of didSelectPost, it gets executed before the NSItemProvider could retrieve the image URL. Because that happens asynchronously. To fix this you have to call completeRequestReturningItems:completionHandler: after NSItemProvider has retrieved the imageURL.

Here is how it should work:

- (void)didSelectPost {
    NSString *typeIdentifier = (NSString *)kUTTypeImage
    NSExtensionItem *item = self.extensionContext.inputItems.firstObject;
    NSItemProvider *itemProvider = item.attachments.firstObject;
    if ([itemProvider hasItemConformingToTypeIdentifier:typeIdentifier]) {
        [itemProvider loadItemForTypeIdentifier:typeIdentifier
                                        options:nil
                              completionHandler:^(NSURL *url, NSError *error) {
                                  NSURL *imageURL = (NSURL *)item
                                  self.saveImage(imageURL)
                                  [self.extensionContext completeRequestReturningItems:@[]         
                                                                     completionHandler:nil];
                              }];
    }
}

By the way, there is no need to use dispatch_async when working with NSItemProvider as the provider is already doing his work asynchronously.

I've written a blog post about this, if you want to read a bit more about this.

EDIT

You can now load the image with the file path that you just retrieve. This works only in the extension! If you want to access the image from the containing app you cannot use the file URL you just retrieved. The share extension saves the selected image in a folder that is not accessible by the containing app.

To make the image available to the containing app you have to activate App Groups in you containing app and the extension. Then the extension saves the image to the shared container and the containing app can retrieve it:

func saveImage(url: NSURL) {
    guard let imageData = NSData(contentsOfURL: url) else { return }
    let selectedImage = UIImage(data: imageData)

    let fileManager = NSFileManager.defaultManager()
    if let sharedContainer = fileManager.containerURLForSecurityApplicationGroupIdentifier("YOUR_GROUP_IDENTIFIER") {
        if let dirPath = sharedContainer.path {
            let sharedImageFilePath = dirPath + "/image.png"
            let binaryImageData = UIImagePNGRepresentation(selectedImage!)
            binaryImageData?.writeToFile(sharedImageFilePath, atomically: true)
            // send the sharedImageFilePath to the containing app
        }
    }
}