11

我在 NSItemProvider 对象上使用 loadItemForTypeIdentifier:options:completionHandler: 方法通过 iOS 8 中的 Share 扩展从 Safari 中提取 url。

在 Objective-C 中,此代码有效并且块运行。

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

在 Swift 中,它看起来非常相似,但是闭包不会运行。此外, itemProvider.hasItemConformingToTypeIdentifier("public.url")返回YES所以必须有一个有效的对象来从itemProvider.

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

对于 Objective-C 和 Swift 版本,Info.plist NSExtension 部分完全相同,如下所示:

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

我究竟做错了什么?

4

6 回答 6

12

称呼

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

在完成处理程序结束时,而不是在 didSelectPost() 结束时调用它

于 2014-10-14T10:06:45.900 回答
6

由于在回调所有 completionHandlers 之后必须调用 completeRequestReturningItems,所以下面是我所做的。

 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)
    })
于 2015-07-06T06:03:04.223 回答
4

我对此不以为然,但看看这个人是如何做到的:https ://github.com/oguzbilgener/SendToInstapaper/blob/master/ShareExtension/SendingViewController.swift

于 2014-07-06T18:56:20.360 回答
1

在没有用户界面的情况下(在这种情况下,扩展的类是 NSObject 的子类),我从未管理过完成处理程序以正常工作。

尽管有[itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeURL]回报YES,但在设备或模拟器上都不会调用 completionHandler。

在尝试了不同的方法后,我最终找到了基于 javascript 将 URL 传递回扩展的解决方法(抱歉,我在示例中使用的是 ObjC 而不是 Swift)。

Info.plistNSExtension 部分:

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

JavascriptAction.js文件:

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

ActionRequestHandler.h头文件:

@interface ActionRequestHandler : NSObject <NSExtensionRequestHandling>

@end

ActionRequestHandler.m基于默认的动作扩展模板:

#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

希望它能帮助人们节省几个小时在完成处理程序问题上的挣扎。

于 2014-12-09T21:38:45.693 回答
1

我在 iOS 12.1 中遇到了同样的问题。我打电话

loadItemForTypeIdentifier:kUTTypeData 

而不是kUTTypeImage等。它对我有用。

于 2018-12-19T08:12:42.330 回答
0

在过去的几周里,我断断续续地与这个问题作斗争,终于找到了这个问题。我与 Objective C 或 Swift 无关,它似乎只是 Apple 代码中的一个错误。

似乎(在 iOS 8.0 中),只有在您使用自己的UIViewController子类时才会调用完成块。如果您使用 的子类SLComposeServiceViewController,则不会调用完成块。

这真的很烦人,因为默认情况下 XCode 会为您创建一个ShareViewController带有SLComposeServiceViewController. 要解决此问题,您只需将 ShareViewController 修改为继承自UIViewController. 这仍然可以访问该extensionContext属性,但您显然会失去所有不错的标准功能,并且必须从头开始实现您的 UI。

我已向 Apple 提交了雷达,但尚未收到回复。希望这将在未来的更新中得到修复。

于 2014-09-26T09:47:28.793 回答