iOS 8 Share extension loadItemForTypeIdentifier:options:completionHandler: completion closure not executing

后端 未结 6 1801
一向
一向 2020-12-24 09:07

I\'m using the loadItemForTypeIdentifier:options:completionHandler: method on an NSItemProvider object to extract a url from Safari via a Share extension in iOS 8.

相关标签:
6条回答
  • 2020-12-24 09:47

    call

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

    at the end of completionHandler instead of calling it at the end of didSelectPost()

    0 讨论(0)
  • 2020-12-24 09:47

    I have been battled with this issue on and off for the last few weeks, and have finally found the issue. I has nothing to do with Objective C or Swift, it just appears to be a bug in Apple's code.

    It seems that (as at iOS 8.0), the completion block is only called if you are using your own UIViewController subclass. If you are using a subclass of SLComposeServiceViewController, then the completion block is not called.

    This is really annoying, as by default XCode creates you a ShareViewController with a subclass of SLComposeServiceViewController. To work around this issue, you just have to modify ShareViewController to inherit from UIViewController. This will still give access to the extensionContext property, but you'll obviously lose all of nice standard functionality and will have to implement your UI from scratch.

    I've filed a radar with Apple, but have not had a reply yet. Hopefully this will be fixed in a future update.

    0 讨论(0)
  • 2020-12-24 09:53

    I was never managed completionHandler to work properly for Share extension with no user interface (in such case extension's class is a subclass on NSObject).

    Despite the [itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeURL] returns YES the completionHandler is never called both on the device or simulator.

    After trying different approaches I ended up with workaround based on javascript passing URL back to extension (sorry as I use ObjC not Swift for my example).

    Info.plist NSExtension portion:

    <key>NSExtension</key>
    <dict>
        <key>NSExtensionAttributes</key>
        <dict>
            <key>NSExtensionActivationRule</key>
            <dict>
                <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
                <integer>1</integer>
            </dict>
            <key>NSExtensionJavaScriptPreprocessingFile</key>
            <string>Action</string>
        </dict>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.services</string>
        <key>NSExtensionPrincipalClass</key>
        <string>ActionRequestHandler</string>
    </dict>
    

    Javascript Action.js file:

    var Action = function() {};
    Action.prototype = {
        run: function(arguments) {
            arguments.completionFunction({ "currentURL" : window.location.href })
        },
        finalize: function(arguments) {
        }
    };
    var ExtensionPreprocessingJS = new Action
    

    ActionRequestHandler.h header file:

    @interface ActionRequestHandler : NSObject <NSExtensionRequestHandling>
    
    @end
    

    ActionRequestHandler.m based on default Action extension template:

    #import "ActionRequestHandler.h"
    #import <MobileCoreServices/MobileCoreServices.h>
    
    @interface ActionRequestHandler ()
    
    @property (nonatomic, strong) NSExtensionContext *extensionContext;
    
    @end
    
    @implementation ActionRequestHandler
    
    - (void)beginRequestWithExtensionContext:(NSExtensionContext *)context {
        // Do not call super in an Action extension with no user interface
        self.extensionContext = context;
    
        BOOL found = NO;
    
        // Find the item containing the results from the JavaScript preprocessing.
        for (NSExtensionItem *item in self.extensionContext.inputItems) {
            for (NSItemProvider *itemProvider in item.attachments) {
                if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypePropertyList]) {
                    [itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypePropertyList options:nil completionHandler:^(NSDictionary *dictionary, NSError *error) {
                        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                            [self itemLoadCompletedWithPreprocessingResults:dictionary[NSExtensionJavaScriptPreprocessingResultsKey]];
                        }];
                    }];
                    found = YES;
                }
                break;
            }
            if (found) {
                break;
            }
        }
    
        if (!found) {
            // We did not find anything - signal that we're done
            [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
            // Don't hold on to this after we finished with it
            self.extensionContext = nil;
        }
    }
    
    - (void)itemLoadCompletedWithPreprocessingResults:(NSDictionary *)javaScriptPreprocessingResults
    {
        // Get the URL
        if ([javaScriptPreprocessingResults[@"currentURL"] length] != 0) {
            NSLog(@"*** URL: %@", javaScriptPreprocessingResults[@"currentURL"]);
        }
    
        // Signal that we're done
        [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
        // Don't hold on to this after we finished with it
        self.extensionContext = nil;
    }
    
    @end
    

    Hope it will help somebody to save couple of hours struggling with the completionHandler issue.

    0 讨论(0)
  • 2020-12-24 09:58

    Since completeRequestReturningItems must be called after all completionHandlers are called back, below is what I do.

     let group = dispatch_group_create()
    
        for item: AnyObject in self.extensionContext!.inputItems {
            let inputItem = item as! NSExtensionItem
            for provider: AnyObject in inputItem.attachments! {
                let itemProvider = provider as! NSItemProvider
                if itemProvider.hasItemConformingToTypeIdentifier("public.url") {
                    dispatch_group_enter(group)
                    itemProvider.loadItemForTypeIdentifier("public.url", options: nil, completionHandler: {
                        (result: NSSecureCoding!, error: NSError!) -> Void in
                        //...
                        dispatch_group_leave(group)
                    });
                }
                if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
                    dispatch_group_enter(group)
                    itemProvider.loadItemForTypeIdentifier(kUTTypeImage as String, options: nil, completionHandler: { (result, error) -> Void in
                        if let resultURL = result as? NSURL {
                            if let image = UIImage(data: NSData(contentsOfURL: resultURL)!) {
                                // ...
                            }
                        }
                        dispatch_group_leave(group)
                    });
                }
            }
        }
        dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), {
            self.extensionContext!.completeRequestReturningItems([], completionHandler: nil)
        })
    
    0 讨论(0)
  • 2020-12-24 10:01

    I had the same issue in my iOS 12.1. I'm calling

    loadItemForTypeIdentifier:kUTTypeData 
    

    instead of kUTTypeImage etc. It worked for me.

    0 讨论(0)
  • 2020-12-24 10:08

    I take no credit for this, but have a look at how this guy did it: https://github.com/oguzbilgener/SendToInstapaper/blob/master/ShareExtension/SendingViewController.swift

    0 讨论(0)
提交回复
热议问题