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

时光毁灭记忆、已成空白 提交于 2019-11-30 11:38:59

问题


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.

In Objective-C, this code and works and the block runs.

[itemProvider loadItemForTypeIdentifier:(@"public.url" options:nil completionHandler:^(NSURL *url, NSError *error) {
    //My code
}];

In Swift, it looks very similar, however the closure doesn't run. Also, itemProvider.hasItemConformingToTypeIdentifier("public.url") returns YES so there must be a valid object to parse the url from inside the itemProvider.

itemProvider.loadItemForTypeIdentifier("public.url", options: nil, completionHandler: { (urlItem, error) in
    //My code
})

The Info.plist NSExtension portion is exactly the same for both Objective-C and Swift version and looks like this:

<key>NSExtension</key>
<dict>
    <key>NSExtensionAttributes</key>
    <dict>
        <key>NSExtensionActivationRule</key>
        <dict>
            <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
            <integer>1</integer>
        </dict>
        <key>NSExtensionPointName</key>
        <string>com.apple.share-services</string>
        <key>NSExtensionPointVersion</key>
        <string>1.0</string>
    </dict>
    <key>NSExtensionPointIdentifier</key>
    <string>com.apple.share-services</string>
    <key>NSExtensionMainStoryboard</key>
    <string>MainInterface</string>
</dict>

What am I doing wrong?


回答1:


call

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

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




回答2:


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)
    })



回答3:


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




回答4:


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

loadItemForTypeIdentifier:kUTTypeData 

instead of kUTTypeImage etc. It worked for me.




回答5:


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.




回答6:


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.



来源:https://stackoverflow.com/questions/24235954/ios-8-share-extension-loaditemfortypeidentifieroptionscompletionhandler-compl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!