11

I used the Solution form iOS Share Extension issue when sharing images from Photo library to get Images from the Photo App. This works great in the Simulator, but on the Device I get an error that I can't Access the NSURL provided by the itemProvider:

2018-02-18 12:54:09.448148+0100 MyApp[6281:1653186] [default] [ERROR] Failed to determine whether URL /var/mobile/Media/PhotoData/OutgoingTemp/554581B2-950C-4CFD-AE67-A2342EDEA04D/IMG_2784.JPG (s) is managed by a file provider

Caused by the Statment:

[itemProvider loadItemForTypeIdentifier:itemProvider.registeredTypeIdentifiers.firstObject options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
}

Searching PHAsset for the Item Name is not a good solution as the user have to grand access to the photo library again.

Indiana J.
  • 119
  • 6
  • I'm facing the same problem. Do you find a solution for this? – DGoogly Mar 15 '18 at 11:06
  • Looks like there is no solution. Currently my code try to load the NSData from the URL and if it fails I load it from PHAsset (Building an Array of all PHAssets and searching for last path component as filename.) Realy bad code but in my tests it works for all Apps I have that allow image sharing. – Indiana J. Mar 16 '18 at 12:13

2 Answers2

2

In didSelectPost you must not dismiss the ShareExtension ViewController until you have processed all the items in the inputItems array. The ViewController is dismissed using [super didSelectPost] or by calling the completion method of the extension context.

Here is my solution in code:

- (void)didSelectPost {

__block NSInteger itemCount = ((NSExtensionItem*)self.extensionContext.inputItems[0]).attachments.count;
__block NSInteger processedCount = 0;
for (NSItemProvider* itemProvider in ((NSExtensionItem*)self.extensionContext.inputItems[0]).attachments ) {

    if([itemProvider hasItemConformingToTypeIdentifier:@"public.jpeg"]) {
        NSLog(@"itemprovider = %@", itemProvider);

        [itemProvider loadItemForTypeIdentifier:@"public.jpeg" options:nil completionHandler: ^(id<NSSecureCoding> item, NSError *error) {

            NSData *imgData;
            if([(NSObject*)item isKindOfClass:[NSURL class]]) {
                imgData = [NSData dataWithContentsOfURL:(NSURL*)item];
            }
            if([(NSObject*)item isKindOfClass:[UIImage class]]) {
                imgData = UIImagePNGRepresentation((UIImage*)item);
            }
            NSDictionary *dict = @{
                                   @"imgData" : imgData,
                                   @"name" : self.contentText
                                   };
            NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.share.extension1"];
            [defaults setObject:dict forKey:@"img"];
            [defaults synchronize];
            processedCount += 1;
            if (processedCount == itemCount)
                [super didSelectPost];
        }];
    }
}
KeithTheBiped
  • 832
  • 10
  • 21
  • 1
    My solution looks similar, additionally I have a thired if: if( [(NSObject*)item isKindOfClass:[NSURL class]] ) and load the Image from PHPhotoLibrary. – Indiana J. Jan 06 '19 at 19:24
  • I was struggling with this for hours... Thank you! – Fengson Aug 29 '19 at 05:25
0

The loadItemForTypeIdentifier or in Swift loadItem method is asynchronous so dismissing the UI must be called as the last thing inside its completionHandler.

For example I have:

override func didSelectPost() {
    // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
    if let item = self.extensionContext?.inputItems[0] as? NSExtensionItem, let attachments = item.attachments {
        for provider in attachments {
            if provider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
                provider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil, completionHandler: {item, error in
                    // do all you need to do here, i.e.
                    if let tmpURL = item as? URL {
                        // etc. etc.
                    }
                    // and, at the end, inside the completionHandler, call
                    // Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
                    self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
                })
            }
        }
    }
}

I you dismiss the UI via:

self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)

or via:

super.didSelectPost()

outside of the completionHandler after the async method loadItem you will get all kind of permission errors, further more this errors could be random, sometimes happen and sometimes don't, this is because sometimes your async call to loadItem gets the chance to terminate before the UI is dismissed and sometimes it doesn't.

Just leaving this here, hoping it helps someone. This issue costed me few hours.

andrea
  • 452
  • 5
  • 14